Compare commits

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

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

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

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

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

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

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

After change number of such blocks drops to zero:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

This change fixes both of them.

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

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

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

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

image-base i64 to u64 parse_unsigned_number

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

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

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

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

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

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

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

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

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

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

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

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

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

The second part is required to workaround a glibc issue.

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

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

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

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

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

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

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

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

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

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

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

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

This patch fixes the issue.

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

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

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

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

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

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

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

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

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

  if (foo)
    foo();

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Fixes #298.

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

Signed-off-by: Christoph Erhardt <[email protected]>
2022-01-23 16:03:53 +01:00
Rui Ueyama 6fb64656b9 Bump mold version to 1.0.2 2022-01-23 17:19:40 +09:00
Rui Ueyama 21dd8834d3 Do not create mold as a PIE by default
It is up to packager's choice to create mold as a position-independent
executable or not. If you want, pass `LDFLAGS=-fpie` and `CXXFLAGS=-fPIE`
to `make`.
2022-01-23 16:22:15 +09:00
Rui Ueyama c6dc8da704 Tidy up Makefile
Our Makefile didn't allow overriding CFLAGS, CXXFLAGS or LDFLAGS
because we add mandatory options to these variables. It is contrary
to conventions Makefile conventions explained here.
https://www.gnu.org/prep/standards/html_node/Command-Variables.html#Command-Variables

In this patch, I separated mandatory options to MOLD_CXXFLAGS and
MOLD_LDFLAGS so that users can freely override CFLAGS, CXXFLAGS and
LDFLAGS.

I also removed `DEBUG`, `ASAN` and `TSAN` variables from the Makefile
because we can now simply pass `CXXFLAGS="-g -O0"` or
`CXXFLAGS=-fsanitize=...` instead.
2022-01-23 15:45:48 +09:00
Rui Ueyama f8e7b157c7 Fix a test 2022-01-23 11:41:19 +09:00
Rui Ueyama 9ddc188f97 [ELF] Improve an error message 2022-01-23 11:33:23 +09:00
Rui Ueyama 6671d5b03b Style fix 2022-01-23 11:33:23 +09:00
Rui Ueyama 10e666ce0f Update the man page 2022-01-23 11:33:22 +09:00
Rui Ueyama 27f5b12ee2 Merge pull request #292 from ADKaster/posix-headers
Explicitly include sys/select and sys/time
2022-01-23 11:25:03 +09:00
Rui Ueyama f7b00b7adb Merge pull request #291 from BertalanD/relr-fix
[ELF]: Fix issues with relr relocations
2022-01-23 11:24:10 +09:00
Andrew Kaster ca3d5f49d2 Explicitly include sys/select and sys/time
The SerenityOS LibC doesn't leak the definitions of select(2) or struct
timeval into other POSIX headers. Manually include these headers in
elf/subprocess.cc where they are used for better compatibility.

Signed-off-by: Andrew Kaster <[email protected]>
2022-01-22 15:04:57 -07:00
Daniel Bertalan 1d0a854957 [ELF] Fix encoding of .relr.dyn bitmap entries
Before this change, all bitmap entries ended up containing the value
0x3 (as `pos[i] - base` is guaranteed to be a multiple of the word
size). This expression needs to produce the offset in words from the
base address, so we need to *divide* by the word size.

Signed-off-by: Daniel Bertalan <[email protected]>
2022-01-22 18:20:02 +01:00
Daniel Bertalan d1151a9528 [ELF] Create dynamic entries for .relr.dyn
If the corresponding DT_RELR, DT_RELRSZ and DT_RELRENT dynamic entries
are not present, dynamic linkers won't pick up the relocations encoded
in .relr.dyn.

Signed-off-by: Daniel Bertalan <[email protected]>
2022-01-22 18:19:55 +01:00
Rui Ueyama 1ae6c55c52 Update man page 2022-01-22 20:59:28 +09:00
Rui Ueyama f7ce2f6748 Remove a useless assignment 2022-01-22 20:59:28 +09:00
Rui Ueyama c6b89f9766 Remove unnecessary #include 2022-01-22 13:11:21 +09:00
Rui Ueyama 8eae808223 Revert "[ELF] Compile an Aho-Corasick tree into a DFA"
This reverts commit 31ca2cc2ae
because looks like it's slower than before.
2022-01-22 12:37:40 +09:00
Rui Ueyama 31ca2cc2ae [ELF] Compile an Aho-Corasick tree into a DFA 2022-01-22 12:35:49 +09:00
Rui Ueyama 5cf2328a8b [ELF] Support negation and range in glob pattern 2022-01-22 11:01:55 +09:00
Rui Ueyama ac93f8dfd1 Add comments 2022-01-22 10:23:11 +09:00
Rui Ueyama 28c36e58b7 Revert "Fix a build issue on Gentoo with musl libc"
This reverts commit 2cc85cc21d because
it didn't fix the Gentoo's issue.

https://github.com/rui314/mold/issues/281
2022-01-22 00:52:17 +09:00
Rui Ueyama ba6ccd109c [ELF] Do not use GlobMatcher if all version patterns are simple 2022-01-21 23:47:54 +09:00
Rui Ueyama e9a5d1cee3 [ELF] Handle glob patterns directly instead of converting them to regex
Converting a glob pattern to a regex is fragile, and std::regex is slow.
So we should handle glob patterns ourselves.
2022-01-21 23:32:27 +09:00
Rui Ueyama 4ee3062054 [ELF] Fix version pattern matching 2022-01-21 21:27:49 +09:00
Rui Ueyama 3f4e570320 Simplify 2022-01-21 20:59:19 +09:00
Rui Ueyama d0c1c4db19 [ELF] Use Aho-Corasick algo to handle version script patternsC
To process version scriots, we have to match glob patterns against
symbol strings. Sometimes, we have hundreds or thousands of glob
patterns and have to match them against millions of mangled long
C++ symbol names. This step can be very slow.

In this patch, I implemented the Aho-Corasick algorithm to match glob
patterns to symbol strings as quickly as possible. For the details
of the algorithm, see https://en.wikipedia.org/wiki/Glob_(programming).

This patch improves mold's performance for programs that uses large
version scripts. For example, linking libQt6Gui.so.6.3.0 reduced from
1.10s to 0.05s with this patch.

This patch also changes how symbol versions are applied if two or more
version patterns match to a single symbol string. Previously, the last
one in a script file took precedence. Now, the first one takes
precedence. I believe the new behavior is compatible with GNU ld.

Fixes https://github.com/rui314/mold/issues/156
Fixed https://github.com/rui314/mold/issues/287
2022-01-21 20:30:05 +09:00
Rui Ueyama 05e1054058 s/std::lock_guard/std::scoped_lock/g
It looks like you should use std::scoped_lock instead of std::lock_guard
by default since C++17 unless you have a strong opinion to prefer the
latter.
2022-01-21 15:02:47 +09:00
Rui Ueyama 036fdf0b29 Add a comment 2022-01-21 11:00:36 +09:00
Rui Ueyama 2b034335ec Use std::erase_if instead of our own
I didn't know that C++20 has this function.
2022-01-21 10:40:46 +09:00
Rui Ueyama be6ae07ac1 [ELF] Ignore an ICC-generated GNU linkonce section
If you try to link ICC-generated objects file with GCC-generated objects,
you'll get an error message that `__gxx_personality_v0` is duplicated.
This is because ICC puts a section with this symbol into a GNU linkonce
section, while GCC puts it into a COMDAT section.

ICC should stop putting that symbol into GNU linkonce because it's
superceded by COMDAT long ago.

That being said, we need to do something to make it possible to mix
ICC and GCC object files. In this patch, we simply discard the ICC-
generated section. Since `__gxx_personality_v0` is always provided by
libc (that function handles C++ exceptions), we can simply ignore ICC's
output.

Fixes https://github.com/rui314/mold/issues/271
2022-01-20 22:59:58 +09:00
Rui Ueyama e3359ea415 Fix macOS buildbot 2022-01-20 16:46:08 +09:00
Rui Ueyama 2cc85cc21d Fix a build issue on Gentoo with musl libc
libtbb.so contains undefined symbols on Gentoo with musl libc.
Because we are not using such symbols at runtime, and symbols are
resolved lazily, mold would work fine as long as we can build it.
By passing `-allow-shlib-undefined`, we can build mold.

Fixes https://github.com/rui314/mold/issues/281
2022-01-20 16:35:44 +09:00
Rui Ueyama 9c0a55a2eb [ELF] Fix off-by-one error 2022-01-20 13:45:20 +09:00
Rui Ueyama 8b1905453d [ELF] Do not use BND prefix
BND prefix is defined by Intel MPX extension which is now deprecated by Intel.
2022-01-20 13:39:31 +09:00
Rui Ueyama fa0cb30d8f Refactor 2022-01-20 13:28:16 +09:00
Rui Ueyama 2a4d8b4f72 Rename variables 2022-01-20 12:32:05 +09:00
Rui Ueyama 92876820cb [ELF/ARM64] Support range extension thunks
ARM64 branch instructions have only a 25-bit displacement. Non-thumb
instructions are always aligned to 4 byte boundaries, but with that
implicit trailing two zeros considered, they can represent only a 27-bit
displacement. That means branch instructions can jump to a location only
if it is within a ±128 MiB range.

If a branch destination is farther than that, a linker has to emit machine
code sequence that constructs a full 32-bit address in a register to jump
to the final destination, and redirect the branch to that code sequence.
Such code sequence is called a "range extension thunk" or just "thunk".

Previously, mold didn't support range extension thunks, so it couldn't link
large programs. That would fail with an "relocation out of range" error.
Now, mold gained a feature to create thunks and can link large programs.

Thunk creation is an interesting algorithmic problem. We need to insert
a thunk for at least in every 128 MiB chunk, because otherwise branch
instructions wouldn't be able to jump to a thunk. Adding an entry to a thunk
could slightly enlarge the distance between a branch instruction location
and its destination if the thunk is in between them. That could make the
branch that was previously reachable unreachable.

Usually, this problem is solved by an iterative algorithm. With the
iterative algorithm, a linker check for reachability of all relocations,
create new thunks if necessary, and repeat it until no new thunks are
created.

I implemented a different algorithm than that in this patch. The algorithm
implemented in this patch is guaranteed to work in O(n) where n is the
number of relocations. This algorithm might be novel.

And the algorithm implemented in this patch is quite fast. It can create
thunks in 80 milliseconds on a 16-core Amazon Graviton 2 machine for
clang-14 that has an ~100MB .text section.
2022-01-20 03:16:50 +00:00
Rui Ueyama 02c9fa82d4 [ELF] Ensure mold-warpper.so is linked against libdl 2022-01-20 10:23:32 +09:00
Rui Ueyama 1b01448f01 Merge pull request #286 from tklauser/test-exception-aarch64
[ELF] Pass -fno-PIC along with -mcmodel=large on aarch64
2022-01-20 09:46:42 +09:00
Rui Ueyama dfeb17a5c8 Merge pull request #285 from tklauser/ignore-fix-cortex-flag
[ELF] Ignore --fix-cortex-a53-835769 flag
2022-01-20 09:45:20 +09:00
Rui Ueyama d8e3980b86 Do not link libdl
I don't think mold uses any functionality in libdl. mold-wrapper.so
needs it for dlopen()-family functions on non-NetBSD systems though.
2022-01-20 09:28:51 +09:00
Rui Ueyama 0540f2c10b Update README
Fixes https://github.com/rui314/mold/issues/282
2022-01-20 09:10:21 +09:00
Rui Ueyama 809652d740 [ELF] Handle glob patterns other than * for local: in version scripts
Previously, we didn't handle version scripts like this correctly:

  ver {
    global: *;
    local: foo*;
  }

We didn't handle `local:` part correctly except for `*`.

Fixes https://github.com/rui314/mold/issues/277
2022-01-20 08:51:04 +09:00
Rui Ueyama be41fdbc21 [ELF] Rename variables
`ctx.arg` should contain only variables that can directly be mapped
to command line arguments.
2022-01-20 08:35:52 +09:00
Tobias Klauser fbd4e559e6 [ELF] Pass -fno-PIC along with -mcmodel=large on aarch64
The -mcmodel=large option is incompatible with -fPIC on aarch64, see
https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mcmodel_003dlarge

This makes test/elf/exception.sh pass on linux/arm64.

Signed-off-by: Tobias Klauser <[email protected]>
2022-01-19 21:36:53 +01:00
Tobias Klauser c34c77b9a5 [ELF] Ignore --fix-cortex-a53-835769 flag
GCC passes this on my linux/arm64 machine (Pine64 Pinebook Pro).

Signed-off-by: Tobias Klauser <[email protected]>
2022-01-19 21:15:55 +01:00
Rui Ueyama a4d81fb723 [ELF] Ignore trailing garbage after a terminator in .eh_frame
Follow-up patch for https://github.com/rui314/mold/pull/278
2022-01-19 19:45:31 +09:00
Rui Ueyama 948248b732 Merge pull request #278 from depressed-pho/netbsd-compatibility
Improve compatibility with NetBSD
2022-01-19 19:35:18 +09:00
Masatake Daimon 807679cb0a Skip empty records in .eh_frame
It should be perfectly legal to have an empty CIE along with an end-of-records
marker of FDE. /usr/lib/crtend.o from NetBSD/amd64 has the following .eh_frame
but mold emits an error for the zero-length FDE:

    .section	.eh_frame, "a", @progbits
    .align 8
    .quad 0

Signed-off-by: Masatake Daimon <[email protected]>
2022-01-19 11:16:55 +09:00
Masatake Daimon 67179950d0 Ignore -dc and -dp
GCC 7.5.0 passes these flags to ld(1). Maybe mold should handle them properly,
but since common symbols are considered to be obsolete it's probably not worth
it. At least it's better to ignore them rather than to abort.

Signed-off-by: Masatake Daimon <[email protected]>
2022-01-19 11:16:55 +09:00
Masatake Daimon af05107964 Do not try to link with -ldl unconditionally
Some operating systems, like NetBSD, don't have a separate library for
dlopen(3). They have dl* functions in their libc.

Signed-off-by: Masatake Daimon <[email protected]>
2022-01-19 11:16:55 +09:00
Rui Ueyama fe209f3f4c [ELF] If we can't open an existing output file, create a new one
We try to overwrite to an existing output file if exists because
writing to a file that is already in buffer cache is much faster than
creating a new fresh file and write to it.

Previously, mold failed with the "fchmod failed" error if an existing
file is writable (so open for read-write succeeds) but owned by
someone else (so fchown fails). Now, mold ignore such existing file
and create a fresh output file.

Fixes https://github.com/rui314/mold/issues/275
2022-01-18 16:04:52 +09:00
Rui Ueyama e6d1d011ef Merge pull request #274 from akawashiro/show-errno
Show errno for ftruncate and fchmod
2022-01-18 15:07:15 +09:00
Akira Kawata fe7bc2a846 [ELF] [Mach-O] Show errno for ftruncate and fchmod
Signed-off-by: Akira Kawata <[email protected]>
2022-01-18 14:42:56 +09:00
Rui Ueyama 9453488424 [ELF] Fix test for ARM64 2022-01-18 13:28:57 +09:00
Rui Ueyama 267125e5b1 Update README 2022-01-18 13:07:34 +09:00
Rui Ueyama 43b2a1e423 [ELF] Do not scan .text for .relr.dyn 2022-01-17 20:40:22 +09:00
Rui Ueyama 04ba8f4cbd [ELF] Rename a function 2022-01-17 10:26:23 +09:00
Rui Ueyama c747162717 Simplify 2022-01-16 17:20:26 +09:00
Rui Ueyama 3ad29b230c Simplify 2022-01-16 15:23:47 +09:00
Rui Ueyama 7a8c14a634 [ELF] Do not use parallel scan to compute section offsets
A function passed to tbb::parallel_scan must be associative, but I don't
think our function satisfied that constraint. So I rewrote code without
tbb::parallel_scan.
2022-01-16 13:32:20 +09:00
Rui Ueyama 021463c628 Refactor 2022-01-16 12:55:23 +09:00
Rui Ueyama 1152b6eb38 [ELF] Remove redundant assignment 2022-01-16 08:41:26 +09:00
Rui Ueyama 27be74e800 Simplify 2022-01-15 20:18:25 +09:00
Rui Ueyama 709ed92ac0 [ELF] Rename a function 2022-01-15 16:08:10 +09:00
Rui Ueyama faa75ee9e7 [ELF] Add a comment to explain the encoding of .relr.dyn 2022-01-15 16:02:43 +09:00
Rui Ueyama 3411511a8b [ELF] Ignore i386-specific linkonce symbols
This is a follow-up to the previously commit patch
0c19046721. We needed to ignore symbols
whose address is in a discarded .gnu.linkonce sections.

Fixes https://github.com/rui314/mold/issues/270
2022-01-15 14:28:34 +09:00
Rui Ueyama 12fcc76ff0 Install missing tools to a docker environment
We need `git` and `pkg-config` because we are using them in our
Makefile.
2022-01-15 14:02:40 +09:00
Rui Ueyama 0c19046721 [ELF] Ignore .gnu.linkonce sections in old glibc i386 CRT files
Fixes https://github.com/rui314/mold/issues/270
2022-01-15 00:06:15 +09:00
Rui Ueyama bd6afa1b23 [ELF] Add -pack-dyn-relocs=relr
.relr.dyn is a new section that has been implemented in other linkers
recently. That section contains only the RELATIVE-type dynamic
relocations (i.e. base relocations). Compared to the regular
.rela.dyn, a .relr.dyn's size is typically less than 1/10 because the
section is compressed.

Since PIEs (position-independent executables) tend to contain lots of
RELATIVE-type relocations and PIEs are now the default on many Linux
distributions for security reasons, .relr.dyn is more effective than
it was. It can reduce binary size by a few percent or more.

Note that the runtime support is catching up, so binaries built with
`-pack-dyn-relocs=relr` may not work on your system unless you are
running a very recent version of Linux.
2022-01-14 20:54:37 +09:00
Rui Ueyama 5fc0e503e1 Do not use CPPFLAGS for C++ compiler flags
The correct variable name is `CXXFLAGS`. `CPPFLAGS` is for C preprocessor
flags in the Makefile.
2022-01-14 15:34:55 +09:00
Rui Ueyama 8e709d2b62 Refactor 2022-01-14 11:34:38 +09:00
Rui Ueyama 7fa15fd17e Ignore /SYM64/ pseudo members in thin archives 2022-01-14 11:06:39 +09:00
Rui Ueyama a80524b478 Refactor 2022-01-14 10:57:01 +09:00
Rui Ueyama 2ee2243c63 Refactor 2022-01-13 23:53:53 +09:00
Rui Ueyama bba506d6b4 Ignore /SYM64/ pseudo member inar
I believe this pseudo member is relatively new to support archive
files larger than 4 GiB. I'm not sure if mold can handle such large
archives, but first of all, mold crashes when an archive with this
pseudo member is given.

This commit fixes the crash bug.

I don't know how to create an archive with this pseudo member, so
this commit does not have a testcase.

Reported by sina-ht at https://github.com/rui314/mold/pull/269.
2022-01-13 23:35:11 +09:00
Rui Ueyama d8dc8a6787 [ELF] Optimize relocation processing for non-alloc sections
In mold, a relocation refers either a symbol or a section piece.
A section piece is a segment of a mergeable section such as
.rodata.str.1 or .debug_str.

Previously, we preprocessed all relocations referring mergeable
sections to find their corresponding section pieces. We did this
because we want to find a relocation target as quickly as possible
for `gc_sections` and `scan_rels`.

However, we didn't actually have to do that for non-alloc sections,
as non-alloc sections are not subject to neither `gc_sections` nor
`scan_rels`. So, we could skip relocation preprocessing for non-alloc
sections. This commit implement that optimization.

It looks like this is an effective optimization for programs that have
large debug info because debug sections tend to contain a lot of
relocations referring a .debug_str which is a mergeable section.
Here is a few notable examples.

            Output size  Before  After
  clang-14  2.2 GiB      1.455s  1.396s  (4% faster)
  mongodb   4.8 GiB      2.341s  1.925s  (17% faster)

These nubmers were measured on a simulated 16-core 32-thread machine.
2022-01-13 23:22:03 +09:00
Rui Ueyama a9cca00933 [ELF] Fix symbol visibility
This commit fixes a regression introduced in
ce5749ce2f.
2022-01-13 22:59:04 +09:00
Rui Ueyama d3cd2e780f Remove a redundant data structure 2022-01-13 20:38:43 +09:00
Rui Ueyama 1e81c772ae Move macros to the beginning of the file 2022-01-13 20:31:03 +09:00
Rui Ueyama d2363f1819 Fix warning for the case if -DDEBUG is given 2022-01-13 16:51:12 +09:00
Rui Ueyama c3466b9021 Move a use of strerror_r to a separate file 2022-01-13 13:55:34 +09:00
Rui Ueyama 011a7d35d6 [ELF] Add a test
https://github.com/rui314/mold/issues/244
2022-01-13 13:06:37 +09:00
Rui Ueyama 9d27ee0839 [ELF] Skip incompatible files specified by linker script GROUP command
Fixes https://github.com/rui314/mold/issues/260
2022-01-13 10:42:13 +09:00
Rui Ueyama 299078aefe [ELF] Improve R_X86_64_PC64 and R_386_PC32 handlings
I believe they can be represented using dynamic relocations at runtime.
Since they are used rarely and there even seem to have no assembler
syntax to generate such relocations, I can't write tests for them.
2022-01-13 00:23:25 +09:00
Rui Ueyama 74e0e4f6d0 [ELF] Resurrect a file that was deleted by accident 2022-01-12 22:40:04 +09:00
Rui Ueyama 60c134893c [ELF] Create DT_DEBUG dynamic entry only for executables
gdb uses this field only if it is an executable. Having an extra
dynamic entry is usually harmless, but unfortunately, RPM's `rpmdeps`
tool uses the presence of this entry to distinguish executables from
shared object files. So, it is better not to create a DT_DEBUG entry
for shared object files.

Fixes https://github.com/rui314/mold/issues/254
2022-01-12 22:13:13 +09:00
Rui Ueyama e78608a37a [ELF] Rename a file
We collectively call .o and .so files as input files, so we should
name this file input-files.cc.
2022-01-12 20:41:49 +09:00
Rui Ueyama d2c57e99ca Refactor 2022-01-12 20:35:41 +09:00
Rui Ueyama bb18b29e13 [ELF] Fix --trace-symbol for DSO
Previously, --trace-symbol didn't work for shared object files.
2022-01-12 17:30:51 +09:00
Rui Ueyama 4b6ed1dbf7 Refactor 2022-01-12 17:11:48 +09:00
Rui Ueyama cea6a569af [ELF] Remove "GNU gold" from the --version string
That substring was introduced in de7ba9014f
to appease Gentoo's dev-libs/jansson-2.13.1-r1 package. But I think I
shouldn't done that from the beginning to avoid the sitaution of the
"User-Agent" string of the web browser, which everybody claims they
are Mozilla, AppleWebKit, Chrome and Safari simultaneously.
2022-01-12 16:38:04 +09:00
Rui Ueyama 205138f16e Refactor SharedFile class
This change factors out `elf_syms` and `first_global` from SharedFile
and move them into the base class.
2022-01-12 15:58:33 +09:00
Rui Ueyama 342ab00ac3 Fix typo 2022-01-12 14:11:20 +09:00
Rui Ueyama 24b629a216 Add information about compiler and standard library versions
Fixes https://github.com/rui314/mold/issues/265
2022-01-12 13:47:07 +09:00
Rui Ueyama cafc57fa65 Simplify 2022-01-12 11:16:21 +09:00
Rui Ueyama ce5749ce2f [ELF] Make symbol resolution deterministic
Previously, our parallel symbol resolution algorithm was not
deterministic in edge cases. As an example, consider the following two
source files:

  foo.c:
    inline void fn1() { ... }

  bar.c:
    inline void fn1() { ... }
    void fn2() { ... }

Let's say you compile these files and put them into an archive file.
If mold decided to pull out `foo.o` first for `fn1` and then `bar.o`
for `fn2`, then both `foo.o` and `bar.o` are included into a result.
However, if mold pulled out `bar.o` first, then there's no chance for
`foo.o` to be pulled out, so only `foo.o` would be included into a
result.

The algorithm implemented in this commit should be deterministic.
We do not override symbols when we mark live objects.
2022-01-12 11:16:21 +09:00
Rui Ueyama b6316ef085 Fix build-static.sh
The script was failing becuase CXX defaulted to `c++`.
2022-01-11 22:38:45 +09:00
Rui Ueyama 2464a5551a Update README 2022-01-11 00:20:23 +09:00
Rui Ueyama cf7cc2bd52 Fix comment 2022-01-10 11:12:11 +09:00
Rui Ueyama b18d488104 Refactor 2022-01-09 23:23:59 +09:00
Rui Ueyama 9ca6a9dc5e [ELF] Add -z ibt
Fixes https://github.com/rui314/mold/issues/229
2022-01-09 12:38:38 +09:00
Rui Ueyama 17bdd95936 Simplify 2022-01-09 12:34:35 +09:00
Rui Ueyama 31a43a7ba6 [ELF] Add -z cet-report
Fixes https://github.com/rui314/mold/issues/229
2022-01-09 12:34:32 +09:00
Rui Ueyama 778f8629eb Refactor 2022-01-09 12:34:32 +09:00
Rui Ueyama e29bd8f42b [ELF] Add -z shstk
Fixes https://github.com/rui314/mold/issues/229
2022-01-09 12:34:24 +09:00
Rui Ueyama 013e6ea06e [ELF] Fix .got.plt for IBTPLT
Previously, the `bnd jmp` instruction in a IBTPLT entry jumps to the
middle of an instruction due to an incorrect GOTPLT value. That happened
to work because a value that was there after the jump can be interpreted
as an `add` instruction and the control falled through the next `pushq`.
2022-01-09 10:14:35 +09:00
Rui Ueyama 27d836180f [ELF] Make an undefined symbol to be resolved to a common symbol in an archive
Previously, mold ignored common symbols in an archive when resolving
symbols. As a result, even if an undefined symbol could be resolved
using a common symbol in an archive, mold would end up with an undefined
symbol error.

This commit changes the behavior. Now, an undefined symbol is resolved
to a common symbol in an archive if exists.

Fixes https://github.com/rui314/mold/issues/256
2022-01-08 20:44:22 +09:00
Rui Ueyama 9b710cf832 Refactor 2022-01-08 15:11:25 +09:00
Rui Ueyama 0370e7f1b3 [ELF] Emit a compact PLT section if -z now is passed
Each entry in the regular PLT section takes 16 bytes.
This new PLT section reduces it to 8 bytes.
2022-01-08 14:57:50 +09:00
Rui Ueyama fbfa01dcd1 [ELF] Implement -z ibtplt
https://github.com/rui314/mold/issues/229
2022-01-08 14:09:12 +09:00
Rui Ueyama 18367e69a8 Refactor 2022-01-08 13:24:29 +09:00
Rui Ueyama badc733488 [ELF] Report an error if an incompatible object file is given
Previously, mold would crash if an incompatible object file was given.
https://github.com/rui314/mold/issues/260
2022-01-07 23:04:08 +09:00
Rui Ueyama 54e71f43d4 [ELF] Use .got.plt instead of .got to compute GOT-relative reloc values 2022-01-07 22:27:48 +09:00
Rui Ueyama 329e382c6d Simplify 2022-01-07 20:56:36 +09:00
Rui Ueyama 644fdc8a2f Simplify 2022-01-07 20:42:25 +09:00
Rui Ueyama c9352710f7 Fix clang buildbot 2022-01-07 18:53:01 +09:00
Rui Ueyama f4a37b2e95 Do not use clang/clang++ as the default compilers
mold does not have to be built with clang/clang++, so don't use
these compiler frontends.

mold also doesn't have be built with g++ (GNU C++ compiler).
We use `c++` command instead.

Fixes https://github.com/rui314/mold/issues/261
2022-01-07 18:45:05 +09:00
Rui Ueyama 3dd76cd953 Attempt to fix buildbots 2022-01-07 18:18:00 +09:00
Rui Ueyama d86a774ebf Fix Mach-O tests 2022-01-07 18:12:40 +09:00
Rui Ueyama e7decd2c77 Use relative paths in tests 2022-01-07 18:08:06 +09:00
Rui Ueyama 4324cb2998 Use cc and c++ instead of clang, gcc, clang++ or g++ in tests 2022-01-07 18:02:35 +09:00
Rui Ueyama 4348417dd8 [ELF] Do not convert .ctors/.dtors to .init_array/.fini_array
The .ctors section contains a list of pointers that have to be run
before `main`. It usually contains pointers to global constructors.
The .dtors are for global destructors.

The .init_array and .fini_array serve the same purpose as .ctors and
.dtors. The former sections are newer than the latter sections, and
most programs have already migrated to .{init,fini}_array.

.ctors/.dtors are very similar to .{init,fini}_array, but the order
in which pointers are executed is different. .ctors/.dtors are executed
from the end to the beginning, while .{init,fini}_array are executed
from the beginning to the end.

Previously, we converted .ctors/.dtors sections into .{init,fini}_array
sections. In order to do that, we not only merge .ctors/.dtors to
.{init,fini}_array but also reverse their section contents, so that
the initialization order remains the same.

However, there was a bug in that logic. We forgot to reverse the
locations of dynamic relocations for the converted sections.
So, .ctors/.dtors sections converted to .{init,fini}_array were executed
in a wrong order.

We have two choices to "fix" the issue: (1) reverse not only section
contents but also dynamic relocations, or (2) stop converting .ctors/.dtors.
In this patch, I chose (2).

Since LLVM lld doesn't convert .ctors/.dtors, it shouldn't cause an
issue for most programs.
2022-01-07 17:47:14 +09:00
Rui Ueyama 0e17dbeda8 [ELF] Make --defsym'ed symbols absolute
If a symbol is defined in the form of --defsym=foo=0x<hexvalue>,
it should be defined as an absolute symbol with the given value.
2022-01-07 16:28:14 +09:00
Rui Ueyama 3b33420fe5 [ELF] Support R_X86_64_PLTOFF64 2022-01-07 14:44:17 +09:00
Rui Ueyama e075e622ba Fix buildbots 2022-01-07 13:24:42 +09:00
Rui Ueyama 092286fc1d [ELF] Fix tests so that they work if CC is clang 2022-01-07 12:45:20 +09:00
Rui Ueyama 9004c514da Suppress -Wformat-overflow GCC warning 2022-01-07 10:57:54 +09:00
Rui Ueyama a27c113d35 Attempt to fix i386 host build
This change cherrypicks
https://github.com/oneapi-src/oneTBB/pull/609/commits/542a27fa1cfafaf76772e793549d9f4d288d03a9

https://github.com/rui314/mold/issues/240
2022-01-07 10:09:34 +09:00
Rui Ueyama 1acabd65e3 Add a comment 2022-01-06 23:42:15 +09:00
Rui Ueyama 2f86fef3a0 [ELF] Fix assertion failure
This patch fixes a regression introduced in
43fa021d48.

Fixes https://github.com/rui314/mold/issues/259
2022-01-06 22:01:56 +09:00
Rui Ueyama 79e397ea03 Add a missing #include 2022-01-06 20:54:23 +09:00
Rui Ueyama daa88f2f06 [ELF] Handle '[]' in glob patterns
Previously, mold crashes due to an invalid regex pattern exception
when `[...]` is given as a version script pattern.

Fixes https://github.com/rui314/mold/issues/258
2022-01-06 20:45:46 +09:00
Rui Ueyama 404fa1288b [ELF] Add a test
https://github.com/rui314/mold/issues/258
2022-01-06 18:27:31 +09:00
Rui Ueyama c33df4771f Revert "[ELF] Keep SHF_MERGE and SHF_STRINGS bits"
This reverts commit 4d78510cb0.

If the SHF_MERGE bit is set, sh_entsize must have the size of the unit
for that section. In the previous commit, we changed the behavior of
mold so that it sets SHF_MERGE bits in the output file. However, we
didn't set sh_entsize. So, the resulting section is technically invalid.

We merge sections with different sh_entsize into a single output
section, so there's no easy way to "keep" the original value.

In this commit, we simply revert the previous change so that mold won't
set SHF_MERGE in the output file.

https://github.com/rui314/mold/issues/155
2022-01-06 18:19:06 +09:00
Rui Ueyama 8f23d17ece Fix macOS build 2022-01-06 17:53:40 +09:00
Rui Ueyama 09b9d0af1d [ELF] Remove redundant code 2022-01-06 17:48:46 +09:00
Rui Ueyama 43fa021d48 [ELF] Do not place non-exported symbols into .gnu.hash
Previously, mold put all global symbols into .gnu.hash. Although I
believe it was not an error, it bloated the size of .gnu.hash because
.gnu.hash needs only exported symbols.

https://github.com/rui314/mold/issues/255
2022-01-06 17:47:19 +09:00
Rui Ueyama 907f713e51 [ELF] Remove NEEDS_DYNSYM flag from symbol
I wanted to make sure that a symbol X is in .dynsym if and only if
(X.is_imported || X.is_exported).
2022-01-06 15:27:39 +09:00
Rui Ueyama 276b9d005d [ELF] Add ARM64 to --help message
Fixes https://github.com/rui314/mold/issues/251
2022-01-05 21:41:53 +09:00
Rui Ueyama fe14a08761 Make make to take EXTRA_CFLAGS and EXTRA_CXXFLAGS 2022-01-05 21:36:48 +09:00
Rui Ueyama 3865a10600 Merge pull request #252 from sicherha/fix-tests-on-aarch64
Fix test failures on aarch64
2022-01-05 21:07:26 +09:00
Christoph Erhardt df865725ed Fix test failures on aarch64
Signed-off-by: Christoph Erhardt <[email protected]>
2022-01-05 12:26:56 +01:00
Rui Ueyama be49d673a5 [ELF] Deduce emulation from input files if -m is not given 2022-01-05 19:55:32 +09:00
Rui Ueyama 31a6a1164e Merge pull request #248 from sina-ht/use-pkg-config
Use pkg-config to know where openssl header files reside
2022-01-05 18:40:55 +09:00
Hiroshi Takekawa e8bdbaab22 Use pkg-config to know where openssl header files reside
Let Makefile know how to locate openssl header files in a directory other than
the default directories by using pkg-config. If you don't have pkg-config, then
$(shell) returns empty.

Signed-off-by: Hiroshi Takekawa <[email protected]>
2022-01-05 18:25:17 +09:00
Rui Ueyama 285c9e2c32 [ELF] Attempt to fix buildbots 2022-01-04 20:59:33 +09:00
Rui Ueyama a5029d19a8 [ELF] Automatically fall back to ld.bfd or ld.lld if LTO is in use
This is very hacky but highly practical, so I couldn't resist to not
implement this. We should support LTO natively in the future. In the
meantime, this feature should work as a poor-man's replacement.

Fixes https://github.com/rui314/mold/issues/242
2022-01-04 20:50:16 +09:00
Rui Ueyama 0565a6a6ff Merge pull request #241 from sina-ht/use-pkg-config
Use pkg-config to know where libcrypto resides
2022-01-04 15:05:34 +09:00
Hiroshi Takekawa 6987f06151 Use pkg-config to know where libcrypto resides
Let Makefile know how to link libcrypto in a directory other than the default
directories by using pkg-config. If you don't have pkg-config, then $(shell)
returns empty.

Signed-off-by: Hiroshi Takekawa <[email protected]>
2022-01-04 14:16:09 +09:00
Rui Ueyama a2839f60dd [ELF] Improve an error message
Fixes https://github.com/rui314/mold/issues/233
2022-01-04 13:49:48 +09:00
Rui Ueyama 1bc7c332ab Do not link against librt on Android
Fixes https://github.com/rui314/mold/issues/231
2022-01-04 13:44:55 +09:00
Rui Ueyama 8ffe4cd472 [ELF] Ignore dead FDE entries
Looks like some CUDA object files contains FDEs whose relocation has
r_sym with value 0. Such FDEs are dead and should be ignored.

Unfortunately, there's no easy way to produce an object file containing
such relocation, so this change lacks a testcase.

Fixes https://github.com/rui314/mold/issues/235
2022-01-04 13:32:26 +09:00
Rui Ueyama 70704c7c09 Disable -Wc++11-narrowing on Android
Fixes https://github.com/rui314/mold/issues/231
2022-01-03 20:59:19 +09:00
Rui Ueyama 5029a6d044 Simplify 2022-01-03 20:56:59 +09:00
Rui Ueyama 9894b3173b [ELF] Add --default-symver
Fixes https://github.com/rui314/mold/issues/228
2022-01-03 20:29:33 +09:00
Rui Ueyama 4b2f10d009 [ELF] Improve test 2022-01-03 20:15:04 +09:00
Rui Ueyama 8590fd9947 Better way to suppress -Wint-to-pointer-cast 2022-01-03 18:15:51 +09:00
Rui Ueyama 7bb478b683 Fix macOS build 2022-01-03 18:05:33 +09:00
Rui Ueyama ce2fee10b5 Use strerror_r() instead of strerror()
I have no idea why, but 2b818dbe44 made
mold's chrome link time slower by ~8%. This patch removes the use
of std::mutex.
2022-01-03 17:46:26 +09:00
Rui Ueyama 4033b53d7f Merge pull request #232 from OctopusET/main
Fix build error on riscv64 (#222)
2022-01-03 16:39:38 +09:00
Rui Ueyama 8c0e2378c9 Update the man page 2022-01-03 16:19:43 +09:00
Moon Sungjoon 3fb54162d6 Fix build error on riscv64 (#222)
Patch from this PR: https://github.com/oneapi-src/oneTBB/pull/550

Signed-off-by: Moon Sungjoon <[email protected]>
2022-01-03 16:06:02 +09:00
Rui Ueyama 571a4f72a4 Merge pull request #230 from Cutlery-Drawer/mdoc
Rewrite manual page in `mdoc(7)`
2022-01-03 14:34:55 +09:00
Rui Ueyama 2b818dbe44 Use strerror() instead of strerror_r()
This should fix the build on Android.
Fixes https://github.com/rui314/mold/issues/231
2022-01-02 23:40:51 +09:00
Rui Ueyama 19dce5342f Fix -Wc++11-narrowing
Fixes https://github.com/rui314/mold/issues/231
2022-01-02 22:47:23 +09:00
Alhadis 632fe22240 Rewrite manual page in mdoc(7)
Signed-off-by: John Gardner <[email protected]>
2022-01-02 22:13:09 +11:00
Rui Ueyama 5618038463 [ELF] Show a better error message for the empty input file
Fixes https://github.com/rui314/mold/issues/227
2022-01-02 19:33:07 +09:00
Rui Ueyama 9178b762fc Refactor 2022-01-02 15:44:52 +09:00
Rui Ueyama fea59750ed Do not use LTO to build oneTBB as it causes link failure 2022-01-02 12:52:01 +09:00
Rui Ueyama fba4cb9114 Merge commit 'b82badd127f38f4fea6a4f1c0ec22900dd9dd8e3' as 'third-party/tbb' 2022-01-02 12:51:35 +09:00
Rui Ueyama b82badd127 Squashed 'third-party/tbb/' content from commit 3df08fe23
git-subtree-dir: third-party/tbb
git-subtree-split: 3df08fe234f23e732a122809b40eb129ae22733f
2022-01-02 12:51:35 +09:00
Rui Ueyama d3dc8a2377 Temporarily remove oneTBB to upgrade to v2021.5.0 2022-01-02 12:51:29 +09:00
Rui Ueyama df24372775 Merge pull request #220 from rmcgibbo/main
[ELF] Fix parsing of --no-dynamic-linker on command line
2022-01-02 11:54:27 +09:00
Rui Ueyama 0a8231b59d [ELF] Support R_X86_64_GOTOFF64
Fixes https://github.com/rui314/mold/issues/224
2022-01-02 11:14:33 +09:00
Rui Ueyama 1f7727975c [ELF] Improve test 2022-01-02 11:13:12 +09:00
Robert T. McGibbon 68fa44d399 [ELF] Fix parsing of --no-dynamic-linker on command line
Signed-off-by: Robert T. McGibbon <[email protected]>
2022-01-01 12:22:48 -05:00
Rui Ueyama ab83ed8691 Merge pull request #221 from sicherha/deduplicate-quick-exit
Remove duplicate code
2022-01-01 19:51:32 +09:00
Christoph Erhardt ebe982c443 Remove duplicate code
The exact same four lines of code exist a few lines below this hunk.

Signed-off-by: Christoph Erhardt <[email protected]>
2022-01-01 09:44:37 +01:00
Rui Ueyama 4ccbd24c60 Merge pull request #219 from sicherha/libcxx-13-compatibility
Compatibility for libc++ < 14, plus two additional fixes
2022-01-01 09:12:00 +09:00
Christoph Erhardt da0d0a7b52 Improve workaround for libc++ < 14
The underlying issue got fixed in libc++ 14 with
https://reviews.llvm.org/D97084, but Xcode 13.1 still ships libc++ 13.

Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-31 16:09:26 +01:00
Christoph Erhardt a537509aa6 Fix errno_string() returning an empty string on GNU
Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-31 16:09:26 +01:00
Christoph Erhardt 865b6fed89 Terminate argv array with nullptr
This fixes an error where the subsequent `execv()` occasionally fails
with 'Bad address'.

Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-31 15:43:22 +01:00
Rui Ueyama d100a735a0 Refactor: rename a function 2021-12-31 21:28:16 +09:00
Rui Ueyama 902f23c456 Refactor 2021-12-31 21:04:07 +09:00
Rui Ueyama c92e192e56 [ELF] Remove redundant inline keywords 2021-12-31 21:02:06 +09:00
Rui Ueyama 440ff27e58 [ELF] Refactor 2021-12-31 21:01:59 +09:00
Rui Ueyama 085e3c3abe Simplify 2021-12-31 18:04:39 +09:00
Rui Ueyama 58e43633fc Revert "Revert "Use C++17 filesystem API""
This reverts commit f29a85f20a with a fix
for a build failure.
2021-12-31 16:39:54 +09:00
Rui Ueyama d743acb6cd Disable mimalloc on Android host 2021-12-31 16:14:15 +09:00
Rui Ueyama cb2b4b8f72 Bump mold version to 1.0.1 2021-12-31 14:10:03 +09:00
Rui Ueyama f0874f187a Merge pull request #214 from tklauser/macho-fatal-no-input-files
[Mach-O] Fatal if no input files are given
2021-12-31 10:45:24 +09:00
Tobias Klauser c029521b24 [Mach-O] Fatal if no input files are given
Follow the same behavior in the ELF version of read_input_files and
fatal in case no input files are given. This avoids a segfault in
successive functions when running `./ld64.mold` without any arguments.

Signed-off-by: Tobias Klauser <[email protected]>
2021-12-30 23:26:28 +01:00
Rui Ueyama 7a8beac1c2 [Mach-O] Remove homebrew paths from -L and -I flags
I added homebrew's library paths to -L and -I flags for libxxhash.
Now mold includes libxxhash as a subdirectory, we don't need it.
2021-12-31 00:45:58 +09:00
Rui Ueyama b5172caeb5 Improve test
I think `-m32` means ARM32 if this test runs on ARM64.
2021-12-31 00:13:07 +09:00
Rui Ueyama baff2948d4 Merge pull request #213 from tklauser/test-fix-i386-mergable-strings
Fix skip check in i386-mergable-strings.sh
2021-12-31 00:07:04 +09:00
Tobias Klauser 66c9659f5a Fix skip check in i386-mergable-strings.sh
Check for the ability to build 32-bit binaries like in the other i386
test cases.

Signed-off-by: Tobias Klauser <[email protected]>
2021-12-30 15:58:11 +01:00
Rui Ueyama 9697febd83 Merge pull request #212 from tklauser/macos-x86_64
Avoid infinite recursion in do_main<X86_64>
2021-12-30 22:18:58 +09:00
Rui Ueyama f29a85f20a Revert "Use C++17 filesystem API"
This reverts commit f6e91df440 because
it causes build breakages for a lot of Gentoo packages.
2021-12-30 22:15:52 +09:00
Tobias Klauser cc9f6f430b Avoid infinite recursion in do_main<X86_64>
Running mold on x86_64 macOS 11 currently leads to a segfault due to
inifinite recursion in do_main<X86_64>. Protect against this case by
only calling into do_main<X86_64> once if cputype doesn't match.

Signed-off-by: Tobias Klauser <[email protected]>
2021-12-30 14:08:16 +01:00
Rui Ueyama 7776165ef7 Fix macOS build 2021-12-30 21:40:42 +09:00
Rui Ueyama f6e91df440 Use C++17 filesystem API 2021-12-30 21:32:01 +09:00
Rui Ueyama 3f6e889449 Do not use exceptions
If a given path does not exist, std::filesystem::canonical throws an
exception unless `ec` is given.
2021-12-30 20:33:26 +09:00
Rui Ueyama 8d838e4fd1 Merge pull request #210 from sicherha/path-buffer-sizes
Fix handling of path buffer sizes
2021-12-30 16:51:02 +09:00
Christoph Erhardt 68c4b76352 Use std::filesystem to circumvent buffer-size issues
Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-30 08:43:36 +01:00
Rui Ueyama f6e800655b [ELF] Add a description for --defsym to the man page 2021-12-30 15:44:08 +09:00
Rui Ueyama 35ca107361 Add a missing header 2021-12-30 15:10:07 +09:00
Rui Ueyama 3f8f2e89e5 Show "Passed all tests" message in green 2021-12-30 15:05:17 +09:00
Rui Ueyama d49c50ddaf [ELF] Add --defsym
Fixes https://github.com/rui314/mold/issues/208
2021-12-30 14:48:39 +09:00
Rui Ueyama 44ae92c4ed Remove redundant "../../" from test script paths
So that it's easier to see the output of the test script running
with `bash -x` . This change was made using sed.
2021-12-30 11:11:27 +09:00
Rui Ueyama ec628cb0bf Merge pull request #209 from sicherha/fix-shellcheck-warnings
Add lots of quotes to shell scripts
2021-12-30 07:04:09 +09:00
Christoph Erhardt 0dcc910223 Add lots of quotes to shell scripts
This makes it possible to build and test mold in a path that contains
whitespace characters - with the notable exception of the tests where
`LD_PRELOAD` is used. That's because `LD_PRELOAD` unconditionally treats
any whitespace as separator, regardless of quoting.

The following ShellCheck warnings are eliminated by this commit:
* SC2046: Quote this to prevent word splitting.
* SC2086: Double quote to prevent globbing and word splitting.

Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-29 22:18:19 +01:00
Rui Ueyama 1c6c8c3479 Update README 2021-12-29 21:36:06 +09:00
Rui Ueyama 99866f01b2 Update README as GCC 12.1.0 or later accepts -fuse-ld=mold
Fixes https://github.com/rui314/mold/issues/205
2021-12-29 21:21:45 +09:00
Rui Ueyama f3766cda81 [ELF] Add -z {max,common}-page-size
Fixes https://github.com/rui314/mold/issues/203
2021-12-29 17:14:08 +09:00
Rui Ueyama 946e0c3acd Avoid uname -o
macOS's uname does not support the `-o` option.
2021-12-29 16:12:05 +09:00
Rui Ueyama e1ce8994b9 Merge pull request #204 from Ingrater/fixAndroidBuild
Fix building on Android
2021-12-29 15:55:57 +09:00
Rui Ueyama b151de6684 [ELF] Fix .tbss layout
Fixes https://github.com/rui314/mold/issues/126
2021-12-29 15:50:56 +09:00
Rui Ueyama 4bfaa964a7 Refactor 2021-12-29 12:42:51 +09:00
Rui Ueyama cfed33ee42 Revert "[ELF] Fix a layout issue for thread-local bss sections"
This reverts commit 5c35d2a41f
because it causes a regression https://github.com/rui314/mold/issues/201.

I'll fix https://github.com/rui314/mold/issues/126 in another patch.
2021-12-28 18:36:31 +09:00
Rui Ueyama aa972e5351 [ELF] Remove redundnat "mold: " from error messages 2021-12-28 12:42:25 +09:00
Rui Ueyama 27509f0408 Fix typo
https://github.com/rui314/mold/issues/203
2021-12-28 12:41:26 +09:00
Benjamin Thaut f943da88d7 Do not use mimalloc when building on Android as it causes a infinite recursuion.
Signed-off-by: Benjamin Thaut <[email protected]>
2021-12-27 17:13:25 +01:00
Rui Ueyama e8dcecfff0 [ELF] Create a symlink to mold as /usr/libexec/mold/ld
So that you can pass that directory name as an argument for `-B`.
2021-12-27 13:37:35 +09:00
Rui Ueyama cfccfc247d Update README 2021-12-27 12:25:44 +09:00
Rui Ueyama 04ad22d326 [ELF] Refactor
std::string_view::find_first_of() is faster than compiling a regex
and runnning it.
2021-12-26 23:33:34 +09:00
Rui Ueyama 1228bf3865 Merge pull request #200 from jmglogow/fix_version_globbing
[ELF] Fix '?' for globbing + version scripts
2021-12-26 23:29:02 +09:00
Jan-Marek Glogowski 31b0248b05 [ELF] Fix '?' for globbing + version scripts
For globbing, '?' matches a single character, so must be replaced
with '.'. As a result, don't skip the later regex matching for
version scripts, if the pattern contains either '*' or '?'.

Signed-off-by: Jan-Marek Glogowski <[email protected]>
2021-12-26 15:02:43 +01:00
Rui Ueyama d1161137d9 [ELF] Fix Initial-Exec TLS variables
Previously, GOTTPOFF relocations against Initial-Exec TLS variables
got wrong value.

Fixes https://github.com/rui314/mold/issues/197
2021-12-26 20:51:37 +09:00
Rui Ueyama 888474b59c [ELF] Ignore --warn-execstack
mold never sets executable bit to the stack area, so we can
simply ignore the flag.

Fixes https://github.com/rui314/mold/issues/196
2021-12-26 10:29:16 +09:00
Rui Ueyama 5c35d2a41f [ELF] Fix a layout issue for thread-local bss sections
Previously, if there are more than one TLS BSS section (e.g. .tbss and
.tcommon), mold layout them in such a way that they overlap in memory.
That caused a mysterious program crash to the programs compiled with DMD,
a D compiler.

Fixes https://github.com/rui314/mold/issues/126
2021-12-25 20:32:33 +09:00
Rui Ueyama eaa8bf8b30 [ELF] Fix an error message 2021-12-25 19:29:04 +09:00
Rui Ueyama 0d3c9811c1 Improve make test 2021-12-25 16:59:43 +09:00
Rui Ueyama 6e290aab3e [ELF] Implement --color-diagnostics 2021-12-25 16:55:51 +09:00
Rui Ueyama d1a47ee57b [ELF] Ignore --apply-dynamic-relocs
We always write dynamic relocation addends for bug-compatibiltiy
with the Go toolchain. Therefore, this flag is no-op.

See 919e8cbc62 for the details of
the Go's bug.
2021-12-25 16:22:35 +09:00
Rui Ueyama 7aa5c393e4 [ELF] Support extern "C++" in dynamic lists
Fixes https://github.com/rui314/mold/issues/149
2021-12-25 15:10:24 +09:00
Rui Ueyama 7b3c640472 Fix --dynamic-list
Previously, symbols were not exported from an executable even if
they are specified so by a dynamic list.
2021-12-25 13:38:24 +09:00
Rui Ueyama d9606d65b5 [ELF] Remove R_X86_64_{TLSLD,TPOFF32,TPOFF64} error checks
Compiler should not emit these types of relocations unless it knows
they will never refer imported symbols. However, it looks like GCC 7
emits these relocations against global symbols with STB_GNU_UNIQUE
attribute, which can be resolved to a symbol in other ELF module at
runtime. It looks like the bug only exists in GCC 7 and fixed in
recent versions of GCC.

mold verifies the above conditions and reports an error if the
condition is violated. On the other hand, GNU ld and LLVM lld don't
verify and simply create a shared library that is not always
guaranteed to work.

Give this situation, I decided to simply remove the verification code
from mold. This is what other linkers do, and it make mold "compatible"
with GCC 7.

Fixes https://github.com/rui314/mold/issues/145
2021-12-24 22:50:15 +09:00
Rui Ueyama 13245fd9a8 Merge pull request #194 from sicherha/container-improvements
Container improvements
2021-12-24 22:12:08 +09:00
Rui Ueyama 04ccd4dbdd [ELF] Export undef symbols if both -z defs and -warn-undefined-symbols are given
When we are creating a DSO, unresolved undefined symbols are promoted to
dynamic symbols by default so that they will get another chance to be
resolved at runtime.

You can suppress this behavior by passing `-z defs`. If that option is
given, remaining undefs are reported as errors instead of being silently
promoted.

It looks like `-warn-undefined-symbols` negates the effect of `-z defs`.
So, if both options are given, remaining undefs are promoted to dynamic
symbols. mold previously silently make such symbols absolute symbols with
value 0.

Fixed https://github.com/rui314/mold/issues/152
2021-12-24 21:13:15 +09:00
Rui Ueyama 5601cf4236 [ELF] Add -z separate-code, -z noseparate-code and -z separate-lodable-segments
Fixes https://github.com/rui314/mold/issues/172
2021-12-24 20:28:45 +09:00
Rui Ueyama 088912e2fc [ELF] Create less number of PT_NOTE segments 2021-12-24 20:15:56 +09:00
Rui Ueyama 29d956a7b0 [ELF] Fix handling of DSO symbols with default version
Fixes https://github.com/rui314/mold/issues/150
2021-12-24 16:10:01 +09:00
Rui Ueyama 1ba6cc9188 [ELF] Handle relocation referring 0th symbol correctly 2021-12-24 16:09:57 +09:00
Christoph Erhardt 27f0e8c0e5 Pass on command-line arguments to make inside the container
This allows the caller to pass extra build options if desired.
Moreover, it is possible to run `./build-static.sh test` in order to
execute the testsuite inside the container.

Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-23 16:01:39 +01:00
Christoph Erhardt 92bc33eed4 Add test dependencies to Docker container
These packages contain the i686 toolchain as well as the `file` and
`hexdump` utilities. They increase the size of the container image by
approximately 10 % (70 MiB).

Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-23 16:01:39 +01:00
Christoph Erhardt 6160f3874f Make build-static.sh compatible with Podman and SELinux
* Append the `Z` option for the mounted volume
* Add `--userns=keep-id` if Podman's `docker` wrapper is used

Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-23 16:01:37 +01:00
Rui Ueyama 135f17c5aa [ELF] Handle --sysroot
Attempt to fix https://github.com/rui314/mold/issues/150
2021-12-23 21:23:00 +09:00
Rui Ueyama d60e302f70 Merge pull request #192 from sicherha/fix-broken-mold-wrapper
Do not use custom `LDFLAGS` for linking `mold-wrapper.so`
2021-12-23 20:31:16 +09:00
Christoph Erhardt d858068975 Do not use custom LDFLAGS for linking mold-wrapper.so
Linking a dynamic library with `-static` would prevent it from being
loadable.

Fixes #191.

Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-23 10:41:53 +01:00
Rui Ueyama 8c86c28496 Add -z nodefaultlib
Fixes https://github.com/rui314/mold/issues/184
2021-12-23 15:01:57 +09:00
Rui Ueyama 76407a6646 Do not create a PT_NOTE segment for non-alloc .note sections
Fixes https://github.com/rui314/mold/issues/185
2021-12-23 14:04:08 +09:00
Rui Ueyama 56f63dd295 Merge pull request #186 from sicherha/fix-cppcheck-warnings
[ELF] Add missing calls to `va_end()`
2021-12-22 23:13:25 +09:00
Christoph Erhardt c8cec61e1c [ELF] Add missing calls to va_end()
Reported by cppcheck. Omitting `va_end()` causes undefined behaviour:
https://en.cppreference.com/w/c/variadic/va_end

Signed-off-by: Christoph Erhardt <[email protected]>
2021-12-22 14:10:01 +01:00
Rui Ueyama feb63f7b5b [ELF] Ignore -z nodefaultlib
Fixes https://github.com/rui314/mold/issues/184
2021-12-22 21:04:21 +09:00
Rui Ueyama 4d5e4730a2 Fix typo 2021-12-22 20:44:29 +09:00
Rui Ueyama 24bf92c27c [ELF] Fix a crash bug 2021-12-22 20:41:11 +09:00
Rui Ueyama 8bc57363c8 [ELF] Issue a warning instead of error for an unknown -z option
Fixes https://github.com/rui314/mold/issues/180
2021-12-22 20:08:14 +09:00
Rui Ueyama e65560f963 Add the Repology badge 2021-12-22 19:59:39 +09:00
Rui Ueyama 9326f90d14 Remove dead code
Fixes https://github.com/rui314/mold/issues/175
2021-12-22 17:08:41 +09:00
Rui Ueyama 390e737dea Attempt to silence cppcheck warning
https://github.com/rui314/mold/issues/176
2021-12-22 17:06:23 +09:00
Rui Ueyama 358464d5ad Reduce docker image size for ./build-static.sh
Suggested by https://github.com/rui314/mold/issues/161#issuecomment-997184302.
2021-12-22 14:01:46 +09:00
Rui Ueyama 5bc8bc9d64 Fix -Wall warnings
Fixes https://github.com/rui314/mold/issues/174
2021-12-22 12:10:20 +09:00
Rui Ueyama 74fdf90d70 Fix a test for clang and clang-as
clang-as does not support --keep-locals, so use its synonym -L
which is supported both by GNU as and clang-as.

Fixes https://github.com/rui314/mold/issues/173
2021-12-22 11:42:29 +09:00
Rui Ueyama 530568e662 [ELF] Use a better type 2021-12-20 20:08:25 +09:00
Rui Ueyama f9ff04866e [ELF] Add --threads=N option
Fixes https://github.com/rui314/mold/issues/168
2021-12-20 16:29:22 +09:00
Rui Ueyama 96c44be33d [ELF] Add an assertion 2021-12-20 14:38:36 +09:00
Rui Ueyama 2bd3bc6d47 Revert "[ELF] Relax TLSLD error check"
This reverts commit 1baac27e8c.

In order to link gcc 7's output, relaxing this error check wasn't
enough as reported on https://github.com/rui314/mold/issues/145.
We need to remove more error checks or keep them as-is.
For now, I'll restore all the error checks, but before making another
release, I may remove all error checks to accept gcc 7's buggy
output files.
2021-12-20 13:38:53 +09:00
Rui Ueyama fb89ce22fd Update third-party/mimalloc to v2.0.3 2021-12-20 13:01:38 +09:00
Rui Ueyama 3e397d6e78 Cosmetic changes 2021-12-20 12:35:06 +09:00
Rui Ueyama c824e7831a [ELF] Show a hint if -b binary is given
Fixes https://github.com/rui314/mold/issues/164
2021-12-20 12:26:02 +09:00
Rui Ueyama 8f1c328d1a Cosmetic changes 2021-12-20 11:46:28 +09:00
Rui Ueyama 4d78510cb0 [ELF] Keep SHF_MERGE and SHF_STRINGS bits
Once object files are linked into an executable or a shared object file,
sections are no longer splittable. So these bits don't make sense anymore.

Previously, we cleared the bits. But it looks like there are tools that
are picky about them. So we'll just leave them as-is.

Fixes https://github.com/rui314/mold/issues/155
2021-12-18 17:05:41 +09:00
Rui Ueyama dbc9869d7e Revert "[ELF] Use RE2 for version script symbol matching"
This reverts commit 27147dfe32.

I don't think the performance improvement wasn't large enough to
justify adding more dependencies to third-party libraries.

https://github.com/rui314/mold/issues/156
2021-12-18 13:26:35 +09:00
Rui Ueyama 27147dfe32 [ELF] Use RE2 for version script symbol matching
This is an attempt to speed up version script application.
It looks like RE2 is faster than libstdc++'s std::regex.
This change reduces libQt6Gui.so.6.3.0's link time by about 30%.

https://github.com/rui314/mold/issues/156
2021-12-18 13:23:18 +09:00
Rui Ueyama 665bffa52f Import xxhash library to third-party for ease of building
In some environment, XXH3 isn't available as a binary package.
If you do not like to use the imported xxhash library but instead
link to system's libxxhash.so, invoke make with `SYSTEM_XXHASH=1`.
2021-12-18 13:05:54 +09:00
Rui Ueyama 65dc32fdb8 Merge commit '0621402e0a83a71ee5a13e92ce6a5de0f14dc1f2' as 'third-party/xxhash' 2021-12-18 12:42:16 +09:00
Rui Ueyama 0621402e0a Squashed 'third-party/xxhash/' content from commit 35b0373c
git-subtree-dir: third-party/xxhash
git-subtree-split: 35b0373c697b5f160d3db26b1cbb45a0d5ba788c
2021-12-18 12:42:16 +09:00
Rui Ueyama 1d6d0e2fe3 Merge pull request #158 from sylvestre/second-fix
test/pie.sh: extend the check to other output
2021-12-17 20:16:07 +09:00
Rui Ueyama 4f37c3d024 Fix tests in non-English environment
These test files are not only invoked by make by also directly by hand.
Unsetting `LANG` on each file so that they don't fail in a non-English
environment.

This change is mechanically created by the following command:

  sed -i '1 a export LANG=' test/*/*.sh
2021-12-17 20:06:50 +09:00
Rui Ueyama 072cf0eb30 Merge pull request #153 from sylvestre/patch-1
Force the execution of tests in English
2021-12-17 20:02:31 +09:00
Rui Ueyama 455e729393 [ELF] Make version script application faster
Instead of calling regex match multiple times, create a single
regex and call regex match function only once.

https://github.com/rui314/mold/issues/156
2021-12-17 19:09:26 +09:00
Sylvestre Ledru fbee0ec588 test/pie.sh: extend the check to other output
On debian unstable, the output doesn't contain 'Shared object file'
Parsing a different output

Signed-off-by: Sylvestre Ledru <[email protected]>
2021-12-17 10:02:21 +01:00
Sylvestre Ledru 9a96dc1c33 Force the execution of tests in English
If the tests are executed in a different locale like French, some logs  (ex: ```out/test/elf/auxiliary/log```) can contain output like:

```La section dynamique à l'offset 0x2000 contient 10 entrées :```

which of course doesn't work as the grep is
```
fgrep -q 'Auxiliary library: [foo]' $t/log
```

Signed-off-by: Sylvestre Ledru <[email protected]>
2021-12-17 09:25:42 +01:00
Rui Ueyama 36633895e5 [ELF] Fix undefined symbol versions
Previously, if the following conditions are met:

 - we are creating a shared object file,
 - there's an undefined symbol in an input object file that are
   to be promoted to a dynamic symbol, and
 - a version script defining a default version is given,

then the symbol gets the default version. But that's a wrong behavior
because version scripts should not affect symbols that are not defined
in an output file.

This change fixes the issue by not setting version to such symbol.

Fixes https://github.com/rui314/mold/issues/151
2021-12-17 17:07:36 +09:00
Rui Ueyama 1baac27e8c [ELF] Relax TLSLD error check
This is a workaround for https://github.com/rui314/mold/issues/145
2021-12-17 14:35:43 +09:00
Rui Ueyama a9f9163d2c Fix macOS build failure 2021-12-17 08:51:31 +09:00
Rui Ueyama e4fc96f691 [ELF] Improve error message 2021-12-17 08:47:31 +09:00
Miguel Raz Guzmán Macedo d3adb6ca09 add missing -lrt flag to Makefile (#141)
* add missing -lrt flag to Makefile

vis #140

* update where -lrt is added to Makefile

* add -lrt in Makefile to appropriate line

Signed-off-by: Miguel Raz Guzmán Macedo <[email protected]>
2021-12-17 08:47:01 +09:00
Rui Ueyama 46f22a2778 Merge pull request #147 from Frederik-Baetens/main
fix clang pkg name in fedora instructions
2021-12-16 21:02:20 +09:00
Rui Ueyama 75bd303b15 Merge branch 'main' into main 2021-12-16 21:02:00 +09:00
Rui Ueyama 1c9eea0033 Merge pull request #148 from markusgod/patch-1
Add libstdc++-devel to fedora dependencies.
2021-12-16 20:39:08 +09:00
Mark Nefedov c77271aa56 Add libstdc++-devel to fedora dependencies.
In build environments such as SUSE OBS or Fedora COPR build fails without clang standard library.

https://build.opensuse.org/package/show/home:MarkNefedov/mold
2021-12-16 14:35:46 +03:00
Rui Ueyama 04d5abc9eb Attempt to fix an ODR violation 2021-12-16 20:34:13 +09:00
Frederik Baetens 031ee33093 fix clang pkg name in fedora instructions 2021-12-16 12:22:30 +01:00
Rui Ueyama 307552a70c Merge pull request #144 from rurban/patch-2
start stop copy pasta
2021-12-16 15:43:20 +09:00
Reini Urban f17a2eb941 start stop copy pasta 2021-12-16 07:31:21 +01:00
Rui Ueyama 613ca6fc80 Merge pull request #143 from thesamesam/main
build: fix build-static.sh (adapt to Makefile changes: respect LDFLAGS)
2021-12-16 14:31:03 +09:00
Sam James e610f62818 build: fix build-static.sh (adapt to Makefile changes: respect LDFLAGS)
Closes: https://github.com/rui314/mold/issues/138
Signed-off-by: Sam James <[email protected]>
2021-12-16 04:18:32 +00:00
Rui Ueyama 6fd76f2619 [Mach-O] Fix build breakage on Alpine by renaming PAGE_SIZE
PAGE_SIZE is sometimes defined by a header file as macro.
This change renames it COMMON_PAGE_SIZE.

Fixes https://github.com/rui314/mold/issues/142
2021-12-16 13:05:13 +09:00
Rui Ueyama ed9924895d Bump mold version to 1.0.0 2021-12-15 21:25:26 +09:00
Rui Ueyama 27f72ac320 Fix tests 2021-12-15 20:42:27 +09:00
Rui Ueyama ba1b63eb76 Fix GCC 10 link failure
Fixes https://github.com/rui314/mold/issues/136
2021-12-15 14:42:01 +09:00
Rui Ueyama 9c651d513a Update README 2021-12-15 10:44:58 +09:00
Rui Ueyama d9903d72c7 Make make uninstall to remove an installed man page 2021-12-15 10:41:50 +09:00
Rui Ueyama c9a2fd1d7d Fix a test 2021-12-15 10:36:51 +09:00
Rui Ueyama e99baebc49 Merge pull request #135 from thesamesam/main
build: respect user *FLAGS, don't compress man pages
2021-12-15 10:36:09 +09:00
Sam James 0c6458da26 build: don't compress man page
Negligible saving and downstream, distributions usually recompress or
compress with their own specific options. Unconditionally compressing
man pages, while well intended, usually creates more hassle there.

Signed-off-by: Sam James <[email protected]>
2021-12-14 11:56:47 +00:00
Sam James 770b7312f5 build: respect user *FLAGS
- Respect user CXXFLAGS
- Rename CPPFLAGS (previously used in the sense of "flags for the C++ compiler") -> CXXFLAGS
- CPPFLAGS is generally used for "flags for the C(++) preprocessor.", so let's
  use it for that
- Respect user LDFLAGS
  (In one instance, we were respecting LDFLAGS, but doing it too late.
  We need to pass LDFLAGS _before_ any objects in order for -Wl,--as-needed
  to work correctly.)

Signed-off-by: Sam James <[email protected]>
2021-12-14 11:56:47 +00:00
Rui Ueyama f05c3162cd Update README 2021-12-13 15:28:26 +09:00
Rui Ueyama d821445304 Update README 2021-12-12 21:47:26 +09:00
Rui Ueyama 100ed91a78 [ELF] Ignore -no-call-graph-profile-sort 2021-12-12 14:27:49 +09:00
Rui Ueyama 733bb6354f Rename variables 2021-12-11 21:40:57 +09:00
Rui Ueyama 10a318d5ba [ELF] Fix -exclude_libs option 2021-12-10 20:45:57 +09:00
Rui Ueyama e394fa73d4 Fix demangler 2021-12-10 16:24:54 +09:00
Rui Ueyama 54d75153d2 Rename variables 2021-12-07 21:55:04 +09:00
Rui Ueyama 84cd674ff1 Revert "[ELF] Rename section piece -> subsection"
This reverts commit 3fc9c6c3ed.
2021-12-07 14:44:32 +09:00
Rui Ueyama 58b74bf961 Revert "[ELF] s/SectionFragment/Subsection/g"
This reverts commit aa46df7735.
2021-12-07 14:44:11 +09:00
Rui Ueyama 381ff9ee48 Merge pull request #134 from Keruspe/strip
allow overriding strip
2021-12-06 22:14:35 +09:00
Marc-Antoine Perennou b6549eb4c9 allow overriding strip
Some packaging systems want to handle the stripping by themselves.
Setting e.g. STRIP=true will result in true being used instead of strip
and thus will skip the stripping part, leaving it to the package manager

Signed-off-by: Marc-Antoine Perennou <[email protected]>
2021-12-06 13:32:21 +01:00
Rui Ueyama 22116629f1 [ELF] Add --start-lib and --end-lib
Fixes https://github.com/rui314/mold/issues/133
2021-12-06 20:09:46 +09:00
Rui Ueyama 0328f37b2a [Mach-O] Add -bundle 2021-12-05 16:52:35 +09:00
Rui Ueyama c5f5e6153f [Mach-O] Add -headerpad_max_install_names 2021-12-05 15:53:41 +09:00
Rui Ueyama 6fae1d8555 [Mach-O] Report an error on -search_dylibs_first 2021-12-05 15:40:36 +09:00
Rui Ueyama f6609474b6 Refactor 2021-12-05 13:37:14 +09:00
Rui Ueyama 83721464ed [Mach-O] Remove unnecessary #includes 2021-12-04 21:30:26 +09:00
Rui Ueyama da13b1d3a6 [Mach-O] Fix an assertion failure 2021-12-04 20:21:48 +09:00
Rui Ueyama efefba2bf3 [Mach-O] Port mold/macOS to ARM64 2021-12-04 20:07:36 +09:00
Rui Ueyama cc35d725f2 [Mach-O] Port TBD reader to ARM64 2021-12-04 19:59:42 +09:00
Rui Ueyama a3e0e9f197 wip 2021-12-04 16:41:51 +09:00
Rui Ueyama a3fd0ea5a4 wip 2021-12-04 16:36:18 +09:00
Rui Ueyama 6a3ddf1ab4 wip 2021-12-04 16:31:15 +09:00
Rui Ueyama a7cae9b2a4 wip 2021-12-04 15:41:53 +09:00
Rui Ueyama d8e25b9753 wip 2021-12-04 13:17:47 +09:00
Rui Ueyama a415accbc3 wip 2021-12-04 12:55:21 +09:00
Rui Ueyama d6a9005b14 wip 2021-12-04 12:10:18 +09:00
Rui Ueyama bf7366438b wip 2021-12-03 23:06:07 +09:00
Rui Ueyama 412e3aa136 wip 2021-12-03 18:20:25 +09:00
Rui Ueyama d23183ed31 wip 2021-12-03 16:06:47 +09:00
Rui Ueyama fa8d5d97d0 wip 2021-12-03 15:47:42 +09:00
Rui Ueyama 16921c78ea [Mach-O] Add ARM64 stub 2021-12-02 11:51:42 +09:00
Rui Ueyama 790703b5fa Fix build breakage 2021-12-01 22:15:47 +09:00
Rui Ueyama 4878b8bca2 [Mach-O] Make everything template 2021-12-01 22:02:33 +09:00
Rui Ueyama bdb01432df [Mach-O] Link against libraries in /opt/homebrew 2021-12-01 20:43:17 +09:00
Rui Ueyama 6dfede035b Fix build breakage 2021-12-01 19:45:49 +09:00
Rui Ueyama 4bd92c0d46 [Mach-O] Do not emit PAGEZERO segment for dylibs 2021-12-01 15:14:38 +09:00
Rui Ueyama 57d7a2e8dd [Mach-O] Refactor 2021-12-01 13:50:34 +09:00
Rui Ueyama 8ceaf53728 [Mach-O] Add -needed_framework 2021-12-01 13:45:25 +09:00
Rui Ueyama ee05f3a65f [Mach-O] Add -dead_strip_dylibs 2021-11-30 19:20:29 +09:00
Rui Ueyama 7156dd265f [Mach-O] Add -needed-l 2021-11-30 18:05:28 +09:00
Rui Ueyama 9e7e41cfac [Mach-O] Fix -L 2021-11-30 15:23:00 +09:00
Rui Ueyama 4998a130e5 [Mach-O] Support -dylib 2021-11-29 18:12:48 +09:00
Rui Ueyama 39fbd5b29c Refactor 2021-11-28 10:25:25 +09:00
Rui Ueyama e99727f87f [Mach-O] Add -Z 2021-11-28 10:05:23 +09:00
Rui Ueyama 0ee3986da6 [ELF] Fix image-base.sh test on AArch64 2021-11-27 13:15:02 +00:00
Rui Ueyama 1219d3250c [ELF] Fix a test for AArch64 2021-11-27 21:03:47 +09:00
Rui Ueyama 8ae43210e7 [Mach-O] Add -framework and -F 2021-11-27 20:03:12 +09:00
Rui Ueyama 5deb697ec4 [Mach-O] wip 2021-11-27 13:18:10 +09:00
Rui Ueyama a4a21b6a92 [Mach-O] wip 2021-11-27 13:18:10 +09:00
Rui Ueyama c3c832fffa [ELF] Support R_AARCH64_ADR_PREL_LO21 2021-11-27 12:18:36 +09:00
Rui Ueyama b32f4592fa [ELF] Support R_AARCH64_CONDBR19 2021-11-27 12:18:36 +09:00
Rui Ueyama c8f1766b71 [ELF] Support R_AARCH64_PREL{16,64} 2021-11-27 12:18:36 +09:00
Rui Ueyama 411706d147 [ELF] Accept any argv[0] other than ld64 or ld64.mold
ld is often named as x86_64-pc-linux-gnu-ld or some other system-
dependent name. We shouldn't report an error for an unknown argv[0].
2021-11-27 12:18:36 +09:00
Rui Ueyama 72cea9a0bf [Mach-O] Improve -filelist 2021-11-21 22:18:55 +09:00
Rui Ueyama 5804d6b4b2 [Mach-O] Add -ObjC 2021-11-21 20:26:41 +09:00
Rui Ueyama af4a4af038 [Mach-O] Ignore --color-diagnostics 2021-11-21 15:32:36 +09:00
Rui Ueyama 602ec8d0c0 [Mach-O] Ignore -fatal_warnings 2021-11-21 15:31:23 +09:00
Rui Ueyama efb6e6729a [Mach-O] Handle response files correctly 2021-11-21 14:53:50 +09:00
Rui Ueyama 6087782660 [Mach-O] Add -filepath 2021-11-20 19:26:29 +09:00
Rui Ueyama cee6412e3d [Mach-O] Add -rpath 2021-11-20 19:01:18 +09:00
Rui Ueyama 1e4262753a [Mach-O] wip 2021-11-19 21:42:33 +09:00
Rui Ueyama 39676ef7a9 Define BINDIR, LIBDIR and MANDIR
So that it is easy to specify alternative install target paths.
2021-11-19 17:18:15 +09:00
Rui Ueyama 8b41bb923a [ELF] Fix canonical PLT entry
Previously, an executable linked by mold could fall into an infinite
loop between .plt.got and .got entries. This commit fixes the issue
by creating .plt entries instead of .plt.got.

Fixes https://github.com/rui314/mold/issues/129
2021-11-18 20:53:15 +09:00
Rui Ueyama e2e344ea7a [Mach-O] Fix relocation addends 2021-11-16 19:28:30 +09:00
Rui Ueyama 94889d7a02 [Mach-O] Support LC_DYLD_EXPORTS_TRIE 2021-11-16 12:51:56 +09:00
Rui Ueyama cd404f72cd [Mach-O] wip 2021-11-16 11:44:47 +09:00
Rui Ueyama c13da97044 [Mach-O] wip 2021-11-15 14:50:16 +09:00
Rui Ueyama 0ac8821015 [Mach-O] wip 2021-11-15 11:27:30 +09:00
Rui Ueyama 40b0e08d35 [Mach-O] wip 2021-11-14 21:53:38 +09:00
Rui Ueyama 15215b1f71 [Mach-O] wip 2021-11-14 15:05:22 +09:00
Rui Ueyama 18cfe1f4ca [Mach-O] Add -pagezero_size option 2021-11-14 14:23:44 +09:00
Rui Ueyama 750d6ca3ce [Mach-O] wip 2021-11-14 14:08:50 +09:00
Rui Ueyama 06bc828a9b [Mach-O] wip 2021-11-14 13:30:55 +09:00
Rui Ueyama 91b9f925d6 [Mach-O] wip 2021-11-13 17:45:04 +09:00
Rui Ueyama 433666a470 [Mach-O] Implement -dead_strip 2021-11-13 15:21:52 +09:00
Rui Ueyama d0138a404d [ELF] Rename variables 2021-11-12 19:29:12 +09:00
Rui Ueyama 75c516f3cd [Mach-O] wip 2021-11-12 19:28:55 +09:00
Rui Ueyama fcd592e53d [Mach-O] Add -e option 2021-11-12 19:10:00 +09:00
Rui Ueyama 4a1ec53c24 [Mach-O] wip 2021-11-12 19:10:00 +09:00
Rui Ueyama 2b807ddf83 [Mach-O] wip 2021-11-12 09:56:28 +09:00
Rui Ueyama 66b95013f9 [Mach-O] wip 2021-11-11 16:14:13 +09:00
Rui Ueyama a5e3c681bc [Mach-O] wip 2021-11-11 16:06:21 +09:00
Rui Ueyama 2d5cb51fe2 [Mach-O] wip 2021-11-11 14:53:28 +09:00
Rui Ueyama 03b9e1b914 [Mach-O] wip 2021-11-11 14:41:57 +09:00
Rui Ueyama 132dd7a7f2 [Mach-O] wip 2021-11-11 14:11:55 +09:00
Rui Ueyama 25a902c45a [Mach-O] wip 2021-11-11 13:36:23 +09:00
Rui Ueyama be6824b14d [Mach-O] wip 2021-11-10 22:20:10 +09:00
Rui Ueyama 026f7b9abc [Mach-O] wip 2021-11-10 22:11:54 +09:00
Rui Ueyama bec8166a77 [Mach-O] wip 2021-11-10 20:54:27 +09:00
Rui Ueyama 9f6db83054 [Mach-O] wip 2021-11-10 15:29:41 +09:00
Rui Ueyama 87c4c1df15 [Mach-O] Relax GOT/TLV load instructions 2021-11-09 19:09:07 +09:00
Rui Ueyama 92f56aa73f [Mach-O] Support thread-local variables 2021-11-09 16:23:43 +09:00
Rui Ueyama 3a24532d2a [Mach-O] wip 2021-11-08 20:36:27 +09:00
Rui Ueyama cd73b40635 [Mach-O] wip 2021-11-08 00:17:36 +09:00
Rui Ueyama 72587e643f [Mach-O] wip 2021-11-07 23:57:43 +09:00
Rui Ueyama ea6d310576 [Mach-O] wip 2021-11-07 20:01:36 +09:00
Rui Ueyama 0cb34615d9 [Mach-O] wip 2021-11-07 19:12:58 +09:00
Rui Ueyama c89448d17c [Mach-O] Fix local symbol handling
With this commit, `git show` seems to be working fine on my Mac.
2021-11-07 16:32:23 +09:00
Rui Ueyama 0f0f747135 [Mach-O] Fix .bss sections 2021-11-07 12:18:55 +09:00
Rui Ueyama fd5d95f9d0 [Mach-O] wip 2021-11-07 11:19:58 +09:00
Rui Ueyama a9fea29aaf [Mach-O] wip 2021-11-06 21:29:11 +09:00
Rui Ueyama 6d709de396 [Mach-O] wip 2021-11-06 16:22:55 +09:00
Rui Ueyama b78a62c7ab [Mach-O] wip 2021-11-06 15:50:27 +09:00
Rui Ueyama d88b4c1e86 [Mach-O] wip 2021-11-06 14:56:51 +09:00
Rui Ueyama a4bdd81966 [Mach-O] wip 2021-11-06 14:29:02 +09:00
Rui Ueyama 97ed33758e [Mach-O] wip 2021-11-06 13:05:34 +09:00
Rui Ueyama f7b2558b0e [Mach-O] wip 2021-11-06 11:04:27 +09:00
Rui Ueyama f232e857f0 [Mach-O] wip 2021-11-06 10:53:44 +09:00
Rui Ueyama 977ebe5256 [Mach-O] wip 2021-11-05 22:02:11 +09:00
Rui Ueyama cbc990d459 [Mach-O] wip 2021-11-05 18:40:59 +09:00
Rui Ueyama cd47a3b2be [Mach-O] wip 2021-11-05 16:00:56 +09:00
Rui Ueyama f5dbb8112c Format 2021-11-05 14:41:26 +09:00
Rui Ueyama 247655179f [Mach-O] Add -map option 2021-11-05 14:23:28 +09:00
Rui Ueyama 3f2124e612 [Mach-O] wip 2021-11-05 12:37:26 +09:00
Rui Ueyama 27ed76ddac [Mach-O] wip 2021-11-04 23:12:46 +09:00
Rui Ueyama f90a55cec9 [ELF] Ignore invalid SHF_MERGE sections
DMD, a compiler for the D language, generates a broken .rodata.str1.1
section with sh_entsize = 0. The section also contains out-of-bound
relocations. This patch handles such broken mergeable sections as
regular sections to workaround the issues.

Fixes https://github.com/rui314/mold/issues/126
2021-11-04 22:42:33 +09:00
Rui Ueyama 06f377806b Fix formatting 2021-11-04 22:14:27 +09:00
Rui Ueyama 32f30c304e [ELF] Fix -thread-count option 2021-11-04 22:13:51 +09:00
Rui Ueyama 3d02a3b20b [Mach-O] wip 2021-11-03 22:07:50 +09:00
Rui Ueyama 0f2dbbc09d [Mach-O] Emit __data_in_code 2021-11-03 21:10:46 +09:00
Rui Ueyama bfa50ac015 [Mach-O] Handle Universal binaries 2021-11-03 16:38:29 +09:00
Rui Ueyama 23bbb0fcd2 [Mach-O] Parse .dylib files 2021-11-03 15:26:31 +09:00
Rui Ueyama 32cdec42cb [Mach-O] wip 2021-11-03 13:58:36 +09:00
Rui Ueyama ac8597dee0 [Mach-O] wip 2021-11-03 13:48:18 +09:00
Rui Ueyama 9cef61debe [Mach-O] Emit __code_signature section 2021-11-02 16:11:29 +09:00
Rui Ueyama 2524c6448e [Mach-O] wip 2021-11-02 15:43:18 +09:00
Rui Ueyama c5f3a92f51 [Mach-O] wip 2021-11-02 15:23:06 +09:00
Rui Ueyama 00e45a4ba1 [Mach-O] wip 2021-11-02 15:08:02 +09:00
Rui Ueyama 51779b2a35 [Mach-O] wip 2021-11-02 14:30:32 +09:00
Rui Ueyama 76a92b1a10 [Mach-O] wip 2021-11-02 14:08:33 +09:00
Rui Ueyama 2af5ec006e [Mach-O] wip 2021-11-02 13:45:32 +09:00
Rui Ueyama 581c445062 [Mach-O] wip 2021-11-02 13:03:48 +09:00
Rui Ueyama 28984b5362 [Mach-O] wip 2021-11-02 10:24:30 +09:00
Rui Ueyama 4de3bb9a78 [Mach-O] wip 2021-11-02 10:07:49 +09:00
Rui Ueyama 95f8035ec0 [Mach-O] wip 2021-11-02 10:07:26 +09:00
Rui Ueyama fe9756c9b4 [Mach-O] Support throw-catch 2021-11-01 22:30:27 +09:00
Rui Ueyama 9cc0a2e5be [Mach-O] wip 2021-11-01 22:19:07 +09:00
Rui Ueyama bfec2435da [Mach-O] wip 2021-11-01 21:06:33 +09:00
Rui Ueyama fa5688737c [Mach-O] wip 2021-11-01 20:52:08 +09:00
Rui Ueyama 1f5400071e [Mach-O] wip 2021-11-01 17:24:27 +09:00
Rui Ueyama fb31927a33 [Mach-O] wip 2021-11-01 17:06:27 +09:00
Rui Ueyama 9820a60374 [Mach-O] wip 2021-11-01 16:00:38 +09:00
Rui Ueyama 3c297c2254 [Mach-O] wip 2021-11-01 15:13:28 +09:00
Rui Ueyama ec5c85dcc7 [Mach-O] wip 2021-10-31 16:28:03 +09:00
Rui Ueyama 1ab9f39f70 [Mach-O] wip 2021-10-31 16:12:39 +09:00
Rui Ueyama 01fb761d98 [Mach-O] simplify 2021-10-31 15:46:23 +09:00
Rui Ueyama 3b47e90274 [Mach-O] wip 2021-10-30 15:16:55 +09:00
Rui Ueyama 631412844c [Mach-O] wip 2021-10-30 12:48:46 +09:00
Rui Ueyama 0a6d3986cd [Mach-O] Align common symbols properly 2021-10-29 21:34:27 +09:00
Rui Ueyama 4afc68d74d [Mach-O] Handle common symbols 2021-10-29 16:19:05 +09:00
Rui Ueyama 5ec27ff6c8 [Mach-O] wip 2021-10-29 14:10:32 +09:00
Rui Ueyama c227286dd5 [Mach-O] Handle .bss 2021-10-29 13:28:52 +09:00
Rui Ueyama 16d904a198 [Mach-O] wip 2021-10-28 20:35:26 +09:00
Rui Ueyama e1ab83155a [Mach-O] wip 2021-10-28 18:51:54 +09:00
Rui Ueyama 3fba4dabd6 [Mach-O] wip 2021-10-28 18:43:38 +09:00
Rui Ueyama d3ef7e754d [Mach-O] wip 2021-10-28 18:43:38 +09:00
Rui Ueyama 4c60f05ca5 [Mach-O] wip 2021-10-28 18:43:38 +09:00
Rui Ueyama e1909a541f [Mach-O] wip 2021-10-28 18:43:38 +09:00
Rui Ueyama b1440067cc Fix indentation 2021-10-28 15:50:31 +09:00
Rui Ueyama 58f6502a1c [Mach-O] Add static archive file support 2021-10-28 14:37:18 +09:00
Rui Ueyama 9973e51b92 [Mach-O] wip 2021-10-28 14:37:18 +09:00
Rui Ueyama 30dd7b0230 Merge pull request #125 from Calinou/improve-readme
Add installation instructions for Fedora and improve README
2021-10-28 12:15:22 +09:00
Hugo Locurcio bfc43ef52b Add installation instructions for Fedora and improve README
Fedora dependencies were tested in a fresh Fedora 34 Docker container.

- Use all available CPU threads to speed up compilation.
- Add `sudo make install` step to make the mold binary available
  system-wide.
- List Ubuntu instructions as compatible with Debian 11 (tested
  in a fresh Docker container).
- Reorder the list of dependencies to be consistent between Ubuntu
  and Fedora.
- Don't use a `$` prefix for commands to make copy-pasting easier.
- Fix warnings reported by markdownlint.

Signed-off-by: Hugo Locurcio <[email protected]>
2021-10-28 02:47:40 +02:00
Rui Ueyama 495df9ea00 Change the default install PREFIX from /usr to /usr/local
If not specified, `make install` usually installs binaries to /usr/local.
It's nice to follow that convention.

Fixes https://github.com/rui314/mold/issues/124
2021-10-28 08:06:37 +09:00
Rui Ueyama 4bf1ba34f6 Fix build breakage 2021-10-27 21:53:23 +09:00
Rui Ueyama 7236d14650 [Mach-O] Report errors for remaining undefined symbols 2021-10-27 21:46:57 +09:00
Rui Ueyama 04fcb66580 [Mach-O] wip 2021-10-27 21:24:00 +09:00
Rui Ueyama 6b02b3d9aa [Mach-O] Ignore -no_deduplicate 2021-10-27 21:15:34 +09:00
Rui Ueyama 6a83705a68 [Mach-O] wip 2021-10-27 21:11:02 +09:00
Rui Ueyama 2ec3b16ffd [Mach-O] wip 2021-10-27 21:04:52 +09:00
Rui Ueyama 0bdb744918 [Mach-O] wip 2021-10-27 20:27:18 +09:00
Rui Ueyama cd3726b394 [Mach-O] wip 2021-10-27 20:12:54 +09:00
Rui Ueyama 6cdfd45134 [Mach-O] Create GOT entries for internal references 2021-10-27 15:57:31 +09:00
Rui Ueyama 5549a005f1 [Mach-O] Create GOT entries for external references 2021-10-26 17:44:57 +09:00
Rui Ueyama 5837fe75eb [Mach-O] wip 2021-10-26 16:38:44 +09:00
Rui Ueyama d7fb95fa63 [Mach-O] wip 2021-10-26 15:26:08 +09:00
Rui Ueyama 8471f47abb [Mach-O] wip 2021-10-26 15:24:23 +09:00
Rui Ueyama 48e7616263 [Mach-O] Add the -platform_version option 2021-10-26 15:14:14 +09:00
Rui Ueyama 2589190724 [Mach-O] wip 2021-10-26 15:04:02 +09:00
Rui Ueyama e12055de49 [Mach-O] wip 2021-10-26 14:21:37 +09:00
Rui Ueyama 986516545a [ELF] Do not share OutputFile between ELF and Mach-O 2021-10-26 14:06:11 +09:00
Rui Ueyama ee0ee2375d [Mach-O] wip 2021-10-26 13:53:48 +09:00
Rui Ueyama ed2a7ba1ed [Mach-O] wip 2021-10-26 13:43:04 +09:00
Rui Ueyama 07bf64d9cb [Mach-O] wip 2021-10-26 13:39:41 +09:00
Rui Ueyama d6657a3cb0 [Mach-O] Add the -headerpad option 2021-10-25 20:14:52 +09:00
Rui Ueyama 5817265e91 [Mach-O] Fix ASAN issue 2021-10-25 20:14:32 +09:00
Rui Ueyama a86ad2771a [Mach-O] Use clang as a linker frontend
Now that we can link a "hello world" program on macOS, we can
use clang as a linker frontend.
2021-10-25 19:09:18 +09:00
Rui Ueyama 31e49a08a0 [Mach-O] wip 2021-10-25 15:47:02 +09:00
Rui Ueyama 2e07e62ffd [Mach-O] wip 2021-10-25 15:46:40 +09:00
Rui Ueyama 638bcfe311 [Mach-O] wip 2021-10-25 15:35:09 +09:00
Rui Ueyama 5450debfce [Mach-O] wip 2021-10-25 15:31:03 +09:00
Rui Ueyama 948f4d2605 [Mach-O] wip 2021-10-25 15:05:25 +09:00
Rui Ueyama cb9d159738 [Mach-O] wip 2021-10-25 15:00:45 +09:00
Rui Ueyama 53fe663791 [Mach-O] wip 2021-10-25 01:12:42 +09:00
Rui Ueyama e7f465bf16 [Mach-O] wip 2021-10-24 22:13:06 +09:00
Rui Ueyama 5c8481f1ca [Mach-O] wip 2021-10-24 22:02:02 +09:00
Rui Ueyama 2e92fe2e2a [Mach-O] wip 2021-10-24 22:01:03 +09:00
Rui Ueyama 9d81e7e1a3 [Mach-O] wip 2021-10-24 20:54:26 +09:00
Rui Ueyama 78ff2bb73a [Mach-O] wip 2021-10-24 17:00:53 +09:00
Rui Ueyama 31373b1d6d [Mach-O] wip 2021-10-24 13:34:36 +09:00
Rui Ueyama c56a83c405 [Mach-O] wip 2021-10-24 12:57:54 +09:00
Rui Ueyama 94cda25277 [Mach-O] wip 2021-10-24 00:42:39 +09:00
Rui Ueyama b3528e0aee [Mach-O] wip 2021-10-24 00:27:27 +09:00
Rui Ueyama ecf17abfd3 [Mach-O] wip 2021-10-24 00:18:00 +09:00
Rui Ueyama be147aea80 [Mach-O] wip 2021-10-24 00:12:55 +09:00
Rui Ueyama 07f6e9517d [Mach-O] wip 2021-10-23 23:05:43 +09:00
Rui Ueyama ba8e305fdb [Mach-O] wip 2021-10-23 22:45:17 +09:00
Rui Ueyama 0fbbef7858 [Mach-O] wip 2021-10-23 22:44:32 +09:00
Rui Ueyama 857aef3244 [Mach-O] wip 2021-10-23 22:16:24 +09:00
Rui Ueyama 1f5bc4b3ee [Mach-O] wip 2021-10-23 22:15:03 +09:00
Rui Ueyama ef943d5f9a [Mach-O] wip 2021-10-23 22:12:23 +09:00
Rui Ueyama a70fa4f6b1 [Mach-O] wip 2021-10-23 22:08:34 +09:00
Rui Ueyama 8c8aaf4133 [Mach-O] wip 2021-10-23 21:31:25 +09:00
Rui Ueyama 0b233ee1e6 [Mach-O] wip 2021-10-23 21:14:57 +09:00
Rui Ueyama 1a70f94dcd [Mach-O] wip 2021-10-23 21:10:24 +09:00
Rui Ueyama e66be5010f [Mach-O] wip 2021-10-23 21:09:30 +09:00
Rui Ueyama 7de2e86045 [Mach-O] wip 2021-10-23 21:01:35 +09:00
Rui Ueyama dc22d42e96 [Mach-O] wip 2021-10-23 18:06:21 +09:00
Rui Ueyama bab46b211a [Mach-O] wip 2021-10-23 18:00:10 +09:00
Rui Ueyama 4b6407dfc0 [Mach-O] wip 2021-10-23 17:45:45 +09:00
Rui Ueyama 59139a778d [Mach-O] wip 2021-10-23 17:10:40 +09:00
Rui Ueyama a599ce318e [Mach-O] wip 2021-10-23 17:06:36 +09:00
Rui Ueyama 5bac5f3c60 [Mach-O] wip 2021-10-23 16:54:10 +09:00
Rui Ueyama 794882435f [Mach-O] wip 2021-10-23 16:22:55 +09:00
Rui Ueyama d599ebe3b9 [Mach-O] wip 2021-10-23 11:40:47 +09:00
Rui Ueyama 834d0045ae [Mach-O] wip 2021-10-23 11:18:52 +09:00
Rui Ueyama 0430c806c4 [Mach-O] wip 2021-10-23 10:44:50 +09:00
Rui Ueyama 12ea4e69f1 [Mach-O] wip 2021-10-23 09:12:30 +09:00
Rui Ueyama 43ff20c0a8 [Mach-O] wip 2021-10-22 17:51:19 +09:00
Rui Ueyama d29e75ac7a [Mach-O] wip 2021-10-22 17:16:06 +09:00
Rui Ueyama ee158a031d [Mach-O] wip 2021-10-22 16:34:49 +09:00
Rui Ueyama 95ae34887c [Mach-O] wip 2021-10-22 15:47:07 +09:00
Rui Ueyama 3b357976df [Mach-O] wip 2021-10-22 15:34:16 +09:00
Rui Ueyama 577199ce64 [Mach-O] wip 2021-10-21 23:48:08 +09:00
Rui Ueyama 71a51d5775 [Mach-O] wip 2021-10-21 23:18:15 +09:00
Rui Ueyama 815707e66c [Mach-O] wip 2021-10-21 23:11:29 +09:00
Rui Ueyama e7cc301f17 [Mach-O] Remove dead code 2021-10-21 23:07:49 +09:00
Rui Ueyama 2e32498ef1 [Mach-O] Add the -demangle flag 2021-10-21 22:57:27 +09:00
Rui Ueyama dbf3594146 [Mach-O] wip 2021-10-21 21:11:22 +09:00
Rui Ueyama baec02dc03 [Mach-O] Fix a build issue on macOS 2021-10-19 20:50:47 +09:00
Rui Ueyama 7769a6d2bc [Mach-O] wip 2021-10-19 20:48:47 +09:00
Rui Ueyama 951eca1680 Fix build issue on Linux
I don't know if it is a bug in Clang 10, but the new code couldn't
compile on Linux because `YamlNode` was an incomplete type.
I changed the type from `unordered_map` to `map` to workaround the issue.
2021-10-19 20:46:05 +09:00
Rui Ueyama e28838031c [ELF] Allow the current directory to have a space in it
Fixes https://github.com/rui314/mold/issues/122
2021-10-19 20:39:48 +09:00
Rui Ueyama 5d910222bf [Mach-O] wip 2021-10-19 20:30:53 +09:00
Rui Ueyama 86255ca6bb [Mach-O] wip 2021-10-19 15:33:37 +09:00
Rui Ueyama d200431602 [Mach-O] wip 2021-10-19 14:49:18 +09:00
Rui Ueyama 6f2a13544b [Mach-O] wip 2021-10-18 21:39:52 +09:00
Rui Ueyama 761a4d953b [Mach-O] wip 2021-10-18 10:55:24 +09:00
Rui Ueyama 6d23937bc7 [Mach-O] wip 2021-10-18 09:33:01 +09:00
Rui Ueyama b7993a5f74 [Mach-O] wip 2021-10-17 20:46:17 +09:00
Rui Ueyama 88e714db09 [Mach-O] wip 2021-10-17 20:42:16 +09:00
Rui Ueyama b66f6e0e61 [Mach-O] wip 2021-10-17 20:31:45 +09:00
Rui Ueyama 9605844ccf [Mach-O] wip 2021-10-17 19:52:43 +09:00
Rui Ueyama 45406823c6 [Mach-O] wip 2021-10-17 18:42:23 +09:00
Rui Ueyama 465ff1e238 [Mach-O] wip 2021-10-17 18:40:15 +09:00
Rui Ueyama 43a1742460 [Mach-O] wip 2021-10-17 18:09:03 +09:00
Rui Ueyama 075ac54cd0 [Mach-O] wip 2021-10-17 16:56:00 +09:00
Rui Ueyama 1ec2e78306 [Mach-O] wip 2021-10-16 20:29:22 +09:00
Rui Ueyama 1e39d9f8c8 [Mach-O] wip 2021-10-16 20:23:33 +09:00
Rui Ueyama 42258f0225 [Mach-O] wip 2021-10-16 19:59:21 +09:00
Rui Ueyama 983cf8f203 [Mach-O] wip 2021-10-15 19:14:28 +09:00
Rui Ueyama 63b4e583dc [Mach-O] wip 2021-10-15 17:07:58 +09:00
Rui Ueyama 0393fc2fda [Mach-O] wip 2021-10-15 16:39:03 +09:00
Rui Ueyama 67f8782b53 [Mach-O] wip 2021-10-15 16:34:29 +09:00
Rui Ueyama fa10e60189 [Mach-O] wip 2021-10-15 15:23:00 +09:00
Rui Ueyama e218692672 [Mach-O] wip 2021-10-14 20:54:28 +09:00
Rui Ueyama 4fee9df38d [Mach-O] wip 2021-10-14 20:20:38 +09:00
Rui Ueyama de34da86a0 [Mach-O] wip 2021-10-14 17:54:35 +09:00
Rui Ueyama 97ab328bd7 [Mach-O] wip 2021-10-14 16:43:59 +09:00
Rui Ueyama 4e30c31e17 [Mach-O] wip 2021-10-14 15:05:55 +09:00
Rui Ueyama 1e63f6fbf6 [Mach-O] wip 2021-10-13 17:30:24 +09:00
Rui Ueyama c3e3cfaf44 [Mach-O] wip 2021-10-13 16:50:31 +09:00
Rui Ueyama 0426f3d1de [Mach-O] wip 2021-10-13 16:49:35 +09:00
Rui Ueyama 31e9cea7d2 [Mach-O] wip 2021-10-13 16:30:31 +09:00
Rui Ueyama 06404b96ba [Mach-O] wip 2021-10-13 16:04:22 +09:00
Rui Ueyama 964702c385 [Mach-O] wip 2021-10-13 16:01:56 +09:00
Rui Ueyama 3faa340895 [Mach-O] wip 2021-10-13 14:29:28 +09:00
Rui Ueyama 369c19fdf6 [Mach-O] wip 2021-10-13 09:46:52 +09:00
Rui Ueyama 4ab5e09974 [Mach-O] wip 2021-10-13 09:34:54 +09:00
Rui Ueyama f8ff631605 [Mach-O] wip 2021-10-13 07:50:36 +09:00
Rui Ueyama 2fcbb582ed [Mach-O] wip 2021-10-12 23:04:08 +09:00
Rui Ueyama f382337eb5 [Mach-O] wip 2021-10-12 22:57:00 +09:00
Rui Ueyama f64446c06f [Mach-O] wip 2021-10-12 22:30:49 +09:00
Rui Ueyama d8d733d211 [Mach-O] wip 2021-10-12 18:58:51 +09:00
Rui Ueyama 81581a475d [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama 8124a07e07 [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama d72c1bf62c [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama 34485cc1ec [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama 8097c09c85 [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama a89c7e528f [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama 8639fc8a9b [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama 031de0df32 [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama 43f2e46f34 [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama bb67c78df8 [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama a0a162a48c [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama d176ed8fbb [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama 96b07d875d [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama c1c8270e45 [Mach-O] wip 2021-10-12 18:33:41 +09:00
Rui Ueyama e6c614d69c [ELF] Remove extra path separators 2021-10-12 18:32:20 +09:00
Rui Ueyama ffb8e03c97 [ELF] Make .rela.dyn and .rela.plt contiguous in an output file
glibc 2.22 or prior have a bug that the dynamic linker (ld-linux.so.2)
crashes on `dlopen` if .rela.dyn and .rela.plt are not contiguous in
the `dlopen`-ed file and RTLD_NOW is specified. That bug was fixed in
https://github.com/gentoo/glibc/commit/fa19d5c48a6b36518ce68412e3bdde6bfa8aa4a6.

glibc 2.23 which includes the fix for the bug was released on 2016-02-18.
So, unless you are using glibc that is more than 5 years old, mold-
generated shared object files work fine with `dlopen`. But it is easy
to make the sections contiguous in file, so I'll commit this change as a
workaround for the bug.

Fixes https://github.com/rui314/mold/issues/120
2021-10-12 17:10:32 +09:00
Rui Ueyama 06c237838c [ELF] Fix --help message
Fixes https://github.com/rui314/mold/issues/119
2021-10-08 23:43:03 +09:00
Rui Ueyama e942f5e28e Fix build failure 2021-10-08 20:38:30 +09:00
Rui Ueyama 038f42a579 [Mach-O] wip 2021-10-08 20:24:39 +09:00
Rui Ueyama 69ce692671 [Mach-O] wip 2021-10-08 18:39:48 +09:00
Rui Ueyama d76c6966b0 [Mach-O] wip 2021-10-08 16:55:33 +09:00
Rui Ueyama f7d3fda88a [Mach-O] wip 2021-10-08 16:52:42 +09:00
Rui Ueyama 45fdfd4e87 Disable C++ exceptions
mold itself doesn't use C++ exceptions, so we can disable C++ exceptions.
Passing `-fno-exceptions` to g++/clang++ makes them to produce smaller code.
2021-10-08 15:04:16 +09:00
Rui Ueyama 8ecb117997 [Mach-O] wip 2021-10-07 22:05:49 +09:00
Rui Ueyama 0b5b5dbcf5 [Mach-O] wip 2021-10-07 20:27:53 +09:00
Rui Ueyama 071c74acb3 [Mach-O] wip 2021-10-07 20:01:46 +09:00
Rui Ueyama 86c8b77dd6 [Mach-O] wip 2021-10-07 19:17:18 +09:00
Rui Ueyama 91ec5ae972 [Mach-O] wip 2021-10-07 18:12:38 +09:00
Rui Ueyama 14cadf44ac [Mach-O] wip 2021-10-07 18:01:00 +09:00
Rui Ueyama a414c03bf3 [Mach-O] wip 2021-10-07 17:52:17 +09:00
Rui Ueyama a8b2f521f7 [ELF] Create .rodata.cst and .rodata.str sections
Previously, mold created two or more ".rodata" sections if input files
contain regular .rodata sections as well as .rodata.str* or .rodata.cst*
sections. This is because .rodata.str* and .rodata.cst* contains mergeable
section contents (i.e. string constants and constant pools, respectively),
and they are handled in a special way.

In ELF, section names are not significant, so nothing is wrong with
creating multiple .rodata. However, valgrind unfortunately doesn't like
such executable.

With this change, mold keeps ".rodata.str" and ".rodata.cst" prefixes
to avoid creating multiple ".rodata" sections.

Fixes https://github.com/rui314/mold/issues/118
2021-10-07 13:55:46 +09:00
Rui Ueyama d71c33b3e8 [ELF] Refactor 2021-10-07 12:40:55 +09:00
Rui Ueyama 301d45b2fa Fix a test 2021-10-06 22:04:25 +09:00
Rui Ueyama f49265c788 [Mach-O] Take stub address into symbol address computation 2021-10-06 20:29:08 +09:00
Rui Ueyama 510ec4b003 [Mach-O] Copy __cstring section from input to output 2021-10-06 17:24:51 +09:00
Rui Ueyama 45788bd8ad [Mach-O] wip 2021-10-06 17:16:02 +09:00
Rui Ueyama e2d7cd7b03 [Mach-O] wip 2021-10-06 16:53:20 +09:00
Rui Ueyama 627fbc3ec0 [Mach-O] wip 2021-10-06 16:41:18 +09:00
Rui Ueyama 3c513a31cc [Mach-O] Apply relocations 2021-10-06 14:33:01 +09:00
Rui Ueyama 7fc365a657 [Mach-O] wip 2021-10-06 13:43:41 +09:00
Rui Ueyama dc9cfc4f41 [Mach-O] wip 2021-10-05 22:17:14 +09:00
Rui Ueyama e36cf0271f [Mach-O] wip 2021-10-05 20:14:28 +09:00
Rui Ueyama 46c20da35e [Mach-O] wip 2021-10-05 20:02:41 +09:00
Rui Ueyama 27e52a430b [Mach-O] wip 2021-10-05 19:39:39 +09:00
Rui Ueyama 46d3e12946 [Mach-O] wip 2021-10-05 19:26:15 +09:00
Rui Ueyama a861f192ec [Mach-O] wip 2021-10-05 19:17:57 +09:00
Rui Ueyama bd259a795f [Mach-O] wip 2021-10-05 16:00:45 +09:00
Rui Ueyama 2da5057d58 [Mach-O] wip 2021-10-05 12:59:22 +09:00
Rui Ueyama d111361a1f [Mach-O] wip 2021-10-05 12:59:18 +09:00
Rui Ueyama 46868805e2 [Mach-O] wip 2021-10-05 11:40:33 +09:00
Rui Ueyama e1e603f633 [Mach-O] wip 2021-10-05 11:40:33 +09:00
Rui Ueyama 0a130212cb [Mach-O] wip 2021-10-05 11:40:33 +09:00
Rui Ueyama 7dd9a38b3b [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama e7f3e94f30 [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama 2dc8ac0bdd [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama 4932daadba [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama 74244c3c9c [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama 8965de697b [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama 89d5e9e3b4 [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama fd62bbfacb [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama e6edf2002d [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama f68e301c30 [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama 745792b34d [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama 7933e44493 [Mach-O] wip 2021-10-05 11:40:32 +09:00
Rui Ueyama 840d19bb5e [Mach-O] Add -o option 2021-10-05 11:40:32 +09:00
Rui Ueyama ca9a6d0843 Move code from elf/output-file.cc to output-file.h 2021-10-05 11:37:30 +09:00
Rui Ueyama de1db98cde Split mold.h into multiple headers 2021-10-05 11:24:48 +09:00
Rui Ueyama 7b251e3d7b [ELF] Improve .init and .fini detection logic 2021-10-05 11:05:36 +09:00
Rui Ueyama 30b45d2263 [ELF] Rename Symbol<E>::intern -> intern 2021-10-05 08:31:28 +09:00
Rui Ueyama 3fc9c6c3ed [ELF] Rename section piece -> subsection 2021-10-05 08:22:38 +09:00
Rui Ueyama 97ff050396 Use s suffix 2021-10-04 15:41:49 +09:00
Rui Ueyama e89af3ee94 Use sv suffix 2021-10-04 15:28:02 +09:00
Rui Ueyama 77069156cb [ELF] Rename OutputChunk Chunk 2021-10-04 10:16:24 +09:00
Rui Ueyama 0c42930d55 [Mach-O] Add -o option 2021-10-03 18:17:59 +09:00
Rui Ueyama 928c39937a Refactor 2021-10-03 16:26:31 +09:00
Rui Ueyama 001cf042d9 Refactor 2021-10-03 16:01:59 +09:00
Rui Ueyama fcd10254b7 Rename variables 2021-10-03 16:01:59 +09:00
Rui Ueyama bc3f556055 Make get_file_type() to recognize Mach-O file types 2021-10-03 13:44:45 +09:00
Rui Ueyama 0426ec6d2f [Mach-O] Simplify 2021-10-03 12:01:17 +09:00
Rui Ueyama 19b1b0252c [Mach-O] Dump export table entries 2021-10-02 17:48:52 +09:00
Rui Ueyama ebc8a68cb3 [ELF] Rename variables 2021-10-02 14:12:42 +09:00
Rui Ueyama 4a65748851 [Mach-O] Refactor 2021-10-02 14:02:42 +09:00
Rui Ueyama 56b7c42fae Refactor 2021-10-01 00:35:42 +09:00
Rui Ueyama 52c7793326 Move archive-file.cc out of elf directory 2021-09-30 23:13:27 +09:00
Rui Ueyama 7c9205f68d Move memory-mapped-file.cc out of elf directory
So that we can use the class from mold/mach-o.
2021-09-30 23:13:22 +09:00
Rui Ueyama ffd34678e0 [ELF] Fix "mold-wrapper.so is not found" error 2021-09-30 15:01:55 +09:00
Rui Ueyama a6c75e969d [ELF] Refactor 2021-09-30 00:10:56 +09:00
Rui Ueyama a2c9d0ad4d [ELF] Reduce the size of Subsection struct 2021-09-29 16:30:04 +09:00
Rui Ueyama aa46df7735 [ELF] s/SectionFragment/Subsection/g 2021-09-29 16:18:41 +09:00
Rui Ueyama d123009460 [Mach-O] Add a test 2021-09-28 21:55:37 +09:00
Rui Ueyama cc5cd031ab [Mach-O] Use sysctl -n hw.logicalcpu instead of nproc no macOS
`nproc` is not a valid command on macOS.
2021-09-28 21:20:05 +09:00
Rui Ueyama e3d47ddafa [Mach-O] Do not try to use ccache for brew
The brew command, by design, will never use ccache.
2021-09-28 21:15:00 +09:00
Rui Ueyama 4f69f53cae [Mach-O] Make GitHub actions macOS rules to use ccache 2021-09-28 21:12:58 +09:00
Rui Ueyama 99fba5995a [Mach-O] Make GitHub CI to run make test on macOS 2021-09-28 18:49:11 +09:00
Rui Ueyama 9d805f5d26 [Mach-O] Make make test to work on macOS 2021-09-28 18:47:24 +09:00
Rui Ueyama 5e3f526bc1 Refactor 2021-09-28 16:40:07 +09:00
Rui Ueyama c74b2197f9 Refactor 2021-09-28 16:24:41 +09:00
Rui Ueyama dad3306d1f [Mach-O] Set up macOS github actions 2021-09-28 15:44:25 +09:00
Rui Ueyama 2844f1f573 [ELF] Refactor 2021-09-28 13:23:58 +09:00
Rui Ueyama bc2bd0e26a [ELF] Refactor 2021-09-27 23:01:51 +09:00
Rui Ueyama 3d95b0e1d4 [ELF] Refactor 2021-09-27 21:44:39 +09:00
Rui Ueyama 5bd9edeeed Bump mold version to 0.9.6 2021-09-27 20:38:18 +09:00
Rui Ueyama c40b9aea5f [Mach-O] Generalize perf.cc so that we can use the feature in mold/mach-o 2021-09-27 18:14:56 +09:00
Rui Ueyama 1fb421d82c Fix a bug for a very long pathname 2021-09-27 17:38:55 +09:00
Rui Ueyama 541399911d [ELF] Refactor 2021-09-26 19:55:46 +09:00
Rui Ueyama 67990f775b [ELF] Refactor 2021-09-26 18:50:03 +09:00
Rui Ueyama 58f1fbadc3 [ELF] Refactor 2021-09-26 12:43:51 +09:00
Rui Ueyama 6536663130 [ELF] Fix comment 2021-09-26 12:43:39 +09:00
Rui Ueyama 774fc0f340 [ELF] Remove an extra space in an error message 2021-09-26 12:43:39 +09:00
Rui Ueyama a5d02b18c7 Refactor 2021-09-25 21:47:09 +09:00
Rui Ueyama e4861bb3e1 [ELF] Fix "recompile with -fPIC" error
mold used to emit a spurious "recompile with -fPIC" error when creating
a position-dependent executable. This is because we alwyas create
dynamic relocations for importe symbols. If the relocated place is
read-only, we can't apply a dynamic relocation, and we printed out the
error message.

This patch fixes the issue by creating copy relocations and PLTs for
such symbols.

Fixes https://github.com/rui314/mold/issues/116
2021-09-25 18:13:26 +09:00
Rui Ueyama d11ecda08c [Mach-O] wip 2021-09-23 14:38:37 +09:00
Rui Ueyama 23ab97351f Update README 2021-09-23 14:32:22 +09:00
Rui Ueyama 326831837d [Mach-O] wip 2021-09-23 14:31:04 +09:00
Rui Ueyama bdd458241b [Mach-O] wip 2021-09-21 21:48:32 +09:00
Rui Ueyama fc2ee0d0b0 [Mach-O] wip 2021-09-21 19:32:48 +09:00
Rui Ueyama c002284cd8 [Mach-O] wip 2021-09-21 19:11:34 +09:00
Rui Ueyama 56fb6ba443 [Mach-O] wip 2021-09-21 18:56:31 +09:00
Rui Ueyama c66bd302bd [Mach-O] Programmatically construct __stubs, __stub_helper and __la_symbol_ptr 2021-09-21 17:19:44 +09:00
Rui Ueyama 17c67418f6 [Mach-O] wip 2021-09-21 17:16:42 +09:00
Rui Ueyama 935194d5ae [Mach-O] wip 2021-09-21 15:57:23 +09:00
Rui Ueyama 02ce171aba [Mach-O] wip 2021-09-21 14:53:38 +09:00
Rui Ueyama a52179d090 [Mach-O] wip 2021-09-21 14:51:29 +09:00
Rui Ueyama 1390a7f622 [Mach-O] wip 2021-09-21 14:47:09 +09:00
Rui Ueyama a05d073018 [Mach-O] wip 2021-09-21 14:32:54 +09:00
Rui Ueyama c0daab6075 [Mach-O] wip 2021-09-21 12:55:09 +09:00
Rui Ueyama 18fb0d5d3f [Mach-O] Programmatically construct symtab 2021-09-20 22:04:41 +09:00
Rui Ueyama 5f5139ad6c [Mach-O] wip 2021-09-20 19:41:03 +09:00
Rui Ueyama 046324b64c Simplify 2021-09-20 17:59:37 +09:00
Rui Ueyama 48c16571d1 Simplify 2021-09-20 17:27:23 +09:00
Rui Ueyama 1127061d54 Simplify 2021-09-20 16:36:50 +09:00
Rui Ueyama fa2f54b358 Fix indentation 2021-09-20 16:31:33 +09:00
Rui Ueyama afbc46e135 Simplify 2021-09-20 12:39:34 +09:00
Rui Ueyama e3cebd981e [Mach-O] wip 2021-09-19 20:28:45 +09:00
Rui Ueyama 9881a61cb3 [Mach-O] Programmatically construct export trie 2021-09-19 20:18:34 +09:00
Rui Ueyama 43e513d7af [Mach-O] wip 2021-09-19 19:17:12 +09:00
Rui Ueyama 08f049434f [Mach-O] wip 2021-09-19 16:21:08 +09:00
Rui Ueyama a71f70708c [Mach-O] wip 2021-09-19 14:35:45 +09:00
Rui Ueyama 7f671f5390 [Mach-O] Programmatically construct lazy bind instructions 2021-09-19 14:13:01 +09:00
Rui Ueyama d84b813cb3 [Mach-O] Programmatically construct bind instructions 2021-09-19 13:37:56 +09:00
Rui Ueyama fabd425f67 [Mach-O] Simplify 2021-09-19 12:13:54 +09:00
Rui Ueyama fbf3a1a24e [Mach-O] Programmatically construct rebase instructions 2021-09-19 12:01:03 +09:00
Rui Ueyama 76874d84ba [Mach-O] Output "hello world" program
With this change, ld64.mold is able to emit a "hello world" program.

  $ ./ld64.mold -out hello
  $ ./hello
  Hello world
2021-09-18 20:57:05 +09:00
Rui Ueyama ded5d5e6a1 [Mach-O] wip 2021-09-18 20:57:03 +09:00
Rui Ueyama 2d5e6888f9 [Mach-O] wip 2021-09-18 20:57:01 +09:00
Rui Ueyama 3a503ce2cc [Mach-O] wip 2021-09-18 20:56:59 +09:00
Rui Ueyama 1f3f019a59 [Mach-O] wip 2021-09-18 20:56:55 +09:00
Rui Ueyama f55fc5f082 [Mach-O] wip 2021-09-18 20:56:51 +09:00
Rui Ueyama fb0b316c81 [Mach-O] wip 2021-09-18 17:38:34 +09:00
Rui Ueyama 1ebfc28aea [Mach-O] wip 2021-09-18 17:25:59 +09:00
Rui Ueyama da5effd52d [Mach-O] wip 2021-09-18 16:56:00 +09:00
Rui Ueyama e8aa31cd8b [Mach-O] wip 2021-09-18 15:05:46 +09:00
Rui Ueyama c7243f6c5c [Mach-O] wip 2021-09-18 14:38:23 +09:00
Rui Ueyama 2500ae7e27 [Mach-O] wip 2021-09-18 12:35:27 +09:00
Rui Ueyama 5af8995471 [Mach-O] wip 2021-09-18 12:27:17 +09:00
Rui Ueyama aa5061d784 [Mach-O] wip 2021-09-18 11:42:48 +09:00
Rui Ueyama 4b79c858dd [Mach-O] wip 2021-09-18 11:40:38 +09:00
Rui Ueyama d2aec43b66 [Mach-O] wip 2021-09-17 22:40:21 +09:00
Rui Ueyama 76b767df4b [Mach-O] wip 2021-09-17 22:01:00 +09:00
Rui Ueyama 1dc41ab2d0 [Mach-O] wip 2021-09-17 21:57:34 +09:00
Rui Ueyama c6d3370f07 [Mach-O] wip 2021-09-17 20:47:53 +09:00
Rui Ueyama 40a689011e [Mach-O] wip 2021-09-17 20:43:49 +09:00
Rui Ueyama 03d73f2e9e [Mach-O] wip 2021-09-17 20:32:29 +09:00
Rui Ueyama 142a9c2703 [Mach-O] wip 2021-09-17 20:13:37 +09:00
Rui Ueyama 49233b1ef6 [Mach-O] wip 2021-09-17 18:03:44 +09:00
Rui Ueyama 958d79fa6b [Mach-O] wip 2021-09-17 15:19:00 +09:00
Rui Ueyama 079900fa84 [Mach-O] wip 2021-09-17 13:54:40 +09:00
Rui Ueyama 211bc1810b [Mach-O] wip 2021-09-16 22:23:18 +09:00
Rui Ueyama 0d60940b40 [Mach-O] wip 2021-09-16 22:12:51 +09:00
Rui Ueyama 1e3aff2d71 [Mach-O] wip 2021-09-16 20:02:24 +09:00
Rui Ueyama 7bab3aa994 [Mach-O] wip 2021-09-16 19:26:49 +09:00
Rui Ueyama bfdf9ecf7b [Mach-O] wip 2021-09-16 18:13:59 +09:00
Rui Ueyama 4933af3550 [Mach-O] wip 2021-09-16 17:59:32 +09:00
Rui Ueyama 474dc4a123 [Mach-O] wip 2021-09-16 17:40:14 +09:00
Rui Ueyama aea0022c5c [Mach-O] wip 2021-09-16 17:19:54 +09:00
Rui Ueyama a1ec749dfe [Mach-O] wip 2021-09-16 16:54:14 +09:00
Rui Ueyama 6b477e37ca [Mach-O] wip 2021-09-16 16:21:18 +09:00
Rui Ueyama 28092ffe9d [Mach-O] wip 2021-09-16 15:29:30 +09:00
Rui Ueyama 5e83481ffb [Mach-O] wip 2021-09-16 14:49:50 +09:00
Rui Ueyama 7232178513 [Mach-O] wip 2021-09-16 14:20:38 +09:00
Rui Ueyama aaf04a515b [Mach-O] wip 2021-09-16 12:25:05 +09:00
Rui Ueyama 5ea876ff2f [Mach-O] wip 2021-09-15 21:00:34 +09:00
Rui Ueyama 2fedb900d3 [Mach-O] wip 2021-09-15 17:32:13 +09:00
Rui Ueyama 150565601b Move error handlers from mold::elf to mold 2021-09-15 15:32:42 +09:00
Rui Ueyama 6ab3ddaf8e Move cleanup handlers from mold::elf to mold 2021-09-15 15:25:14 +09:00
Rui Ueyama cae5a93c7e [Mach-O] wip 2021-09-15 12:40:27 +09:00
Rui Ueyama 955f35e977 [Mach-O] wip 2021-09-14 19:45:51 +09:00
Rui Ueyama a504ef9ba7 [Mach-O] wip 2021-09-14 18:45:23 +09:00
Rui Ueyama dcc3cbf1b1 [Mach-O] wip 2021-09-14 17:11:49 +09:00
Rui Ueyama 6aed43b89c [Mach-O] wip 2021-09-14 16:23:03 +09:00
Rui Ueyama 8035024531 [Mach-O] wip 2021-09-14 16:02:41 +09:00
Rui Ueyama 90380ed232 [Mach-O] wip 2021-09-14 15:57:22 +09:00
Rui Ueyama 568bba75c6 [Mach-O] wip 2021-09-14 15:28:28 +09:00
Rui Ueyama f81c869d8f [Mach-O] wip 2021-09-14 15:27:36 +09:00
Rui Ueyama f546e7e696 [Mach-O] wip 2021-09-14 13:12:43 +09:00
Rui Ueyama 730e030994 [Mach-O] wip 2021-09-13 22:40:10 +09:00
Rui Ueyama 2e24055f5c [Mach-O] wip 2021-09-13 21:59:20 +09:00
Rui Ueyama e9caa18ff9 [Mach-O] wip 2021-09-13 21:26:51 +09:00
Rui Ueyama 93bc0cf38a [Mach-O] wip 2021-09-13 20:48:46 +09:00
Rui Ueyama 1fe9055055 [Mach-O] wip 2021-09-13 20:28:06 +09:00
Rui Ueyama 79f2b2bbdc [Mach-O] wip 2021-09-13 19:30:41 +09:00
Rui Ueyama fbc2ea478e [Mach-O] wip 2021-09-13 19:18:04 +09:00
Rui Ueyama 7ffc3a4545 [Mach-O] Add a feature to dump an executable
This is not a linker feature, but in order to learn how Mach-O
executables are constructed, I'll implement a dump feature.
I'll remove the feature once I understand the structure of Mach-O
binaries.
2021-09-13 18:16:13 +09:00
Rui Ueyama e051ad2a9a Do not define _GNU_SOURCE
We should not depend on glibc-specific features.
2021-09-13 17:29:49 +09:00
Rui Ueyama 02b9744d5d Add -g compiler option only when DEBUG=1 2021-09-12 19:42:25 +09:00
Rui Ueyama 08b61f29d2 [ELF] Add --require-defined 2021-09-12 18:34:42 +09:00
Rui Ueyama 1cb750a649 [ELF] Add -Bno-symbolic 2021-09-11 21:41:17 +09:00
Rui Ueyama 730105898b Silence a warning 2021-09-11 14:56:09 +09:00
Rui Ueyama 0a4305f82a Inline a few functions 2021-09-11 14:54:55 +09:00
Rui Ueyama 2e5f480a97 Simplify input file preloading logic 2021-09-10 20:41:20 +09:00
Rui Ueyama 0001cb2818 Simplify 2021-09-10 16:10:28 +09:00
Rui Ueyama 8558a98bf6 Simplify 2021-09-10 16:05:45 +09:00
Rui Ueyama c42bd19c49 Add a missing filename 2021-09-08 20:22:51 +09:00
Rui Ueyama 68ff4a8408 Rename functions 2021-09-08 20:15:03 +09:00
Rui Ueyama dc7e0ca4bf Remove a useless macro 2021-09-08 19:51:02 +09:00
Rui Ueyama 78bddacd5b Move target-independent files to the top directory 2021-09-08 19:49:51 +09:00
Rui Ueyama 8f10ac5047 [Mach-O] Add a test for mach-o stub 2021-09-08 19:19:07 +09:00
Rui Ueyama 5dab8612c8 Move ELF tests to test/elf directory 2021-09-08 19:13:18 +09:00
Rui Ueyama bfa24a3205 Create temporary files for tests under out/ 2021-09-08 19:08:52 +09:00
Rui Ueyama cab0ccf0bd [Mach-O] Add a stub for Mach-O 2021-09-08 19:03:07 +09:00
Rui Ueyama 6cf0a010ec Install mold as ld.mold
This command name is more comsistent with other linkers, i.e.,
ld.bfd, ld.gold and ld.bfd. mold will be a all-in-one linker that
is capable of cross-linking, and it should dispatch according to
argv[0] as follows:

 - mold: a native linker
 - ld or ld.mold: a Unix linker
 - ld64.mold: a macOS linker
 - mold-link: a Windows linker
2021-09-08 17:06:22 +09:00
Rui Ueyama b7be2e6d3b Remove an unused variable 2021-09-08 15:56:45 +09:00
Rui Ueyama aed968ebe6 Fix CI failure 2021-09-07 20:35:26 +09:00
Rui Ueyama 087d4300f2 Create object files in out directory 2021-09-07 20:17:31 +09:00
Rui Ueyama dc672ad821 Bump mold version to 0.9.5 2021-09-06 21:06:34 +09:00
Rui Ueyama b39a47a759 Promote weak undefs to dynamic symbols if building DSO
475a250ad4 changed mold's behavior on
weak undefined symbols, so that if they are not resolved within the
current ELF module, they are resolved to address zero. Previously,
they would be promoted to dynamic symbols to give then another chance
to be resolved at runtime.

That change caused a regression in Firefox. Firefox uses weak undef
symbols as a mean to export symbols from libxul.so. Quote from
Firefox's mfbt/Types.h:

> On linux mozglue is linked in the program and we link libxul.so with
> -z,defs. Normally that causes the linker to reject undefined references in
> libxul.so, but as a loophole it allows undefined references to weak
> symbols. We add the weak attribute to the import version of the MFBT API
> macros to exploit this.

So, they use this as a "loophole".

This change partially revert 475a250ad4.
Now, remaining weak undefs are resolved to address zero only when we
are creating an executable.

Fixes https://github.com/rui314/mold/issues/114
2021-09-06 20:47:16 +09:00
Rui Ueyama c21302e28a Do not use strerror
strerror is not guaranteed to be thread-safe
2021-09-05 17:15:15 +09:00
Rui Ueyama 76018da068 Remove dependency to OpenSSL's RAND_bytes 2021-09-05 17:02:08 +09:00
Rui Ueyama b4953951f0 Remove an unused file 2021-09-03 01:31:33 +09:00
Rui Ueyama 459b5973bb Move code to elf sub-directory 2021-09-02 23:16:49 +09:00
Rui Ueyama 1103b14683 Revert "Remove dependency to openssl/rand.h"
This reverts commit 16d48eae6f.

I don't know why, but that commit broke statically-linked mold.
2021-09-02 23:14:23 +09:00
Rui Ueyama 177dc71335 Move third-party libraries to a separate sub-directory 2021-09-02 22:19:09 +09:00
Rui Ueyama f7a4aceeae Remove an unused file 2021-09-02 22:14:51 +09:00
Rui Ueyama fd391b03ef Move everything into mold::elf namespace 2021-09-02 18:19:43 +09:00
Rui Ueyama 1be5d61bb4 Fix a build issue on macOS 2021-09-01 15:40:11 +09:00
Rui Ueyama 4678fcee52 Fix a build issue on macOS 2021-09-01 15:29:45 +09:00
Rui Ueyama e7537116b3 Fix a build issue on macOS
macOS does not define std::quick_exit.
2021-09-01 14:12:05 +09:00
Rui Ueyama 16d48eae6f Remove dependency to openssl/rand.h 2021-09-01 13:37:11 +09:00
Rui Ueyama 0ce0ceb2c2 Fix a build issue on macOS 2021-09-01 13:26:32 +09:00
Rui Ueyama 0bac54baaa Remove dependency to endian.h
This header file is not there on macOS.
2021-08-31 23:45:38 +09:00
Rui Ueyama d994020807 Add missing include 2021-08-31 23:42:52 +09:00
Rui Ueyama 6a33e7eaa1 Refactor 2021-08-31 17:43:38 +09:00
Rui Ueyama 2a2da35c25 Remove unused declaration 2021-08-30 22:01:49 +09:00
Rui Ueyama b3d0ee093b Report the "recompile with -fPIC' error correctly 2021-08-30 21:07:27 +09:00
Rui Ueyama d3577550d2 Fix filename typo 2021-08-30 20:52:28 +09:00
Rui Ueyama 16c02c2cc3 Refactor 2021-08-30 18:41:27 +09:00
Rui Ueyama 92855aa6ee Refactor 2021-08-30 18:01:27 +09:00
Rui Ueyama e69182d7bc Refactor 2021-08-30 17:32:08 +09:00
Rui Ueyama 4734007ec9 [AArch64] Improve error message 2021-08-30 00:10:43 +09:00
Rui Ueyama ed230a12cc [i386] Add relocation overflow check 2021-08-30 00:05:07 +09:00
Rui Ueyama 0b2584018d Refactor 2021-08-29 23:43:52 +09:00
Rui Ueyama 9501d2cc67 [AArch64] Add relocation overflow check 2021-08-29 21:48:54 +09:00
Rui Ueyama c50dd18881 Fix an issue that local symbols are exported
We sometimes put local symbols into .dynsym, but if they are
global symbols hidden by a version script, they were exported
from .dynsym. This patch correctly makes them local symbols.
2021-08-29 13:04:23 +09:00
Rui Ueyama d06e2f71f3 Attempt to fix CI 2021-08-28 20:47:45 +09:00
Rui Ueyama d43e43adbb Add a test 2021-08-28 14:50:19 +09:00
Rui Ueyama 59c4fc06f3 Fix indentation 2021-08-28 14:37:30 +09:00
Rui Ueyama 5016240d14 Bump mold version to 0.9.4 2021-08-27 18:23:34 +09:00
Rui Ueyama 2032b51dac Use ccache to speed up compilation 2021-08-27 16:59:00 +09:00
Rui Ueyama 06b11da76d Refactor 2021-08-27 16:41:34 +09:00
Rui Ueyama 475a250ad4 Do not promote unresolved weak undefs to dynamic symbols
Previously, we try to promote unresolved weak undefined symbols to
dynamic symbols so that they will get another chance to be resolved at
runtime. This is not always doable. Consider the following two cases:

 - There's a relocation that directly refers a weak undefined symbol

 - There's a relocation that is in a read-only section

In the former case, we need to create a copy relocation, but we can't
do that against a weak undef becasue a weak undef doesn't have the notion
of the symbol size.

In the latter case, we need to create a dynamic relocation, but such
dynamic relocation would fail at load-time when the runtime linker tries
to mutate the contents of a read-only section.

So, we had a logic to detect the above cases and promote weak undefs
only when they can be promoted. But the logic is not as robust as I
hoped. So, in this patch, I removed the logic and stop promoting weak
undefs.
2021-08-27 12:50:23 +09:00
Rui Ueyama b99626fbf2 Refactor 2021-08-25 13:53:09 +00:00
Rui Ueyama 046cdbe724 Simplify 2021-08-25 17:43:57 +09:00
Rui Ueyama 5d53ed9eae Support R_AARCH64_LDST16_ABS_LO12_NC relocation 2021-08-25 08:43:03 +00:00
Rui Ueyama b933cbbae6 Improve Gentoo test script 2021-08-25 08:42:51 +00:00
Rui Ueyama 92616474e4 Fix R_AARCH64_ADR_GOT_PAGE relocation 2021-08-25 07:22:12 +00:00
Rui Ueyama ffb32fe73e Make gentoo-test script work on AArch64 2021-08-25 07:22:02 +00:00
Rui Ueyama e4e618628f Use compare_exchange_weak instead of strong version
I'm not sure if that makes any difference (at least on x86, it doesn't),
but this change is harmless, so I'm making this change.
2021-08-25 04:05:36 +00:00
Rui Ueyama be4f2f52b2 Fix indentation 2021-08-25 12:48:41 +09:00
Rui Ueyama 05ecceb436 Update README 2021-08-25 12:36:16 +09:00
Rui Ueyama 81cb2fab89 Refactor 2021-08-24 10:37:38 +00:00
Rui Ueyama 8bb9fe654e Support R_AARCH64_LDST128_ABS_LO12_NC
With this change, mold can self-host it on ARM64 machines.
2021-08-24 07:08:08 +00:00
Rui Ueyama 684962b84c Support TLSGD relocations for AArch64 2021-08-24 06:29:04 +00:00
Rui Ueyama 75b85b2e8f Remove an unnecessary insn from .plt.got 2021-08-23 12:34:21 +00:00
Rui Ueyama 51705d90c4 Update comment 2021-08-23 20:57:29 +09:00
Rui Ueyama 5912a0022f Fix CI 2021-08-23 18:40:05 +09:00
Rui Ueyama fe9e52b184 Fix CI 2021-08-23 18:32:18 +09:00
Rui Ueyama a4aa3b93cb Fix execle wrapper 2021-08-23 09:21:31 +00:00
Rui Ueyama f0f40d073b Fix tests for AArch64 2021-08-23 09:21:31 +00:00
Rui Ueyama bb1ec6177f Fix TLSDESC relocations against imported symbols
Such relocations shouldn't be relaxed to local-exec, as they are
not local.
2021-08-23 13:38:53 +09:00
Rui Ueyama 7f943d1445 Implement TLSDESC relaxation for AArch64 2021-08-23 03:24:18 +00:00
Rui Ueyama abc2ffc185 Refactor 2021-08-23 11:44:24 +09:00
Rui Ueyama 4c710df347 Fix TLSDESC relocations for statically-linked executables
We shouldn't create TLSDESC dynamic relocations for statically-
linked executables because such executables don't contain the
tranpoline function needed for TLSDESC. Instead, we should
always relax these relocations.
2021-08-22 22:01:38 +09:00
Rui Ueyama 3c79d672dc Implement TLSDESC for AArch64 2021-08-22 11:34:22 +00:00
Rui Ueyama a82bc71088 Fix test/nocopyreloc.sh for AARCH64 2021-08-22 10:33:38 +00:00
Rui Ueyama 96589d4acf Allow to put local symbols to .dynsym
We used to write only global symbols to .dynsym because, in most
use cases, .dynsym are used for resolving inter-module dependencies.
However, we need to put local symbols to .dynsym if they are thread-
local symbols.
2021-08-22 19:28:37 +09:00
Rui Ueyama 771fc02b6d Handle LLVM compiler-rt's .eh_frame symbols 2021-08-21 13:29:41 +09:00
Rui Ueyama f570c4e9e8 Fix tests for AARCH64 2021-08-20 09:48:10 +00:00
Rui Ueyama 2a3639a7d8 Fix tests for AARCH63 2021-08-19 12:31:23 +00:00
Rui Ueyama e4547c31d3 Fix AARCH64 PLT
This patch fixes test/hello-dynamic.sh on ARM64 machines.
2021-08-19 18:45:24 +09:00
Rui Ueyama 91d14191c7 Fix TSAN build issue 2021-08-19 18:45:19 +09:00
Rui Ueyama 34558f410d Do not generate dynrels against RO sections for weak undef symbols
Previously, we created dynamic relocations against read-only sections
if the sections contain relocations against weak undefined symbols.
Since such dynamic relocations cannot be applied at load-time, the
generated executables failed with SEGV.

Now, such relocations are resolved to address 0.

Reported at https://github.com/rui314/mold/pull/110
2021-08-17 17:23:22 +09:00
Rui Ueyama aea7fac8f0 Merge pull request #109 from mqudsi/cmake_unix_makefiles
Explicitly instruct CMake to use Unix Makefiles
2021-08-16 07:34:59 +09:00
Mahmoud Al-Qudsi 4bd77f1d78 Explicitly instruct CMake to use Unix Makefiles
mold currently builds dependencies in its `Makefile` by explicitly
issuing `make -C ...` targeting the CMake build directory; this will
fail if a global `CMAKE_GENERATOR` environment variable is present that
specifies a different CMake build file generator.

Closes #91

Signed-off-by: Mahmoud Al-Qudsi <[email protected]>
2021-08-15 12:27:01 -05:00
Rui Ueyama a46ba486a8 Merge pull request #110 from dylanaraps/fix-cc-cxx
Do not hardcode clang/clang++
2021-08-15 21:23:27 +09:00
Dylan Araps f9336832d4 Do not hardcode clang/clang++
Signed-off-by: Dylan Araps <[email protected]>
2021-08-15 14:51:09 +03:00
Rui Ueyama cc7dc1cf0b Fix build for older versions of CMake
Based on https://github.com/oneapi-src/oneTBB/issues/297#issuecomment-878408577

Fixes https://github.com/rui314/mold/issues/105
2021-08-14 10:18:51 +09:00
Rui Ueyama 631913599e Merge pull request #108 from mqudsi/tbb_no_werror
Disable TBB_STRICT when building TBB (on by default)
2021-08-13 23:00:37 +09:00
Rui Ueyama 184583f1ac Merge pull request #104 from syrusakbary/patch-1
Fix README typos and improved syntax a bit
2021-08-13 22:45:57 +09:00
Mahmoud Al-Qudsi f6c92c2eae Disable TBB_STRICT when building TBB (on by default)
This allows mold to build under clang++-14 which catches the write-only
`intptr_t drained` in `tbb/arena.cpp` as an unused parameter that
ultimately triggers `-Werror` to error out and abort the build.

It's 2021 and there are still projects that default to `-Werror` as the
default instead of using it only in the dev and CI environments... smh.

Closes #107.
2021-08-11 18:39:52 -05:00
Syrus Akbary f448fddd50 Update README.md 2021-08-06 04:16:42 -05:00
Syrus Akbary a44ca755d2 Apply suggestions from code review 2021-08-06 04:15:28 -05:00
Syrus Akbary 3720528354 Fix README typos and improved syntax a bit 2021-08-05 16:55:25 -07:00
Rui Ueyama ef69e9e736 Refactor 2021-08-02 21:51:57 +09:00
Rui Ueyama 31be7ba0d9 Refactor 2021-08-02 21:34:26 +09:00
Rui Ueyama 91428eaa96 Do not dereference sections for undef symbols 2021-08-02 15:41:39 +09:00
Rui Ueyama 05b532af79 Refactor 2021-07-29 20:57:05 +09:00
Rui Ueyama 035aa73c43 Fix CI 2021-07-28 23:40:29 +09:00
Rui Ueyama d25c2553ad Intercept all invocations of ld, ld.gold or ld.lld
Previously, only /usr/bin/ld, /usr/bin/ld.lld and /usr/bin/ld.gold
were intercepted. Now, mold ignores the directory part when comparing
a command name with `ld`, `ld.lld` or `ld.gold`.

This change is made because ld is often installed other than /usr/bin.

Fixes https://github.com/rui314/mold/issues/101
2021-07-28 22:42:35 +09:00
Rui Ueyama 965da0af4a Use hyphen instead of underscore in filename
Test files are following this convention already, but source files weren't.
2021-07-26 21:34:04 +09:00
Rui Ueyama 73d1dcac5d Add a test 2021-07-26 21:32:34 +09:00
Rui Ueyama 2d003c2546 Update issue templates 2021-07-26 20:51:57 +09:00
Rui Ueyama 79787360c6 Fix test
This piece of code is intended to be position-independent,
but it actually directly called printf without PLT.

Fixes https://github.com/rui314/mold/issues/100
2021-07-26 20:17:28 +09:00
Rui Ueyama 6b0d599a31 Fix i386 relocation addends
Fixes https://github.com/rui314/mold/issues/98
2021-07-26 19:32:08 +09:00
Rui Ueyama 0f82b352c7 Compile mold object files as PIE 2021-07-26 16:20:15 +09:00
Rui Ueyama fbdb8367ef Do not add an -I option if not necessary 2021-07-26 16:18:26 +09:00
Rui Ueyama 982544a63d Add a header for linker LTO plugin 2021-07-24 17:29:56 +09:00
Rui Ueyama ec3319b37f Bump mold version to 0.9.3 2021-07-21 19:44:21 +09:00
Rui Ueyama 6fa659c41b Remove an argument from test scripts 2021-07-21 16:27:29 +09:00
Rui Ueyama e4e8535345 Remove CMakeLists.txt
I was experimenting with cmake to see if it is useful for mold,
but it looks like using cmake doesn't make much sense for us.
So I'm undoing all changes that I did for cmake.

cmake is definitely better than autoconf, and it is also useful
if your program is cross-platform. However, neither of the benefits
don't apply to mold; mold doesn't need a complex feature detection,
and it currently supports only ELF-based Unix-like systems.

I think Makefile is a lingua-franca, while CMakeLists.txt isn't.
The good ol' Makefile works just fine for mold.

In the future, mold might become a cross-platform product, and if
that happens, we might want to re-introduce cmake.
2021-07-21 16:27:29 +09:00
Rui Ueyama d1422a03e2 Check if libxxhash exists 2021-07-21 00:02:44 +09:00
Rui Ueyama e13b1180af Make cmake to accept -DMOLD_BUILD_STATIC_EXE flags 2021-07-20 23:22:27 +09:00
Rui Ueyama 33fef23ffb Enable assert() for release builds
Our assert() statements are pretty cheap. We want to enable them
even for release builds.

By default, cmake passes -DNDEBUG to disable assert() for release
builds. So, in this patch, we undefine it to re-enable assertions.
2021-07-20 21:13:03 +09:00
Rui Ueyama 9e78e4a792 Update README 2021-07-20 19:46:45 +09:00
Rui Ueyama 800aba8312 Update README 2021-07-20 15:33:57 +09:00
Rui Ueyama 6254d7def3 Make CI to use cmake instead of make 2021-07-20 14:08:07 +09:00
Rui Ueyama 81ea1d407a Make cmake to accept -DMOLD_USE_SYSTEM_{MIMALLOC,TBB} flags 2021-07-20 13:43:02 +09:00
Rui Ueyama 271c9151f4 Disable tests for mold-wrapper if ASAN is enabled
ASAN doens't work well if other libraries are LD_PRELOAD'ed.
2021-07-20 13:32:02 +09:00
Rui Ueyama 59822bf07e Attempt to fix CI 2021-07-20 00:43:14 +09:00
Rui Ueyama fe26dad744 Fix cmake build
You can now build mold with the following commands:

  $ mkdir -p out/debug
  $ cd out/debug
  $ cmake -GNinja -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Debug ../..
  $ ninja

To run tests, use the following commands:

  $ cd out/debug
  $ ctest -j$(nproc)
2021-07-20 00:33:28 +09:00
Rui Ueyama 4d81fdd4d8 Merge pull request #49 from 3a2l/cmake
Add CMake support
2021-07-20 00:08:57 +09:00
Rui Ueyama 2bf2da4822 Refactor 2021-07-18 20:01:55 +09:00
Rui Ueyama e9c6a08e1e Use more relaxed memory store to flip "is_alive" flag
By default, std::atomic<T>::operator=() stores a given value with
the sequentially-consistent memory ordering, which is a very strong
memory order. We only need the atomicity of store in this case,
so we can use memory_order_relaxed instead.

This change improves performance when we have a lot of section
fragments.

This change shortens the time for garbage collection for clang-13
from 170ms to 100ms.
2021-07-18 14:56:25 +09:00
Rui Ueyama afc1fe6645 Update README 2021-07-17 15:09:16 +09:00
Rui Ueyama c0c2e1409c Merge pull request #95 from tbillington/patch-1
include example of linking rust code
2021-07-17 15:08:36 +09:00
Rui Ueyama bb989791a7 Update .gitignore 2021-07-17 01:24:23 +09:00
Rui Ueyama 04e9aa4ebc Skip i386/aarch64 tests if cross compilers are not available
https://github.com/rui314/mold/issues/89
2021-07-17 01:23:58 +09:00
Trent abb1258ee2 include example of linking rust code 2021-07-16 23:00:12 +10:00
Rui Ueyama c220909e1b Improve Gentoo test script 2021-07-16 16:26:41 +09:00
Rui Ueyama cce1cb353f Fill .rela.dyn even if there's no .got 2021-07-16 15:43:37 +09:00
Rui Ueyama c44fa6172b Recognize yet another crt{begin,end}.o variant
Looks like musl libc sometimes uses crtbeginT.o and crtendT.o.
2021-07-16 15:43:21 +09:00
Rui Ueyama 280892f061 Fix tests for musl 2021-07-16 15:30:54 +09:00
Rui Ueyama bedb2f55f9 Fix header path
"sys/signal.h" happens to work with GNU libc, but the correct path
is "signal.h".
2021-07-16 15:26:21 +09:00
Rui Ueyama dca0e6b7ad Do out-of-tree build for tbb and mimalloc
So that the build directory is less scattered.
2021-07-16 15:10:59 +09:00
Rui Ueyama 1713f6e2f7 Rename oneTBB tbb
"oneTBB" used to be a git submodule, and it is now just a subdirectory.
Using the same directory name makes it difficult to switch between
branches. So I rename it "tbb".
2021-07-16 15:10:59 +09:00
Rui Ueyama 3a7b0525c7 Make it build with musl
musl does not provide makecontext() and swapcontext(), though
they were once in POSIX. They were removed from POSIX.2008 though.
2021-07-16 15:10:59 +09:00
Rui Ueyama 7937360c6a Avoid "PAGE_SIZE" as an identifier
musl libc uses PAGE_SIZE as a macro name, so use other name as
a variable name.
2021-07-16 15:10:59 +09:00
Rui Ueyama fb100f21b3 Do not print out stacktrace on crash
This commit partially reverts e855b90344
because that change was not portable to non-glibc systems.
2021-07-16 14:33:20 +09:00
Rui Ueyama fa543ae350 Attempt to fix a breakage
It looks like 564d7e044ff8a2534a2413d34762dd84be7eaf66 introduced
a concurrency issue.
2021-07-16 14:31:12 +09:00
Rui Ueyama d8ad600b6a Improve MergedSection<E>::assign_offsets()
This patch parallelize MergedSection<E>::assign_offsets().
clang-13 link time is reduced from ~2.5s to ~2.35 with this patch.
2021-07-14 17:11:25 +09:00
Rui Ueyama d79cdb8371 Fix a broken test
Fixes https://github.com/rui314/mold/issues/89
2021-07-13 19:24:26 +09:00
Rui Ueyama 41b2fa7375 Optimize string merging
Linking clang-13 with debug info takes ~3.6 seconds on a simulated
10-core/20-threads machine. mold spends most of its time (~2.3 seconds)
merging string literals in .debug_str. Input .debug_str sections contain
70 million string literals in total, which is reduced to 2 million after
de-duplication. The input object files contain a lot of duplicates.
clang-13 with debug info is enormous -- it is ~3.1 GiB after linking.

It looks like TBB's concurrent hashmap doesn't scale well with the
input.

In this patch, I implemented our own concurrent hashmap. The hashmap
is extremely lightweight and support only the key-value insertion
operation. It doesn't even support rehashing. It aborts once the hash
table becomes full.

In order to know the correct size for the hashmap before inserting
strings into it, I also implemented HyperLogLog algorithm in this patch.
HyperLogLog is an algorithm that gives a fairly accurate estimate on
the number of unique elements.

With this patch, mold can link clang-13 in ~2.5 seconds, which is ~30%
faster than before.

https://github.com/rui314/mold/issues/73
2021-07-13 16:54:09 +09:00
Rui Ueyama 6bb091595e Refactor 2021-07-13 13:13:34 +09:00
Rui Ueyama ecbd3eae14 Merge pull request #87 from Logarithmus/fix-run-sh
Fix test/run.sh for cases when /usr/bin/ld -> /usr/bin/mold
2021-07-12 13:49:39 +09:00
Artur Sinila 5fb7ecbd03 Fix test/run.sh for cases when /usr/bin/ld -> /usr/bin/mold
Signed-off-by: Artur Sinila <[email protected]>
2021-07-12 05:27:21 +03:00
Rui Ueyama e0d44b6678 Use concurrent_unorderd_map for string merging
With high contention rate, it looks like concurrent_unordered_map
outperforms concurrent_hash_map.
2021-07-12 00:02:01 +09:00
Rui Ueyama 3204bb01d9 Fix function pointer equality
Previously, a GOT relocation (e.g. R_X86_64_REX_GOTPCRELX) and a
R_X86_64_64 relocation referring the same imported symbols were
resolved to different addresses. Here is why:

 - When we saw a R_X86_64_64 relocation against an imported symbol,
   we created a PLT and resolve the relocation there.

 - GOT relocation is resolved to a GOT entry, which has a true
   address of an imported function at runtime, which is different
   from PLT entries that redirect calls to the real function.

With this patch, we no longer create a PLT entry for R_X86_64_64.
Instead, we emit a dynamic relocation so that it is always resolved
to a real function address.

Fixes GNU MP's `make check` failure, which was reported at
https://github.com/rui314/mold/issues/81
2021-07-10 17:54:57 +09:00
Rui Ueyama faac31f217 Support VERSION linker script command
The grammar of the command is this

  VERSION { <version-script> }

where <version-script> is a version script you can specify with
the --version-script option.
2021-07-09 23:34:44 +09:00
Rui Ueyama e855b90344 Show stacktrace on SEGV and assertion failure 2021-07-09 23:34:39 +09:00
Rui Ueyama 63b6d30876 Compress .repro section contents
Previously, .repro contains an uncompressed tar file containing all
input files. This patch compresses the contents in the gzip format.
2021-07-09 00:47:15 +09:00
Rui Ueyama 3acb7c8e2f Update README
Pointed out by @pecak.
2021-07-08 17:17:10 +09:00
Rui Ueyama 50b7497e19 Align section header to a E::wordsize boundary
Fixes https://github.com/rui314/mold/issues/80
2021-07-08 15:58:55 +09:00
Rui Ueyama 64913ba610 Merge pull request #77 from Logarithmus/fix-tests
Optimize CI build (enable ccache & run tests in parallel)
2021-07-08 00:43:40 +09:00
Artur Sinila f9a5e9171e ci.yml: enable ccache
Signed-off-by: Artur Sinila <[email protected]>
2021-07-07 18:37:14 +03:00
Artur Sinila 67948ac222 ci.yml: run tests in parallel
Signed-off-by: Artur Sinila <[email protected]>
2021-07-07 18:37:14 +03:00
Rui Ueyama 84d9e4d5ac Fix regexp 2021-07-07 23:12:24 +09:00
Rui Ueyama 72cc07fb42 Merge pull request #83 from nehaljwani/dynsym-IFUNC
Do not replace IFUNC with FUNC in .dynsym
2021-07-07 21:48:15 +09:00
Rui Ueyama 057fffb1dd Optimize regexes 2021-07-07 21:46:23 +09:00
Rui Ueyama 6ae115ac69 Support R_AARCH_ABS{32,64} relocations 2021-07-07 21:06:18 +09:00
Nehal J Wani 1aec54efdb Do not replace IFUNC with FUNC in .dynsym
This is a partial revert of dd04357

Signed-off-by: Nehal J Wani <[email protected]>
2021-07-07 07:59:51 -04:00
Rui Ueyama e3dccd0157 Add a test 2021-07-07 20:38:48 +09:00
Rui Ueyama 048a478bab Merge pull request #82 from nehaljwani/dynsym-visibility
Ensure correct visibility of symbols in .dynsym section
2021-07-07 20:25:33 +09:00
Nehal J Wani 36853d1669 Ensure correct visibility of symbols in .dynsym section
Signed-off-by: Nehal J Wani <[email protected]>
2021-07-07 07:19:10 -04:00
Rui Ueyama 6d6df45a23 Fix definition of __GLOBAL_OFFSET_TABLE__ for AArch64 2021-07-07 12:51:23 +09:00
Rui Ueyama d4a67f6f38 Disable an AArch64 test 2021-07-06 23:06:02 +09:00
Rui Ueyama b9c35e0009 Add R_AARCH64_LD64_GOTPAGE_LO15 2021-07-06 23:00:01 +09:00
Rui Ueyama 4dffb74762 Fix CI 2021-07-06 22:49:51 +09:00
Rui Ueyama ce06ea80ca Fix CI 2021-07-06 22:38:41 +09:00
Rui Ueyama 41497d7990 Support AARCH64 relocations
With this change, mold can now create a "Hello world" program
for the AArch64 target.
2021-07-06 22:24:48 +09:00
Rui Ueyama fc4aafa974 Fix assertion failures
mold failed with `make EXTRA_CPPFLAGS="-Wp,-D_GLIBCXX_ASSERTIONS" test`,
which enables extra assertions.

Thanks for @nehaljwani to find out the flag.
2021-07-06 14:43:06 +09:00
Rui Ueyama d0265554a7 Refactor 2021-07-06 13:39:33 +09:00
Rui Ueyama 713b3a9925 Merge pull request #78 from nehaljwani/oob
Fix out of bound check in get_shndx()
2021-07-06 10:49:41 +09:00
Nehal J Wani b596248a1e Fix out of bound check in get_shndx()
Assert failure:
/usr/include/c++/11.1.0/span:276:
  std::span::reference
  std::span<ElfSym<X86_64>, 18446744073709551615>
     ::operator[](std::span::size_type)
         const [_Type = ElfSym<X86_64>, _Extent = 18446744073709551615]:
           Assertion '__idx < size()' failed.

GDB stacktrace:
Thread 2.4 "mold" received signal SIGABRT, Aborted.
[Switching to Thread 0x7fabc3d3f640 (LWP 17804)]
0x00007fabc496fd22 in raise () from /usr/lib/libc.so.6
(gdb) bt
 #0  0x00007fabc496fd22 in raise () from /usr/lib/libc.so.6
 #1  0x00007fabc4959862 in abort () from /usr/lib/libc.so.6
 #2  0x000055a9685eb018 in std::__replacement_assert (...)
     at /usr/include/c++/11.1.0/x86_64-pc-linux-gnu/bits/c++config.h:504
 #3  0x000055a968614c47 in std::span<ElfSym<X86_64>, ...>::operator[] (...)
     at /usr/include/c++/11.1.0/span:276
 #4  ObjectFile<X86_64>::get_shndx (this=<...>, esym=...) at ./mold.h:2145
 #5  ObjectFile<X86_64>::get_section (this=<...>, esym=...) at ./mold.h:2154

Signed-off-by: Nehal J Wani <[email protected]>
2021-07-05 18:02:27 -04:00
Rui Ueyama 426a689d22 Fix i386 relocation handling 2021-07-05 14:00:16 +09:00
Rui Ueyama b721514f98 Rename a variable 2021-07-05 14:00:16 +09:00
Rui Ueyama 4282ef24cd Merge pull request #76 from Logarithmus/cache
.gitignore: add .cache & compile_commands.json (for clangd)
2021-07-05 10:15:08 +09:00
Artur Sinila 7bbfaac67a .gitignore: add .cache & compile_commands.json (for clangd)
Signed-off-by: Artur Sinila <[email protected]>
2021-07-04 21:09:51 +03:00
Rui Ueyama 11f7653daf Refactor 2021-07-04 21:01:39 +09:00
Rui Ueyama 19d29f25f2 Merge pull request #71 from nehaljwani/gcc-ci
[CI] Test code with GCC 11.1.0
2021-07-04 13:26:40 +09:00
Nehal J Wani 32dff0b877 [CI] Test code with GCC 11.1.0
Signed-off-by: Nehal J Wani <[email protected]>
2021-07-03 23:51:39 -04:00
Rui Ueyama f3a7f86abb Merge pull request #75 from nehaljwani/fix-spare-dynamic-tags-test
[tests] Improve test for -spare-dynamic-tags
2021-07-04 12:45:33 +09:00
Nehal J Wani be6d443bc8 [tests] Improve test for -spare-dynamic-tags
The default size and offsets of the .dynamic section are not necessarily
same with different builds of clang. For example, some builds can make
clang pass --hash-style=gnu to the linker and some --hash-style=both.

This change updates the test to compute the difference in size of the
.dynamic section before and after using the flag -spare-dynamic-tags,
making it independent of the compiler build.

Signed-off-by: Nehal J Wani <[email protected]>
2021-07-03 22:35:42 -04:00
Rui Ueyama 70d1acd744 Fix CI 2021-07-03 21:53:28 +09:00
Rui Ueyama 30ecba45c6 Update README 2021-07-03 21:48:18 +09:00
Rui Ueyama accf96877f Add a stub for AArch64
It can generate an executable, but generated executable won't run
correctly because we don't process relocations at all yet.
2021-07-03 21:39:44 +09:00
Rui Ueyama d502b6ec60 Initial AARCH64 support 2021-07-03 20:48:38 +09:00
Rui Ueyama 546df46eaf Define AARCH64 type and other constants for ARM64 2021-07-03 19:58:46 +09:00
Rui Ueyama fe3af50fb8 Fix comment 2021-07-03 18:32:43 +09:00
Rui Ueyama 6935272033 Create $PREFIX/bin if not exists 2021-07-03 15:07:26 +09:00
Rui Ueyama 3c992c02ea Merge pull request #72 from nehaljwani/fix-pie-test
[tests] Fix pie test when cc isn't built with --enable-default-pie
2021-07-03 14:30:05 +09:00
Rui Ueyama 4c4bfca20e Make make clean to remove artifacts of vendored repos 2021-07-03 14:29:01 +09:00
Rui Ueyama 2abca8b5c1 Fix tests for non-Ubuntu-20 systems
Fixes https://github.com/rui314/mold/issues/67
2021-07-03 14:26:38 +09:00
Rui Ueyama 662e8aa60a Support R_386_TLS_IE relocation 2021-07-03 14:14:50 +09:00
Nehal J Wani 3208ceaac6 [tests] Fix pie test when cc isn't built with --enable-default-pie
mold: /__w/mold/mold/test/tmp/pie/a.o:(.text): R_X86_64_32 relocation
       against symbol `.rodata' can not be used; recompile with -fPIE
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Signed-off-by: Nehal J Wani <[email protected]>
2021-07-03 01:05:28 -04:00
Rui Ueyama c278c66e16 Merge pull request #70 from nehaljwani/gcc-11-fix
Fix compilation error with GCC 11
2021-07-03 12:52:21 +09:00
Nehal J Wani a48eee9179 Fix compilation error with GCC 11
mold.h:128:19: error: explicit specialization of
  ‘template<class Key> class tbb::detail::d1::tbb_hash_compare’
  outside its namespace must use a nested-name-specifier [-fpermissive]
  128 | template<> struct tbb_hash_compare<std::string_view> {
      |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Signed-off-by: Nehal J Wani <[email protected]>
2021-07-02 23:40:16 -04:00
Rui Ueyama 475fad3a78 Allow sysroot to be a relative path
Chromium cannot be built with out this patch.
2021-07-03 12:02:32 +09:00
Rui Ueyama 4749764e3e Merge pull request #68 from nehaljwani/tbb-only
[oneTBB] build only required target(s)
2021-07-03 01:43:44 +09:00
Nehal J Wani 9c541ad461 [oneTBB] build only required target(s)
The 'all' target builds tbbmalloc and tbbmalloc_proxy
which mold does not care about.

Signed-off-by: Nehal J Wani <[email protected]>
2021-07-02 12:38:26 -04:00
Rui Ueyama 2f681babf5 Do not attempt to call dlopen
Without this change, statically-linked mold executable crashes
on startup.
2021-07-03 00:08:47 +09:00
Rui Ueyama 3e3d58b6f1 Static-link oneTBB by default
oneTBB API is changing rapidly, so even if a oneTBB is installed
into a system already, it is likely that that is not a compatible
version with mold. This makes program building unnecessarily hard.

This change intends to solve the problem once and for all by
including oneTBB as a subdirectory and static-link against it at
build-time.

oneTBB is released under the Apache License 2.0, which is compatible
with AGPL. Therefore, we can still distribute the binary of mold
under AGPL.

If you do not want to static-link oneTBB, pass `SYSTEM_TBB=1`
to `make`.

This change also ports mold from oneTBB v2020.3 to v2021.3.0.

Fixes https://github.com/rui314/mold/issues/66
2021-07-03 00:08:33 +09:00
Rui Ueyama a32c81f94c Merge commit 'c3c791efcb97384ae25bbdd8a165bbc7473598a8' as 'oneTBB' 2021-07-03 00:01:04 +09:00
Rui Ueyama c3c791efcb Squashed 'oneTBB/' content from commit 261fe226
git-subtree-dir: oneTBB
git-subtree-split: 261fe22630bd61ec137b66f6dd4403ad7ab36a14
2021-07-03 00:01:04 +09:00
Rui Ueyama d19c7dbafc Do not ignore oneTBB directory 2021-07-03 00:00:41 +09:00
Rui Ueyama 29a7d06dd4 Fix race condition 2021-07-01 23:10:00 +09:00
Rui Ueyama cd55d22611 Merge pull request #64 from Logarithmus/fix-makefile
Makefile: add $(DESTDIR) to strip
2021-07-01 20:29:37 +09:00
Artur Sinila 5c846496a2 Makefile: add $(DESTDIR) to strip 2021-07-01 13:05:43 +03:00
Rui Ueyama 0fd62ce693 Fix sysroot handling
Fix https://github.com/rui314/mold/issues/59
2021-07-01 17:40:45 +09:00
Rui Ueyama 18156f3965 Do not set a variable instead of undefine it later 2021-07-01 10:38:30 +09:00
Rui Ueyama 4efc795520 Merge pull request #62 from Logarithmus/fix-makefile
Various fixes in Makefile
2021-07-01 10:05:14 +09:00
Rui Ueyama 9abfeee0e3 Split a function and add tests 2021-07-01 10:00:08 +09:00
Rui Ueyama 44bdbbb164 Merge pull request #61 from johejo/fix_dso_detection
Fix DSO detection for installed with PREFIX
2021-07-01 09:39:11 +09:00
Rui Ueyama b044ac844c Merge pull request #63 from Logarithmus/readme-gcc-guide
README.md: add guide to using mold with GCC via -B option
2021-07-01 09:38:34 +09:00
Rui Ueyama 34a75dee02 Update README
I recommend users use the latest release instead of git HEAD
because HEAD may be broken in a subtle way. Before a release,
I build all Gentoo packages using mold, but I don't do that for
every commit because it takes ~2 days.
2021-07-01 09:28:35 +09:00
Artur Sinila da619e8615 Various fixes in Makefile
Signed-off-by: Artur Sinila <[email protected]>
2021-06-30 20:53:49 +03:00
Artur Sinila aa7d0f5cca README.md: add guide to using mold with GCC via -B option
Signed-off-by: Artur Sinila <[email protected]>
2021-06-30 20:46:26 +03:00
Mitsuo Heijo b6e97e5fb5 Fix DSO detection for installed with PREFIX
Signed-off-by: Mitsuo Heijo <[email protected]>
2021-07-01 01:48:39 +09:00
Rui Ueyama 6432114e2a Fix handling of --sysroot
Fixes https://github.com/rui314/mold/issues/59
2021-06-30 22:15:19 +09:00
Rui Ueyama a89df30537 Merge pull request #57 from nehaljwani/mimalloc-static-only
[mimalloc] build only required target(s)
2021-06-30 08:18:32 +09:00
Rui Ueyama 9a09c777d9 Bump mold version to 0.9.1 2021-06-30 08:11:15 +09:00
Nehal J Wani a1073e69b0 [mimalloc] build only required target(s)
The 'all' target builds the shared library as well as test binaries
which mold doesn't care about

Signed-off-by: Nehal J Wani <[email protected]>
2021-06-29 16:34:46 -04:00
Ahmed Hesham 93845d6a2c Add a CMakeLists.txt file.
This uses CMake to fetch the dependencies without using Git submodules.

Signed-off-by: Ahmed Hesham <[email protected]>
2021-06-07 00:28:17 +01:00
1815 changed files with 328114 additions and 20049 deletions

No files matched your search

+10
View File
@@ -0,0 +1,10 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
Delete this message and describe the bug here. Note that we only fix bugs in the most recent release of mold, so please make sure that you are using the most recent version.
+43 -4
View File
@@ -5,13 +5,52 @@ on:
pull_request:
branches: [ main ]
jobs:
build:
build-clang:
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 dwarfdump libc6-dev:i386 lib32gcc-10-dev gcc-multilib
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: make -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: make test
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 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: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=gcc CXX=g++ -j$(nproc)
- name: make test
run: make -j$(nproc) test-all
build-macos:
runs-on: macos-11
steps:
- uses: actions/checkout@v2
- name: brew
run: brew install xxhash
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu)
- name: make test
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu) test
+5 -1
View File
@@ -1,10 +1,14 @@
*.o
*.so
*~
.cache
compile_commands.json
/mold
/test/tmp
options.inc
core
ld
oneTBB
ld64.mold
gentoo
out
/mold-*.tar.gz
View File
Whitespace-only changes.
+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)
+193 -58
View File
@@ -1,89 +1,224 @@
CC = clang
CXX = clang++
# If you want to enable ASAN, run `make` with the following options:
#
# make CXXFLAGS='-fsanitize=address -g' LDFLAGS=-fsanitize=address USE_MIMALLOC=0
MIMALLOC_LIB = mimalloc/out/release/libmimalloc.a
GIT_HASH = $(shell [ -d .git ] && git rev-parse HEAD)
VERSION = 1.2.0
CPPFLAGS = -g -Imimalloc/include -pthread -std=c++20 \
-Wno-deprecated-volatile \
-DMOLD_VERSION=\"0.1.1\" \
-DGIT_HASH=\"$(GIT_HASH)\" \
$(EXTRA_CPPFLAGS)
LDFLAGS = $(EXTRA_LDFLAGS)
LIBS = -Wl,-as-needed -lcrypto -pthread -ltbb -lz -lxxhash -ldl
OBJS = main.o object_file.o input_sections.o output_chunks.o \
mapfile.o perf.o linker_script.o archive_file.o output_file.o \
subprocess.o gc_sections.o icf.o symbols.o cmdline.o filepath.o \
passes.o tar.o compress.o memory_mapped_file.o relocatable.o \
arch_x86_64.o arch_i386.o
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
LIBDIR = $(PREFIX)/lib
LIBEXECDIR = $(PREFIX)/libexec
MANDIR = $(PREFIX)/share/man
PREFIX ?= /usr
DEBUG ?= 0
LTO ?= 0
ASAN ?= 0
TSAN ?= 0
INSTALL = install
INSTALL_PROGRAM = $(INSTALL)
INSTALL_DATA = $(INSTALL) -m 644
ifeq ($(DEBUG), 1)
CPPFLAGS += -O0
else
CPPFLAGS += -O2
D = $(DESTDIR)
# CXX defaults to `g++`. Rewrite it with a vendor-neutral compiler
# name `c++`.
ifeq ($(origin CXX), default)
CXX = c++
endif
# Allow overriding pkg-config binary
PKG_CONFIG = pkg-config
# If you want to keep symbols in the installed binary, run make with
# `STRIP=true` to run /bin/true instead of the strip command.
STRIP = strip
SRCS = $(wildcard *.cc elf/*.cc macho/*.cc)
OBJS = $(SRCS:%.cc=out/%.o)
OS := $(shell uname -s)
ARCH := $(shell uname -m)
IS_ANDROID = 0
ifneq ($(findstring -android,$(shell $(CC) -dumpmachine)),)
IS_ANDROID = 1
endif
# If you want to compile mold for debugging, invoke make as
# `make CXXFLAGS=-g`.
CFLAGS = -O2
CXXFLAGS = -O2
MOLD_CXXFLAGS := -std=c++20 -fno-exceptions -fno-unwind-tables \
-fno-asynchronous-unwind-tables -Ithird-party/xxhash \
-DMOLD_VERSION=\"$(VERSION)\" -DLIBDIR="\"$(LIBDIR)\""
MOLD_LDFLAGS := -pthread -lz -lm -ldl
GIT_HASH := $(shell [ -d .git ] && git rev-parse HEAD)
ifneq ($(GIT_HASH),)
MOLD_CXXFLAGS += -DGIT_HASH=\"$(GIT_HASH)\"
endif
LTO = 0
ifeq ($(LTO), 1)
CPPFLAGS += -flto -O3
CXXFLAGS += -flto -O3
LDFLAGS += -flto
endif
ifeq ($(ASAN), 1)
CPPFLAGS += -fsanitize=address
LDFLAGS += -fsanitize=address
else
# By default, we want to use mimalloc as a memory allocator.
# Since replacing the standard malloc is not compatible with ASAN,
# we do that only when ASAN is not enabled.
LDFLAGS += -Wl,-whole-archive $(MIMALLOC_LIB) -Wl,-no-whole-archive
# By default, we want to use mimalloc as a memory allocator. mimalloc
# is disabled on macOS and Android because it didn't work on those hosts.
USE_MIMALLOC = 1
ifeq ($(OS), Darwin)
USE_MIMALLOC = 0
else ifeq ($(IS_ANDROID), 1)
USE_MIMALLOC = 0
endif
ifeq ($(TSAN), 1)
CPPFLAGS += -fsanitize=thread
LDFLAGS += -fsanitize=thread
ifeq ($(USE_MIMALLOC), 1)
ifdef SYSTEM_MIMALLOC
MOLD_CXXFLAGS += -DUSE_SYSTEM_MIMALLOC
MOLD_LDFLAGS += -lmimalloc
else
MIMALLOC_LIB = out/mimalloc/libmimalloc.a
MOLD_CXXFLAGS += -Ithird-party/mimalloc/include
MOLD_LDFLAGS += -Wl,-whole-archive $(MIMALLOC_LIB) -Wl,-no-whole-archive
endif
endif
ifdef SYSTEM_TBB
MOLD_LDFLAGS += -ltbb
else
TBB_LIB = out/tbb/libs/libtbb.a
MOLD_LDFLAGS += $(TBB_LIB)
MOLD_CXXFLAGS += -Ithird-party/tbb/include
endif
ifeq ($(OS), Linux)
ifeq ($(IS_ANDROID), 0)
# glibc before 2.17 need librt for clock_gettime
MOLD_LDFLAGS += -Wl,-push-state -Wl,-as-needed -lrt -Wl,-pop-state
endif
endif
NEEDS_LIBCRYPTO = 0
ifneq ($(OS), Darwin)
NEEDS_LIBCRYPTO = 1
endif
ifeq ($(NEEDS_LIBCRYPTO), 1)
MOLD_CXXFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I openssl)
MOLD_LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L openssl) -lcrypto
endif
# '-latomic' flag is needed building on riscv64 system
# RV32 system doesn't tested yet
# seems like '-atomic' would be better but not working.
ifeq ($(ARCH), riscv64)
MOLD_LDFLAGS += -latomic
endif
# -Wc++11-narrowing is a fatal error on Android, so disable it.
ifeq ($(IS_ANDROID), 1)
MOLD_CXXFLAGS += -Wno-c++11-narrowing
endif
ifeq ($(OS), Linux)
MOLD_WRAPPER_LDFLAGS = -Wl,-push-state -Wl,-no-as-needed -ldl -Wl,-pop-state
endif
DEPFLAGS = -MT $@ -MMD -MP -MF out/$*.d
all: mold mold-wrapper.so
mold: $(OBJS) $(MIMALLOC_LIB)
$(CXX) $(CFLAGS) $(OBJS) -o $@ $(LDFLAGS) $(LIBS)
-include $(SRCS:%.cc=out/%.d)
mold-wrapper.so: mold-wrapper.c Makefile
$(CC) -fPIC -shared -o $@ $< -ldl
mold: $(OBJS) $(MIMALLOC_LIB) $(TBB_LIB)
$(CXX) $(OBJS) -o $@ $(MOLD_LDFLAGS) $(LDFLAGS)
ln -sf mold ld
ln -sf mold ld64.mold
$(OBJS): mold.h elf.h Makefile
mold-wrapper.so: elf/mold-wrapper.c
$(CC) $(DEPFLAGS) $(CFLAGS) -fPIC -shared -o $@ $< $(MOLD_WRAPPER_LDFLAGS) $(LDFLAGS)
out/%.o: %.cc out/elf/.keep out/macho/.keep
$(CXX) $(MOLD_CXXFLAGS) $(DEPFLAGS) $(CXXFLAGS) -c -o $@ $<
out/elf/.keep out/macho/.keep:
mkdir -p $(@D)
touch $@
$(MIMALLOC_LIB):
mkdir -p mimalloc/out/release
(cd mimalloc/out/release; CFLAGS=-DMI_USE_ENVIRON=0 cmake ../..)
$(MAKE) -C mimalloc/out/release
mkdir -p out/mimalloc
(cd out/mimalloc; CFLAGS=-DMI_USE_ENVIRON=0 cmake -G'Unix Makefiles' ../../third-party/mimalloc)
$(MAKE) -C out/mimalloc mimalloc-static
$(TBB_LIB):
mkdir -p out/tbb
(cd out/tbb; cmake -G'Unix Makefiles' -DBUILD_SHARED_LIBS=OFF -DTBB_TEST=OFF -DCMAKE_CXX_FLAGS="$(CXXFLAGS) -D__TBB_DYNAMIC_LOAD_ENABLED=0" -DTBB_STRICT=OFF ../../third-party/tbb)
$(MAKE) -C out/tbb tbb
(cd out/tbb; ln -sf *_relwithdebinfo libs)
test tests check: all
$(MAKE) -C test --output-sync --no-print-directory
ifeq ($(OS), Darwin)
$(MAKE) -C test -f Makefile.darwin --no-print-directory
else
$(MAKE) -C test -f Makefile.linux --no-print-directory --output-sync
endif
@if test -t 1; then \
printf '\e[32mPassed all tests\e[0m\n'; \
else \
echo 'Passed all tests'; \
fi
test-x86-64: all
@echo x86_64
CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ GCC=x86_64-linux-gnu-gcc GXX=x86_64-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=x86_64 QEMU='qemu-x86_64 -L /usr/x86_64-linux-gnu' $(MAKE) test
test-i386: all
@echo i386
CC=i686-linux-gnu-gcc CXX=i686-linux-gnu-g++ GCC=i686-linux-gnu-gcc GXX=i686-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=i386 QEMU='qemu-i386 -L /usr/i686-linux-gnu' $(MAKE) test
test-arm64: all
@echo arm64
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ GCC=aarch64-linux-gnu-gcc GXX=aarch64-linux-gnu-g++ OBJDUMP=aarch64-linux-gnu-objdump MACHINE=aarch64 QEMU='qemu-aarch64 -L /usr/aarch64-linux-gnu' $(MAKE) test
test-arm32: all
@echo arm
CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++ GCC=arm-linux-gnueabihf-gcc GXX=arm-linux-gnueabihf-g++ OBJDUMP=arm-linux-gnueabihf-objdump MACHINE=arm QEMU='qemu-arm -L /usr/arm-linux-gnueabihf' $(MAKE) test
test-riscv64: all
@echo riscv64
CC=riscv64-linux-gnu-gcc CXX=riscv64-linux-gnu-g++ GCC=riscv64-linux-gnu-gcc GXX=riscv64-linux-gnu-g++ OBJDUMP=riscv64-linux-gnu-objdump MACHINE=riscv64 QEMU='qemu-riscv64 -L /usr/riscv64-linux-gnu' $(MAKE) test
test-all:
$(MAKE) test-x86-64
$(MAKE) test-i386
$(MAKE) test-arm64
$(MAKE) test-arm32
$(MAKE) test-riscv64
install: all
install -m 755 mold $(PREFIX)/bin
strip $(PREFIX)/bin/mold
$(INSTALL) -d $D$(BINDIR)
$(INSTALL_PROGRAM) mold $D$(BINDIR)
$(STRIP) $D$(BINDIR)/mold
install -m 755 -d $(PREFIX)/lib/mold
install -m 644 mold-wrapper.so $(PREFIX)/lib/mold
strip $(PREFIX)/lib/mold/mold-wrapper.so
$(INSTALL) -d $D$(LIBDIR)/mold
$(INSTALL_DATA) mold-wrapper.so $D$(LIBDIR)/mold
$(STRIP) $D$(LIBDIR)/mold/mold-wrapper.so
install -m 644 docs/mold.1 $(PREFIX)/share/man/man1
rm -f $(PREFIX)/share/man/man1/mold.1.gz
gzip -9 $(PREFIX)/share/man/man1/mold.1
$(INSTALL) -d $D$(LIBEXECDIR)/mold
ln -sf $(BINDIR)/mold $D$(LIBEXECDIR)/mold/ld
$(INSTALL) -d $D$(MANDIR)/man1
$(INSTALL_DATA) docs/mold.1 $D$(MANDIR)/man1
ln -sf mold $D$(BINDIR)/ld.mold
ln -sf mold $D$(BINDIR)/ld64.mold
uninstall:
rm -rf $(PREFIX)/bin/mold $(PREFIX)/share/man/man1/mold.1.gz \
$(PREFIX)/lib/mold
rm -f $D$(BINDIR)/mold $D$(BINDIR)/ld.mold $D$(BINDIR)/ld64.mold
rm -f $D$(MANDIR)/man1/mold.1
rm -rf $D$(LIBDIR)/mold
clean:
rm -rf *.o *~ mold mold-wrapper.so test/tmp
rm -rf *~ mold mold-wrapper.so out ld ld64.mold mold-*-linux.tar.gz
.PHONY: all test tests check clean $(TESTS)
.PHONY: all test tests check clean test-x86-64 test-i386 test-arm64 test-arm32 test-riscv64 test-all
+165 -567
View File
@@ -1,116 +1,190 @@
# mold: A Modern Linker
![mold image](docs/mold.jpg)
mold is a faster drop-in replacement for existing Unix linkers.
It is several times faster than the LLVM lld linker, the second-fastest
open-source linker which I originally created a few years ago.
mold is designed to increase developer productivity by reducing
build time, especially in rapid debug-edit-rebuild cycles.
mold is a high performance drop-in replacement for existing Unix
linkers. It is several times faster than LLVM lld linker, the (then-)
fastest open-source linker which I originally created a few years ago.
Here is a performance comparison of GNU gold, LLVM lld and mold for
linking final executables of major large programs.
Here is a performance comparison of GNU gold, LLVM lld, and mold for
linking final debuginfo-enabled executables of major large programs
on a simulated 8-core 16-threads machine.
| Program (linker output size) | GNU gold | LLVM lld | mold | mold w/ preloading
|-------------------------------|----------|----------|-------|-------------------
| Firefox 87 (1.6 GiB) | 29.2s | 6.16s | 1.69s | 0.79s
| Chrome 86 (1.9 GiB) | 54.5s | 11.7s | 1.85s | 0.97s
| Clang 13 (3.1 GiB) | 59.4s | 5.68s | 2.76s | 0.86s
![Link speed comparison](docs/comparison.png)
(These nubmers are measured on an AMD Threadripper 3990X 64-core
machine with 32 threads enabled. All programs are built with debug
info enabled.)
| Program (linker output size) | GNU gold | LLVM lld | mold
|-------------------------------|----------|----------|--------
| Chrome 96 (1.89 GiB) | 53.86s | 11.74s | 2.21s
| Clang 13 (3.18 GiB) | 64.12s | 5.82s | 2.90s
| Firefox 89 libxul (1.64 GiB) | 32.95s | 6.80s | 1.42s
Let me explain the "w/ preloading" column. mold supports the file
preloading feature. That is, if you run mold with `-preload` flag
along with other command line flags, it becomes a daemon and halts
after parsing input files. Then, if you invoke mold with the same
command line options (except `-preload` flag), it tells the daemon to
reload only updated files and proceed. With this feature enabled, and
if most of the input files haven't been updated, mold achieve a
near-`cp` performance or even exceeds it, as the throughput of file
copy using the `cp` command is about 2 GiB/s on my machine.
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.
So, mold is extremely fast per-se and even faster with a bit of cheating.
mold currently supports x86-64, i386, ARM32, ARM64 and 64-bit RISC-V.
Why is mold so fast? One reason is because it simply uses faster
algorithms and efficient data structures than other linkers do.
The other reason is that the new linker is highly parallelized.
## Why does the speed of linking matter?
Here is a side-by-side comparison of per-core CPU usage of lld (left)
and mold (right). They are linking the same program, Chromium
executable.
If you are using a compiled language such as C, C++ or Rust, a build
consists of two phases. In the first phase, a compiler compiles a
source file into an object file (`.o` files). In the second phase,
a linker takes all object files to combine them into a single executable
or a shared library file.
![](docs/htop.gif)
The second phase takes a long time if your build output is large.
mold can make it faster, saving your time and keeping you from being
distracted while waiting for a long build to finish. The difference is
most noticeable when you are in rapid debug-edit-rebuild cycles.
As you can see, mold uses all available cores throughout its execution
and finishes quickly. On the other hand, lld failed to use available
cores most of the time. On this demo, the maximum parallelism is
artificially capped to 16 so that the bars fit in the GIF.
## Install
Currently, mold is being developed with Linux/x86-64 as the primary
target platform. mold can link many user-land programs including large
ones such as web browsers for that target. It also has preliminary
Linux/i386 support. Supporting other OSes and ISAs are planned after
Linux/x86-64 support is complete.
Binary packages for the following distros are currently available.
[![Packaging status](https://repology.org/badge/vertical-allrepos/mold.svg)](https://repology.org/project/mold/versions)
## How to build
mold is written in C++20, so you need a very recent version of GCC or
Clang. I'm using Ubuntu 20.04 as a development platform. In that
environment, you can build mold by the following commands.
mold is written in C++20, so if you build mold yourself, you need a
recent version of a C++ compiler and a C++ standard library. GCC 10.2
or Clang 12.0.0 as well as libstdc++ 10 or libc++ 7 are recommended.
```
$ sudo apt-get install build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libxxhash-dev libtbb-dev git
$ git clone https://github.com/rui314/mold.git
$ cd mold
$ make
I'm using Ubuntu 20.04 as a development platform. In that environment,
you can build mold by the following commands.
### Install dependencies
#### Ubuntu 20.04 and later / Debian 11 and later
```shell
sudo apt-get update
sudo apt-get install -y build-essential git clang cmake libstdc++-10-dev libssl-dev libxxhash-dev zlib1g-dev pkg-config
```
The last `make` command creates `mold` executable.
#### Fedora 34 and later
If you don't have Ubuntu 20.04, or if for any reason `make` in the
above commands doesn't work for you, you can use Docker to build it in
a Docker environment. To do so, just run `./build-static.sh` in this
directory. The script creates a Ubuntu 20.04 Docker image, install
necessary tools and libraries to it and build mold as a static binary.
```shell
sudo dnf install -y git clang cmake openssl-devel xxhash-devel zlib-devel libstdc++-devel
```
### Compile mold
```shell
git clone https://github.com/rui314/mold.git
cd mold
git checkout v1.2.0
make -j$(nproc) CXX=clang++
sudo make install
```
You may need to pass a C++20 compiler command name to `make`.
In the above case, `clang++` is passed. If it doesn't work for you,
try a specific version of a compiler such as `g++-10` or `clang++-12`.
By default, `mold` is installed to `/usr/local/bin`.
If you don't use a recent enough Linux distribution, or if for any reason
`make` in the above commands doesn't work for you, you can use Docker to
build it in a Docker environment. To do so, just run `./dist.sh` in this
directory instead of running `make -j$(nproc)`. The shell script creates a
Ubuntu 18.04 Docker image, installs necessary tools and libraries to it,
builds mold and auxiliary files, and packs them into a single tar file
`mold-$version-$arch-linux.tar.gz`. You can extract the tar file anywhere
and use `mold` executable in it.
`make test` depends on a few more packages. To install, run the following commands:
```
$ sudo dpkg --add-architecture i386
$ sudo apt-get install dwarfdump libc6-dev:i386 lib32gcc-10-dev gcc-multilib
```shell
sudo dpkg --add-architecture i386
sudo apt update
sudo apt-get install bsdmainutils dwarfdump libc6-dev:i386 lib32gcc-10-dev libstdc++-10-dev-arm64-cross gcc-10-aarch64-linux-gnu g++-10-aarch64-linux-gnu
```
## How to use
On Unix, the linker command (which is usually `/usr/bin/ld`) is
invoked indirectly by `cc` (or `gcc` or `clang`), which is typically
in turn indirectly invoked by `make` or some other build system
command. It is sometimes very hard to pass an appropriate command line
option to `cc` to specify an alternative linker.
<details><summary>A classic way to use mold</summary>
To deal with the situation, mold has a feature to intercept all
invocations of `/usr/bin/ld`, `/usr/bin/ld.lld` or `/usr/bin/ld.gold`
and redirect it to itself. To use the feature, run `make` (or other
build command) as a subcommand of mold as follows:
On Unix, the linker command (which is usually `/usr/bin/ld`) is
invoked indirectly by the compiler driver (which is usually `cc`,
`gcc` or `clang`), which is typically in turn indirectly invoked by
`make` or some other build system command.
If you can specify an additional command line option to your compiler
driver by modifying build system's config files, add one of the
following flags to use `mold` instead of `/usr/bin/ld`:
- Clang: pass `-fuse-ld=mold`
- GCC 12.1.0 (upcoming version) or later: pass `-fuse-ld=mold`
- GCC before 12.1.0: `-fuse-ld` does not accept `mold` as a valid
argument, so you need to use `-B` option instead. `-B` is an option
to tell GCC where to look for external commands such as `ld`.
If you have installed mold with `make install`, there should be a
directory named `/usr/libexec/mold` (or `/usr/local/libexec/mold`,
depending on your $PREFIX), and `ld` command should be there. The
`ld` is actually a symlink to `mold`. So, all you need is to pass
`-B/usr/libexec/mold` (or `-B/usr/local/libexec/mold`) to GCC.
If you haven't installed `mold` to any `$PATH`, you can still pass
`-fuse-ld=/absolute/path/to/mold` to clang to use mold. GCC does not
take an absolute path as an argument for `-fuse-ld` though.
</details>
<details><summary>If you are using Rust</summary>
Create `.cargo/config.toml` in your project directory with the following:
```
$ path/to/mold -run make <make-options-if-any>
[target.x86_64-unknown-linux-gnu]
linker = "clang"
rustflags = ["-C", "link-arg=-fuse-ld=/path/to/mold"]
```
where `/path/to/mold` is an absolute path to `mold` exectuable.
Please make sure you have installed `clang`.
If you want to use mold for all projects, put the above snippet to
`~/.cargo/config.toml`.
</details>
<details><summary>mold -run</summary>
It is sometimes very hard to pass an appropriate command line option
to `cc` to specify an alternative linker. To deal with the situation,
mold has a feature to intercept all invocations of `ld`, `ld.lld` or
`ld.gold` and redirect it to itself. To use the feature, run `make`
(or another build command) as a subcommand of mold as follows:
```shell
mold -run make <make-options-if-any>
```
Internally, mold invokes a given command with `LD_PRELOAD` environment
variable set to its companion shared object file. The shared object
file intercepts all function calls to exec-family functions to replace
`argv[0]` with `mold` if it is `/usr/bin/ld`, `/usr/bin/ld.gold` or
`/usr/bin/ld.lld`.
file intercepts all function calls to `exec(3)`-family functions to
replace `argv[0]` with `mold` if it is `ld`, `ld.gold` or `ld.lld`.
Alternatively, you can pass `-fuse-ld=<absolute-path-to-mold-executable>`
to a linker command line. Since GCC doesn't support that option,
I recommend using clang.
</details>
<details><summary>GitHub Action</summary>
If you want to use mold in your GitHub-hosted CI to speed up continuous
build, you can use <a href=https://github.com/rui314/setup-mold>setup-mold</a>
GitHub Action. GitHub runs a CI on a two-core machine, but mold is
still significantly faster than the default GNU linker there
especially when a program being linked is large.
</details>
<details><summary>Verify that you are using mold</summary>
mold leaves its identification string in `.comment` section in an output
file. You can print it out to verify that you are actually using mold.
```
$ readelf -p .comment <executable-file>
```shell
readelf -p .comment <executable-file>
String dump of section '.comment':
[ 0] GCC: (Ubuntu 10.2.0-5ubuntu1~20.04) 10.2.0
@@ -119,503 +193,27 @@ String dump of section '.comment':
If `mold` is in `.comment`, the file is created by mold.
# Design and implementation of mold
</details>
For the rest of this documentation, I'll explain the design and the
implementation of mold. If you are only interested in using mold, you
don't need to read the below.
## Why is mold so fast?
## Motivation
One reason is because it simply uses faster algorithms and efficient
data structures than other linkers do. The other reason is that the
new linker is highly parallelized.
Here is why I'm writing a new linker:
Here is a side-by-side comparison of per-core CPU usage of lld (left)
and mold (right). They are linking the same program, Chromium
executable.
- Even though lld has significantly improved the situation, linking is
still one of the slowest steps in a build. It is especially
annoying when I changed one line of code and had to wait for a few
seconds or even more for a linker to complete. It should be
instantaneous. There's a need for a faster linker.
![CPU usage comparison in htop animation](docs/htop.gif)
- The number of cores on a PC has increased a lot lately, and this
trend is expected to continue. However, the existing linkers can't
take the advantage of the trend because they don't scale well for more
cores. I have a 64-core/128-thread machine, so my goal is to create
a linker that uses the CPU nicely. mold should be much faster than
other linkers on 4 or 8-core machines too, though.
As you can see, mold uses all available cores throughout its execution
and finishes quickly. On the other hand, lld failed to use available
cores most of the time. In this demo, the maximum parallelism is
artificially capped to 16 so that the bars fit in the GIF.
- It looks to me that the designs of the existing linkers are somewhat
too similar, and I believe there are a lot of drastically different
designs that haven't been explored yet. Developers generally don't
care about linkers as long as they work correctly, and they don't
even think about creating a new one. So there may be lots of low
hanging fruits there in this area.
For details, please read [design notes](docs/design.md).
## Basic design
# Logo
- In order to achieve a `cp`-like performance, the most important
thing is to fix the layout of an output file as quickly as possible, so
that we can start copying actual data from input object files to an
output file as soon as possible.
- Copying data from input files to an output file is I/O-bounded, so
there should be room for doing computationally-intensive tasks while
copying data from one file to another.
- We should allow the linker to preload object files from disk and
parse them in memory before a complete set of input object files
is ready. To do so, we need
to split the linker into two in such a way that the latter half of
the process finishes as quickly as possible by speculatively parsing
and preprocessing input files in the first half of the process.
- One of the most computationally-intensive stage among linker stages
is symbol resolution. To resolve symbols, we basically have to throw
all symbol strings into a hash table to match undefined symbols with
defined symbols. But this can be done in the preloading stage using
[string interning](https://en.wikipedia.org/wiki/String_interning).
- Object files may contain a special section called a mergeable string
section. The section contains lots of null-terminated strings, and
the linker is expected to gather all mergeable string sections and
merge their contents. So, if two object files contain the same
string literal, for example, the resulting output will contain a
single merged string. This step is computationally-intensive, but string
merging can be done in the preloading stage using string interning.
- Static archives (.a files) contain object files, but the static
archive's string table contains only defined symbols of member
object files and lacks other types of symbols. That makes static
archives unsuitable for speculative parsing. Therefore, the linker
should ignore the symbol table of static archive and directly read
static archive members.
- If there's a relocation that uses a GOT of a symbol, then we have to
create a GOT entry for that symbol. Otherwise, we shouldn't. That
means we need to scan all relocation tables to fix the length and
the contents of a .got section. This is computationally intensive,
but this step is parallelizable.
## Compatibility
- GNU ld, GNU gold and LLVM lld support essentially the same set of
command line options and features. mold doesn't have to be
completely compatible with them. As long as it can be used for
linking large user-land programs, I'm fine with that. It is OK to
leave some command line options unimplemented; if mold is blazingly
fast, other projects would still be happy to adopt it by modifying
their projects' build files.
- mold emits Linux executables and runs only on Linux. I won't avoid
Unix-ism when writing code. I don't want to think about portability
until mold becomes a thing that's worth to be ported.
## Linker Script
Linker script is an embedded language for the linker. It is mainly
used to control how input sections are mapped to output sections and
the layout of the output, but it can also do a lot of tricky stuff.
Its feature is useful especially for embedded programming, but it's
also an awfully underdocumented and complex language.
We have to implement a subset of the linker script language anwyay,
because on Linux, /usr/lib/x86_64-linux-gnu/libc.so is (despite its
name) not a shared object file but actually an ASCII file containing
linker script code to load the _actual_ libc.so file. But the feature
set for this purpose is very limited, and it is okay to implement them
to mold.
Besides that, we really don't want to implement the linker script
langauge. But at the same time, we want to satisfy the user needs that
are currently satisfied with the linker script langauge. So, what
should we do? Here is my observation:
- Linker script allows to do a lot of tricky stuff, such as specifying
the exact layout of a file, inserting arbitrary bytes between
sections, etc. But most of them can be done with a post-link binary
editing tool (such as `objcopy`).
- It looks like there are two things that truely cannot be done by a
post-link editing tool: (a) mapping input sections to output
sections, and (b) applying relocations.
From the above observation, I believe we need to provide only the
following features instead of the entire linker script langauge:
- A method to specify how input sections are mapped to output
sections, and
- a method to set addresses to output sections, so that relocations
are applied based on desired adddresses.
I believe everything else can be done with a post-link binary editing
tool.
## Details
- As we aim to the 1 second goal for Chromium, every millisecond
counts. We can't ignore the latency of process exit. If we mmap a
lot of files, \_exit(2) is not instantaneous but takes a few hundred
milliseconds because the kernel has to clean up a lot of
resources. As a workaround, we should organize the linker command as
two processes; the first process forks the second process, and the
second process does the actual work. As soon as the second process
writes a result file to a filesystem, it notifies the first process,
and the first process exits. The second process can take time to
exit, because it is not an interactive process.
- At least on Linux, it looks like the filesystem's performance to
allocate new blocks to a new file is the limiting factor when
creating a new large file and filling its contents using mmap.
If you already have a large file in the buffer cache, writing to it is
much faster than creating a new fresh file and writing to it.
Based on this observation, mold overwrites to an existing
executable file if exists. My quick benchmark showed that I could
save 300 milliseconds when creating a 2 GiB output file.
Linux doesn't allow to open an executable for writing if it is
running (you'll get "text busy" error if you attempt). mold
falls back to the usual way if it fails to open an output file.
- The output from the linker should be deterministic for the sake of
[build reproducibility](https://en.wikipedia.org/wiki/Reproducible_builds)
and ease of debugging. This might add a little bit of overhead to
the linker, but that shouldn't be too much.
- A .build-id, a unique ID embedded to an output file, is usually
computed by applying a cryptographic hash function (e.g. SHA-1) to
an output file. This is a slow step, but we can speed it up by
splitting a file into small chunks, computing SHA-1 for each chunk,
and then computing SHA-1 of the concatenated SHA-1 hashes
(i.e. constructing a [Markle
Tree](https://en.wikipedia.org/wiki/Merkle_tree) of height 2).
Modern x86 processors have purpose-built instructions for SHA-1 and
can compute SHA-1 pretty quickly at about 2 GiB/s rate. Using 16
cores, a build-id for a 2 GiB executable can be computed in 60 to 70
milliseconds.
- BFD, gold, and lld support section garbage collection. That is, a
linker runs a mark-sweep garbage collection on an input graph, where
sections are vertices and relocations are edges, to discard all
sections that are not reachable from the entry point symbol
(i.e. `_start`) or a few other root sections. In mold, we are using
multiple threads to mark sections concurrently.
- Similarly, BFD, gold an lld support Identical Comdat Folding (ICF)
as a yet another size optimization. ICF merges two or more read-only
sections that happen to have the same contents and relocations.
To do that, we have to find isomorphic subgraphs from larger graphs.
I implemented a new algorithm for mold, which is 5x faster than lld
to do ICF for Chromium (from 5 seconds to 1 second).
- [Intel Threading Building
Blocks](https://github.com/oneapi-src/oneTBB) (TBB) is a good
library for parallel execution and has several concurrent
containers. We are particularly interested in using
`parallel_for_each` and `concurrent_hash_map`.
- TBB provides `tbbmalloc` which works better for multi-threaded
applications than the glib'c malloc, but it looks like
[jemalloc](https://github.com/jemalloc/jemalloc) and
[mimalloc](https://github.com/microsoft/mimalloc) are a little bit
more scalable than `tbbmalloc`.
## Size of the problem
When linking Chrome, a linker reads 3,430,966,844 bytes of data in
total. The data contains the following items:
| Data item | Number
| ------------------------ | ------
| Object files | 30,723
| Public undefined symbols | 1,428,149
| Mergeable strings | 1,579,996
| Comdat groups | 9,914,510
| Regular sections¹ | 10,345,314
| Public defined symbols | 10,512,135
| Symbols | 23,953,607
| Sections | 27,543,225
| Relocations against SHF_ALLOC sections | 39,496,375
| Relocations | 62,024,719
¹ Sections that have to be copied from input object files to an
output file. Sections that contain relocations or symbols are for
example excluded.
## Internals
In this section, I'll explain the internals of mold linker.
### A brief history of Unix and the Unix linker
Conceptually, what a linker does is pretty simple. A compiler compiles
a fragment of a program (a single source file) into a fragment of
machine code and data (an object file, which typically has the .o
extension), and a linker stiches them together into a single
executable or a shared library image.
In reality, modern linkers for Unix-like systems are much more
compilcated than the naive understanding because they have gradually
gained one feature at a time over the 50 years history of Unix, and
they are now something like a bag of lots of miscellaneous features in
which none of the features is more important than the others. It is
very easy to miss the forest for the trees, since for those who don't
know the details of the Unix linker, it is not clear which feature is
essential and which is not.
That being said, one thing is clear that at any point of Unix history,
a Unix linker has a coherent feature set for the Unix of that age. So,
let me entangle the history to see how the operating system, runtime
and linker have gained features that we see today. That should give
you an idea why a particular feature has been added to a linker in the
first place.
1. Original Unix didn't support shared library, and a program was
always loaded to a fixed address. An executable was something like
a memory dump which was just loaded to a particular address by the
kernel. After loading, the kernel started executing the program by
setting the instruction pointer to a particular address.
The most essential feature for any linker is relocation processing.
The original Unix linker of course supported that. Let me explain
what that is.
Individual object files are inevitably incomplete as a program,
because when a compiler created them, it only see a part of an
entire program. For example, if an object file contains a function
call that refers other object file, the `call` instruction in the
object cannot be complete, as the compiler has no idea as to what
is the called function's address. To deal with this, the compiler
emits a placeholder value (typically just zero) instead of a real
address and leave a metadata in an object file saying "fix offset X
of this file with an address of Y". That metadata is called
"relocation". Relocations are typically processed by the linker.
It is easy for a linker to apply relocations for the original Unix
because a program is always loaded to a fixed address. It exactly
knows the addresses of all functions and data when linking a
program.
Static library support, which is still an important feature of Unix
linker, also dates back to this early period of Unix history.
To understand what it is, imagine that you are trying to compile
a program for the early Unix. You don't want to waste time to
compile libc functions every time you compile your program (the
computers of the era was incredibly slow), so you have already
placed each libc function into a separate source file and compiled
them individually. That means, you have object files for each libc
function, e.g., printf.o, scanf.o, atoi.o, write.o, etc.
Given this configuration, all you have to do to link your program
against libc functions is to pick up a right set of libc object
files and give them to the linker along with the object files of your
program. But, keeping the linker command line in sync with the
libc functions you are using in your program is bothersome. You can
be conservative; you can specify all libc object files to the
command line, but that leads to program bloat because the linker
unconditionally link all object files given to it no matter whether
they are used or not. So, a new feature was added to the linker to
fix the problem. That is the static library, which is also called
the archive file.
An archive file is just a bundle of object files, just like zip
file but in an uncompressed form. An achive file typically has the
.a file extension and named after its contents. For example, the
archive file containing all libc objects is named `libc.a`.
If you pass an archive file along with other object files to the
linker, the linker pulls out an object file from the archive _only
when_ it is referenced by other object files. In other words,
unlike object files directly given to a linker, object files
wrapped in an archive are not linked to an output by default.
An archive works as supplements to complete your program.
Even today, you can still find a libc archive file. Run `ar t
/usr/lib/x86_64-linux-gnu/libc.a` on Linux should give you a list
of object files in the libc archive.
2. In '80s, Sun Microsystems, a leading commercial Unix vendor at the
time, added a shared library support to their Unix variant, SunOS.
(This section is incomplete.)
## Concurrency strategy
In this section, I'll explain the high level concurrency strategy of
mold.
In most places, mold adopts data parallelism. That is, we have a huge
number of piece of data of the same kind, and we process each of them
individually using parallel for-loop. For example, after identifying
the exact set of input object files, we need to scan all relocation
tables to determine the sizes of .got and .plt sections. We do that
using a parallel for-loop. The granularity of parallel processing in
this case is the relocation table.
Data parallelism is very efficient and scalable because there's no
need for threads to communicate with each other while working on each
element of data. In addition to that, data parallelism is easy to
understand, as it is just a for-loop in which multiple iterations may
be executed in parallel. We don't use high-level communication or
synchronization mechanisms such as channels, futures, promises,
latches or something like that in mold.
In some cases, we need to share a little bit of data between threads
while executing a parallel for-loop. For example, the loop to scan
relocations turns on "requires GOT" or "requires PLT" flags in a
symbol. Symbol is a shared resource, and writing to them from multiple
threads without synchronization is unsafe. To deal with it, we made
the flag an atomic variable.
The other common pattern you can find in mold which is build on top of
the parallel for-loop is the map-reduce pattern. That is, we run a
parallel for-loop on a large data set to produce a small data set and
process the small data set with a single thread. Let me take a
build-id computation as an example. Build-id is typically computed by
applying a cryptographic hash function such as SHA-1 on a linker's
output file. To compute it, we first consider an output as a sequence
of 1 MiB blocks and compute a SHA-1 hash for each block in parallel.
Then, we concatenate the SHA-1 hashes and compute a SHA-1 hash on the
hashes to get a final build-id.
Finally, we use concurrent hashmap at a few places in mold. Concurrent
hashmap is a hashmap to which multiple threads can safely insert items
in parallel. We use it in the symbol resolution stage, for example.
To resolve symbols, we basically have to throw in all defined symbols
into a hash table, so that we can find a matching defined symbol for
an undefined symbol by name. We do the hash table insertion from a
parallel for-loop which iterates over a list of input files.
Overall, even though mold is highly scalable, it succeeded to avoid
complexties you often find in complex parallel programs. From high
level, mold just serially executes linker's internal passes one by
one. Each pass is parallelized using parallel for-loops.
## Rejected ideas
In this section, I'll explain the alternative designs I currently do
not plan to implement and why I turned them down.
- Placing variable-length sections at end of an output file and start
copying file contents before fixing the output file layout
Idea: Fixing the layout of regular sections seems easy, and if we
place them at beginning of a file, we can start copying their
contents from their input files to an output file. While copying
file contents, we can compute the sizes of variable-length sections
such as .got or .plt and place them at end of the file.
Reason for rejection: I did not choose this design because I doubt
if it could actually shorten link time and I think I don't need it
anyway.
The linker has to de-duplicate comdat sections (i.e. inline
functions that are included into multiple object files), so we
cannot compute the layout of regular sections until we resolve all
symbols and de-duplicate comdats. That takes a few hundred
milliseconds. After that, we can compute the sizes of
variable-length sections in less than 100 milliseconds. It's quite
fast, so it doesn't seem to make much sense to proceed without
fixing the final file layout.
The other reason to reject this idea is because there's good a
chance for this idea to have a negative impact on linker's overall
performance. If we copy file contents before fixing the layout, we
can't apply relocations to them while copying because symbol
addresses are not available yet. If we fix the file layout first, we
can apply relocations while copying, which is effectively zero-cost
due to a very good data locality. On the other hand, if we apply
relocations long after we copy file contents, it's pretty expensive
because section contents are very likely to have been evicted from
CPU cache.
- Incremental linking
Idea: Incremental linking is a technique to patch a previous linker's
output file so that only functions or data that are updated from the
previous build are written to it. It is expected to significantly
reduce the amount of data copied from input files to an output file
and thus speed up linking. GNU BFD and gold linkers support it.
Reason for rejection: I turned it down because it (1) is
complicated, (2) doesn't seem to speed it up that much and (3) has
several practical issues. Let me explain each of them.
First, incremental linking for real C/C++ programs is not as easy as
one might think. Let me take malloc as an example. malloc is usually
defined by libc, but you can implement it in your program, and if
that's the case, the symbol `malloc` will be resolved to your
function instead of the one in libc. If you include a library that
defines malloc (such as libjemalloc or libtbbmallc) before libc,
their malloc will override libc's malloc.
Assume that you are using a nonstandard malloc. What if you remove
your malloc from your code, or remove `-ljemalloc` from your
Makefile? The linker has to include a malloc from libc, which may
include more object files to satisfy its dependencies. Such code
change can affect the entire program rather than just replacing one
function. The same is true to adding malloc to your program. Making
a local change doesn't necessarily result in a local change in the
binary level. It can easily have cascading effects.
Some ELF fancy features make incremental linking even harder to
implement. Take the weak symbol as an example. If you define `atoi`
as a weak symbol in your program, and if you are not using `atoi`
at all in your program, that symbol will be resolved to address
0. But if you start using some libc function that indirectly calls
`atoi`, then `atoi` will be included to your program, and your weak
symbol will be resolved to that function. I don't know how to
efficiently fix up a binary for this case.
This is a hard problem, so existing linkers don't try too hard to
solve it. For example, IIRC, gold falls back to full link if any
function is removed from a previous build. If you want to not annoy
users in the fallback case, you need to make full link fast anyway.
Second, incremental linking itself has an overhead. It has to detect
updated files, patch an existing output file and write additional
data to an output file for future incremental linking. GNU gold, for
instance, takes almost 30 seconds on my machine to do a null
incremental link (i.e. no object files are updated from a previous
build) for chrome. It's just too slow.
Third, there are other practical issues in incremental linking. It's
not reproducible, so your binary isn't going to be the same as other
binaries even if you are compiling the same source tree using the
same compiler toolchain. Or, it is complex and there might be a bug
in it. If something doesn't work correctly, "remove --incremental
from your Makefile and try again" could be a piece of advise, but
that isn't ideal.
So, all in all, incremental linking is tricky. I wanted to make full
link as fast as possible, so that we don't have to think about how
to workaround the slowness of full link.
- Defining a completely new file format and use it
Idea: Sometimes, the ELF file format itself seems to be a limiting
factor of improving linker's performance. We might be able to make a
far better one if we create a new file format.
Reason for rejection: I rejected the idea because it apparently has
a practical issue (backward compatibility issue) and also doesn't
seem to improve performance of linkers that much. As clearly
demonstrated by mold, we can create a fast linker for ELF. I believe
ELF isn't that bad, after all. The semantics of the existing Unix
linkers, such as the name resolution algorithm or the linker script,
have slowed the linkers down, but that's not a problem of the file
format itself.
- Watching object files using inotify(2)
Idea: When mold is running as a daemon for preloading, use
inotify(2) to watch file system updates so that it can reload files
as soon as they are updated.
Reason for rejection: Just like the maximum number of files you can
simultaneously open, the maximum number of files you can watch using
inotify(2) isn't that large. Maybe just a single instance of mold is
fine with inotify(2), but it may fail if you run multiple of it.
The other reason for not doing it is because mold is quite fast
without it anyway. Invoking stat(2) on each file for file update
check takes less than 100 milliseconds for Chrome, and if most of
the input files are not updated, parsing updated files takes almost
no time.
-![mold image](docs/mold.jpg)
-441
View File
@@ -1,441 +0,0 @@
#include "mold.h"
enum {
R_TLS_GOTIE = R_END + 1,
R_TLS_LE,
R_TLS_GD,
R_TLS_LD,
R_TLS_GOTDESC,
R_TLS_GOTDESC_RELAX,
R_TLS_DESC_CALL_RELAX,
R_TPOFF,
};
template <>
void PltSection<I386>::copy_buf(Context<I386> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
// Write PLT header
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
};
memcpy(buf, plt0, sizeof(plt0));
} else {
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushl (GOTPLT+4)
0xff, 0x25, 0, 0, 0, 0, // jmp *(GOTPLT+8)
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr + 8;
}
// Write PLT entries
i64 relplt_idx = 0;
for (Symbol<I386> *sym : symbols) {
u8 *ent = buf + sym->get_plt_idx(ctx) * I386::plt_size;
if (ctx.arg.pic) {
static const u8 data[] = {
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0x68, 0, 0, 0, 0, // pushl $reloc_offset
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - ctx.gotplt->shdr.sh_addr;
} else {
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x68, 0, 0, 0, 0, // pushl $reloc_offset
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_gotplt_addr(ctx);
}
*(u32 *)(ent + 7) = relplt_idx++ * sizeof(ElfRel<I386>);
*(u32 *)(ent + 12) = this->shdr.sh_addr - sym->get_plt_addr(ctx) - 16;
}
}
template <>
void PltGotSection<I386>::copy_buf(Context<I386> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
if (ctx.arg.pic) {
static const u8 data[] = {
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0x66, 0x90, // nop
};
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;
}
} else {
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x66, 0x90, // nop
};
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);
}
}
}
template <>
void EhFrameSection<I386>::apply_reloc(Context<I386> &ctx,
ElfRel<I386> &rel,
u64 loc, u64 val) {
u8 *base = ctx.buf + this->shdr.sh_offset;
switch (rel.r_type) {
case R_386_NONE:
return;
case R_386_32:
*(u32 *)(base + loc) = val;
return;
case R_386_PC32:
*(u32 *)(base + loc) = val - this->shdr.sh_addr - loc;
return;
}
unreachable(ctx);
}
static void write_val(Context<I386> &ctx, u64 r_type, u8 *loc, u64 val) {
switch (r_type) {
case R_386_NONE:
return;
case R_386_8:
case R_386_PC8:
*loc += val;
return;
case R_386_16:
case R_386_PC16:
*(u16 *)loc += val;
return;
case R_386_32:
case R_386_PC32:
case R_386_GOT32:
case R_386_GOT32X:
case R_386_PLT32:
case R_386_GOTOFF:
case R_386_GOTPC:
case R_386_TLS_LDM:
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_GD:
case R_386_TLS_LDO_32:
case R_386_SIZE32:
case R_386_TLS_GOTDESC:
*(u32 *)loc += val;
return;
}
unreachable(ctx);
}
template <>
void InputSection<I386>::apply_reloc_alloc(Context<I386> &ctx, u8 *base) {
ElfRel<I386> *dynrel = nullptr;
std::span<ElfRel<I386>> rels = get_rels(ctx);
i64 frag_idx = 0;
if (ctx.reldyn)
dynrel = (ElfRel<I386> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<I386> &rel = rels[i];
Symbol<I386> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
const SectionFragmentRef<I386> *ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
ref = &rel_fragments[frag_idx++];
auto write = [&](u64 val) {
write_val(ctx, rel.r_type, loc, val);
};
#define S (ref ? ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (ref ? ref->addend : 0)
#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
switch (rel_types[i]) {
case R_NONE:
break;
case R_ABS:
write(S + A);
break;
case R_BASEREL:
*dynrel++ = {P, R_386_RELATIVE, 0};
*(u32 *)loc += S + A;
break;
case R_DYN:
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
*(u32 *)loc += A;
break;
case R_PC:
write(S + A - P);
break;
case R_GOT:
write(sym.get_got_addr(ctx) + A - GOTPLT);
break;
case R_GOTOFF:
write(S + A - GOTPLT);
break;
case R_GOTPC:
write(GOTPLT + A - P);
break;
case R_GOTPCREL:
write(G + GOTPLT + A - P);
break;
case R_TLS_GOTIE:
write(sym.get_gottp_addr(ctx) + A - GOTPLT);
break;
case R_TLS_LE:
write(S + A - ctx.tls_end);
break;
case R_TLS_GD:
write(sym.get_tlsgd_addr(ctx) + A - GOTPLT);
break;
case R_TLS_LD:
write(ctx.got->get_tlsld_addr(ctx) + A - GOTPLT);
break;
case R_TPOFF:
write(S + A - ctx.tls_begin);
break;
case R_TLS_GOTDESC:
write(sym.get_tlsdesc_addr(ctx) + A - GOTPLT);
break;
case R_TLS_GOTDESC_RELAX: {
static const u8 insn[] = {
0x8d, 0x05, 0, 0, 0, 0, // lea 0, %eax
};
memcpy(loc - 2, insn, sizeof(insn));
write(S + A - ctx.tls_end);
break;
}
case R_TLS_DESC_CALL_RELAX:
// call *(%rax) -> nop
loc[0] = 0x66;
loc[1] = 0x90;
break;
case R_SIZE:
write(sym.esym().st_size + A);
break;
default:
unreachable(ctx);
}
#undef S
#undef A
#undef P
#undef G
#undef GOTPLT
}
}
template <>
void InputSection<I386>::apply_reloc_nonalloc(Context<I386> &ctx, u8 *base) {
std::span<ElfRel<I386>> rels = get_rels(ctx);
i64 frag_idx = 0;
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<I386> &rel = rels[i];
if (rel.r_type == R_386_NONE)
continue;
Symbol<I386> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, sym);
continue;
}
const SectionFragmentRef<I386> *ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
ref = &rel_fragments[frag_idx++];
auto write = [&](u64 val) {
write_val(ctx, rel.r_type, loc, val);
};
switch (rel.r_type) {
case R_386_8:
case R_386_16:
case R_386_32:
case R_386_PC8:
case R_386_PC16:
case R_386_PC32:
case R_386_GOTPC:
if (ref)
write(ref->frag->get_addr(ctx) + ref->addend);
else
write(sym.get_addr(ctx));
break;
case R_386_GOTOFF:
write(sym.get_addr(ctx) - ctx.got->shdr.sh_addr);
break;
case R_386_TLS_LDO_32:
write(sym.get_addr(ctx) - ctx.tls_begin);
break;
case R_386_SIZE32:
write(sym.esym().st_size);
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel_to_string<I386>(rel.r_type);
break;
}
}
}
template <>
void InputSection<I386>::scan_relocations(Context<I386> &ctx) {
assert(shdr.sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<I386>);
std::span<ElfRel<I386>> rels = get_rels(ctx);
bool is_writable = (shdr.sh_flags & SHF_WRITE);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<I386> &rel = rels[i];
if (rel.r_type == R_386_NONE) {
rel_types[i] = R_NONE;
break;
}
Symbol<I386> &sym = *file.symbols[rel.r_sym];
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (!sym.file) {
report_undef(ctx, sym);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
switch (rel.r_type) {
case R_386_8:
case R_386_16: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, R_ABS, i);
break;
}
case R_386_32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, DYNREL, PLT }, // PDE
};
dispatch(ctx, table, R_ABS, i);
break;
}
case R_386_PC8:
case R_386_PC16: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, R_PC, i);
break;
}
case R_386_PC32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ BASEREL, NONE, ERROR, ERROR }, // DSO
{ BASEREL, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, R_PC, i);
break;
}
case R_386_GOT32:
case R_386_GOT32X:
sym.flags |= NEEDS_GOT;
rel_types[i] = R_GOT;
break;
case R_386_PLT32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
rel_types[i] = R_PC;
break;
case R_386_GOTOFF:
rel_types[i] = R_GOTOFF;
break;
case R_386_GOTPC:
sym.flags |= NEEDS_GOT;
rel_types[i] = R_GOTPC;
break;
case R_386_TLS_GOTIE:
sym.flags |= NEEDS_GOTTP;
rel_types[i] = R_TLS_GOTIE;
break;
case R_386_TLS_LE:
sym.flags |= NEEDS_GOTTP;
rel_types[i] = R_TLS_LE;
break;
case R_386_TLS_GD:
sym.flags |= NEEDS_TLSGD;
rel_types[i] = R_TLS_GD;
break;
case R_386_TLS_LDM:
sym.flags |= NEEDS_TLSLD;
rel_types[i] = R_TLS_LD;
break;
case R_386_TLS_LDO_32:
rel_types[i] = R_TPOFF;
break;
case R_386_SIZE32:
rel_types[i] = R_SIZE;
break;
case R_386_TLS_GOTDESC:
if (ctx.arg.relax && !ctx.arg.shared) {
rel_types[i] = R_TLS_GOTDESC_RELAX;
} else {
sym.flags |= NEEDS_TLSDESC;
rel_types[i] = R_TLS_GOTDESC;
}
break;
case R_386_TLS_DESC_CALL:
if (ctx.arg.relax && !ctx.arg.shared)
rel_types[i] = R_TLS_DESC_CALL_RELAX;
else
rel_types[i] = R_NONE;
break;
default:
Error(ctx) << *this << ": unknown relocation: "
<< rel_to_string<I386>(rel.r_type);
}
}
}
-687
View File
@@ -1,687 +0,0 @@
#include "mold.h"
enum {
R_GOTPCRELX_RELAX = R_END + 1,
R_REX_GOTPCRELX_RELAX,
R_TLSGD,
R_TLSGD_RELAX_LE,
R_TLSLD,
R_TLSLD_RELAX_LE,
R_DTPOFF,
R_DTPOFF_RELAX,
R_TPOFF,
R_GOTTPOFF,
R_GOTTPOFF_RELAX,
R_GOTPC_TLSDESC,
R_GOTPC_TLSDESC_RELAX_LE,
R_TLSDESC_CALL_RELAX,
};
template <>
void PltSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
// Write PLT header
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip)
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
0x0f, 0x1f, 0x40, 0x00, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr + 2;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr + 4;
// Write PLT entries
i64 relplt_idx = 0;
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
0x68, 0, 0, 0, 0, // push $index_in_relplt
0xe9, 0, 0, 0, 0, // jmp PLT[0]
};
for (Symbol<X86_64> *sym : symbols) {
u8 *ent = buf + sym->get_plt_idx(ctx) * X86_64::plt_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
*(u32 *)(ent + 7) = relplt_idx++;
*(u32 *)(ent + 12) = this->shdr.sh_addr - sym->get_plt_addr(ctx) - 16;
}
}
template <>
void PltGotSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x66, 0x90, // nop
};
for (Symbol<X86_64> *sym : symbols) {
u8 *ent = buf + sym->get_pltgot_idx(ctx) * X86_64::pltgot_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 6;
}
}
template <>
void EhFrameSection<X86_64>::apply_reloc(Context<X86_64> &ctx,
ElfRel<X86_64> &rel,
u64 loc, u64 val) {
u8 *base = ctx.buf + this->shdr.sh_offset;
switch (rel.r_type) {
case R_X86_64_NONE:
return;
case R_X86_64_32:
*(u32 *)(base + loc) = val;
return;
case R_X86_64_64:
*(u64 *)(base + loc) = val;
return;
case R_X86_64_PC32:
*(u32 *)(base + loc) = val - this->shdr.sh_addr - loc;
return;
case R_X86_64_PC64:
*(u64 *)(base + loc) = val - this->shdr.sh_addr - loc;
return;
}
unreachable(ctx);
}
static void overflow_check(Context<X86_64> &ctx, InputSection<X86_64> *sec,
Symbol<X86_64> &sym, u64 r_type, u64 val) {
switch (r_type) {
case R_X86_64_8:
if (val != (u8)val)
Error(ctx) << *sec << ": relocation R_X86_64_8 against " << sym
<< " out of range: " << val << " is not in [0, 255]";
return;
case R_X86_64_PC8:
if (val != (i8)val)
Error(ctx) << *sec << ": relocation R_X86_64_PC8 against " << sym
<< " out of range: " << (i64)val << " is not in [-128, 127]";
return;
case R_X86_64_16:
if (val != (u16)val)
Error(ctx) << *sec << ": relocation R_X86_64_16 against " << sym
<< " out of range: " << val << " is not in [0, 65535]";
return;
case R_X86_64_PC16:
if (val != (i16)val)
Error(ctx) << *sec << ": relocation R_X86_64_PC16 against " << sym
<< " out of range: " << (i64)val
<< " is not in [-32768, 32767]";
return;
case R_X86_64_32:
if (val != (u32)val)
Error(ctx) << *sec << ": relocation R_X86_64_32 against " << sym
<< " out of range: " << val << " is not in [0, 4294967296]";
return;
case R_X86_64_32S:
case R_X86_64_PC32:
case R_X86_64_GOT32:
case R_X86_64_GOTPC32:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_PLT32:
case R_X86_64_TLSGD:
case R_X86_64_TLSLD:
case R_X86_64_TPOFF32:
case R_X86_64_DTPOFF32:
case R_X86_64_GOTTPOFF:
case R_X86_64_GOTPC32_TLSDESC:
case R_X86_64_SIZE32:
case R_X86_64_TLSDESC_CALL:
if (val != (i32)val)
Error(ctx) << *sec << ": relocation " << rel_to_string<X86_64>(r_type)
<< " against " << sym << " out of range: " << (i64)val
<< " is not in [-2147483648, 2147483647]";
return;
case R_X86_64_NONE:
case R_X86_64_64:
case R_X86_64_PC64:
case R_X86_64_TPOFF64:
case R_X86_64_DTPOFF64:
case R_X86_64_GOT64:
case R_X86_64_GOTPCREL64:
case R_X86_64_GOTPC64:
case R_X86_64_SIZE64:
return;
}
unreachable(ctx);
}
static void write_val(Context<X86_64> &ctx, u64 r_type, u8 *loc, u64 val) {
switch (r_type) {
case R_X86_64_NONE:
return;
case R_X86_64_8:
case R_X86_64_PC8:
*loc = val;
return;
case R_X86_64_16:
case R_X86_64_PC16:
*(u16 *)loc = val;
return;
case R_X86_64_32:
case R_X86_64_32S:
case R_X86_64_PC32:
case R_X86_64_GOT32:
case R_X86_64_GOTPC32:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCRELX:
case R_X86_64_REX_GOTPCRELX:
case R_X86_64_PLT32:
case R_X86_64_TLSGD:
case R_X86_64_TLSLD:
case R_X86_64_TPOFF32:
case R_X86_64_DTPOFF32:
case R_X86_64_GOTTPOFF:
case R_X86_64_GOTPC32_TLSDESC:
case R_X86_64_SIZE32:
case R_X86_64_TLSDESC_CALL:
*(u32 *)loc = val;
return;
case R_X86_64_64:
case R_X86_64_PC64:
case R_X86_64_TPOFF64:
case R_X86_64_DTPOFF64:
case R_X86_64_GOT64:
case R_X86_64_GOTPCREL64:
case R_X86_64_GOTPC64:
case R_X86_64_SIZE64:
*(u64 *)loc = val;
return;
}
unreachable(ctx);
}
static u32 relax_gotpcrelx(u8 *loc) {
switch ((loc[0] << 8) | loc[1]) {
case 0xff15: return 0x90e8; // call *0(%rip) -> call 0
case 0xff25: return 0x90e9; // jmp *0(%rip) -> jmp 0
}
return 0;
}
static u32 relax_rex_gotpcrelx(u8 *loc) {
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
case 0x488b05: return 0x488d05; // mov 0(%rip), %rax -> lea 0(%rip), %rax
case 0x488b0d: return 0x488d0d; // mov 0(%rip), %rcx -> lea 0(%rip), %rcx
case 0x488b15: return 0x488d15; // mov 0(%rip), %rdx -> lea 0(%rip), %rdx
case 0x488b1d: return 0x488d1d; // mov 0(%rip), %rbx -> lea 0(%rip), %rbx
case 0x488b25: return 0x488d25; // mov 0(%rip), %rsp -> lea 0(%rip), %rsp
case 0x488b2d: return 0x488d2d; // mov 0(%rip), %rbp -> lea 0(%rip), %rbp
case 0x488b35: return 0x488d35; // mov 0(%rip), %rsi -> lea 0(%rip), %rsi
case 0x488b3d: return 0x488d3d; // mov 0(%rip), %rdi -> lea 0(%rip), %rdi
case 0x4c8b05: return 0x4c8d05; // mov 0(%rip), %r8 -> lea 0(%rip), %r8
case 0x4c8b0d: return 0x4c8d0d; // mov 0(%rip), %r9 -> lea 0(%rip), %r9
case 0x4c8b15: return 0x4c8d15; // mov 0(%rip), %r10 -> lea 0(%rip), %r10
case 0x4c8b1d: return 0x4c8d1d; // mov 0(%rip), %r11 -> lea 0(%rip), %r11
case 0x4c8b25: return 0x4c8d25; // mov 0(%rip), %r12 -> lea 0(%rip), %r12
case 0x4c8b2d: return 0x4c8d2d; // mov 0(%rip), %r13 -> lea 0(%rip), %r13
case 0x4c8b35: return 0x4c8d35; // mov 0(%rip), %r14 -> lea 0(%rip), %r14
case 0x4c8b3d: return 0x4c8d3d; // mov 0(%rip), %r15 -> lea 0(%rip), %r15
}
return 0;
}
static u32 relax_gottpoff(u8 *loc) {
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
case 0x488b05: return 0x48c7c0; // mov 0(%rip), %rax -> mov $0, %rax
case 0x488b0d: return 0x48c7c1; // mov 0(%rip), %rcx -> mov $0, %rcx
case 0x488b15: return 0x48c7c2; // mov 0(%rip), %rdx -> mov $0, %rdx
case 0x488b1d: return 0x48c7c3; // mov 0(%rip), %rbx -> mov $0, %rbx
case 0x488b25: return 0x48c7c4; // mov 0(%rip), %rsp -> mov $0, %rsp
case 0x488b2d: return 0x48c7c5; // mov 0(%rip), %rbp -> mov $0, %rbp
case 0x488b35: return 0x48c7c6; // mov 0(%rip), %rsi -> mov $0, %rsi
case 0x488b3d: return 0x48c7c7; // mov 0(%rip), %rdi -> mov $0, %rdi
case 0x4c8b05: return 0x49c7c0; // mov 0(%rip), %r8 -> mov $0, %r8
case 0x4c8b0d: return 0x49c7c1; // mov 0(%rip), %r9 -> mov $0, %r9
case 0x4c8b15: return 0x49c7c2; // mov 0(%rip), %r10 -> mov $0, %r10
case 0x4c8b1d: return 0x49c7c3; // mov 0(%rip), %r11 -> mov $0, %r11
case 0x4c8b25: return 0x49c7c4; // mov 0(%rip), %r12 -> mov $0, %r12
case 0x4c8b2d: return 0x49c7c5; // mov 0(%rip), %r13 -> mov $0, %r13
case 0x4c8b35: return 0x49c7c6; // mov 0(%rip), %r14 -> mov $0, %r14
case 0x4c8b3d: return 0x49c7c7; // mov 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().
template <>
void InputSection<X86_64>::apply_reloc_alloc(Context<X86_64> &ctx, u8 *base) {
ElfRel<X86_64> *dynrel = nullptr;
std::span<ElfRel<X86_64>> rels = get_rels(ctx);
i64 frag_idx = 0;
if (ctx.reldyn)
dynrel = (ElfRel<X86_64> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<X86_64> &rel = rels[i];
Symbol<X86_64> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
const SectionFragmentRef<X86_64> *ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
ref = &rel_fragments[frag_idx++];
auto write = [&](u64 val) {
overflow_check(ctx, this, sym, rel.r_type, val);
write_val(ctx, rel.r_type, loc, val);
};
#define S (ref ? ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (ref ? ref->addend : rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
switch (rel_types[i]) {
case R_NONE:
break;
case R_ABS:
write(S + A);
break;
case R_BASEREL:
*dynrel++ = {P, R_X86_64_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
break;
case R_DYN:
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
break;
case R_PC:
write(S + A - P);
break;
case R_GOT:
write(G + A);
break;
case R_GOTPC:
write(GOT + A - P);
break;
case R_GOTPCREL:
write(G + GOT + A - P);
break;
case R_GOTPCRELX_RELAX: {
u32 insn = relax_gotpcrelx(loc - 2);
loc[-2] = insn >> 8;
loc[-1] = insn;
write(S + A - P);
break;
}
case R_REX_GOTPCRELX_RELAX: {
u32 insn = relax_rex_gotpcrelx(loc - 3);
loc[-3] = insn >> 16;
loc[-2] = insn >> 8;
loc[-1] = insn;
write(S + A - P);
break;
}
case R_TLSGD:
write(sym.get_tlsgd_addr(ctx) + A - P);
break;
case R_TLSGD_RELAX_LE: {
// 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));
*(u32 *)(loc + 8) = S - ctx.tls_end + A + 4;
i++;
break;
}
case R_TLSLD:
write(ctx.got->get_tlsld_addr(ctx) + A - P);
break;
case R_TLSLD_RELAX_LE: {
// 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));
i++;
break;
}
case R_DTPOFF:
write(S + A - ctx.tls_begin);
break;
case R_DTPOFF_RELAX:
write(S + A - ctx.tls_end);
break;
case R_TPOFF:
write(S + A - ctx.tls_end);
break;
case R_GOTTPOFF:
write(sym.get_gottp_addr(ctx) + A - P);
break;
case R_GOTTPOFF_RELAX: {
u32 insn = relax_gottpoff(loc - 3);
loc[-3] = insn >> 16;
loc[-2] = insn >> 8;
loc[-1] = insn;
write(S + A - ctx.tls_end + 4);
break;
}
case R_GOTPC_TLSDESC:
write(sym.get_tlsdesc_addr(ctx) + A - P);
break;
case R_GOTPC_TLSDESC_RELAX_LE: {
static const u8 insn[] = {
0x48, 0xc7, 0xc0, 0, 0, 0, 0, // mov $0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
write(S + A - ctx.tls_end + 4);
break;
}
case R_SIZE:
write(sym.esym().st_size + A);
break;
case R_TLSDESC_CALL_RELAX:
// call *(%rax) -> nop
loc[0] = 0x66;
loc[1] = 0x90;
break;
default:
unreachable(ctx);
}
#undef S
#undef A
#undef P
#undef G
#undef GOT
}
}
// This function is responsible for applying relocations against
// non-SHF_ALLOC sections (i.e. sections that are not mapped to memory
// at runtime).
//
// Relocations against non-SHF_ALLOC sections are much easier to
// handle than that against SHF_ALLOC sections. It is because, since
// they are not mapped to memory, they don't contain any variable or
// function and never need PLT or GOT. Non-SHF_ALLOC sections are
// mostly debug info sections.
//
// Relocations against non-SHF_ALLOC sections are not scanned by
// scan_relocations.
template <>
void InputSection<X86_64>::apply_reloc_nonalloc(Context<X86_64> &ctx, u8 *base) {
std::span<ElfRel<X86_64>> rels = get_rels(ctx);
i64 frag_idx = 0;
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<X86_64> &rel = rels[i];
if (rel.r_type == R_X86_64_NONE)
continue;
Symbol<X86_64> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, sym);
continue;
}
const SectionFragmentRef<X86_64> *ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
ref = &rel_fragments[frag_idx++];
auto write = [&](u64 val) {
overflow_check(ctx, this, sym, rel.r_type, val);
write_val(ctx, rel.r_type, loc, val);
};
switch (rel.r_type) {
case R_X86_64_8:
case R_X86_64_16:
case R_X86_64_32:
case R_X86_64_32S:
case R_X86_64_64:
if (ref)
write(ref->frag->get_addr(ctx) + ref->addend);
else
write(sym.get_addr(ctx) + rel.r_addend);
break;
case R_X86_64_DTPOFF32:
case R_X86_64_DTPOFF64:
write(sym.get_addr(ctx) + rel.r_addend - ctx.tls_begin);
break;
case R_X86_64_SIZE32:
case R_X86_64_SIZE64:
write(sym.esym().st_size + rel.r_addend);
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel_to_string<X86_64>(rel.r_type);
break;
}
}
}
// Linker has to create data structures in an output file to apply
// some type of relocations. For example, if a relocation refers a GOT
// or a PLT entry of a symbol, linker has to create an entry in .got
// or in .plt for that symbol. In order to fix the file layout, we
// need to scan relocations.
template <>
void InputSection<X86_64>::scan_relocations(Context<X86_64> &ctx) {
assert(shdr.sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<X86_64>);
std::span<ElfRel<X86_64>> rels = get_rels(ctx);
bool is_writable = (shdr.sh_flags & SHF_WRITE);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<X86_64> &rel = rels[i];
if (rel.r_type == R_X86_64_NONE) {
rel_types[i] = R_NONE;
continue;
}
Symbol<X86_64> &sym = *file.symbols[rel.r_sym];
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (!sym.file) {
report_undef(ctx, sym);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
switch (rel.r_type) {
case R_X86_64_8:
case R_X86_64_16:
case R_X86_64_32:
case R_X86_64_32S: {
// Dynamic linker does not support 8, 16 or 32-bit dynamic
// relocations for these types of relocations. We report an
// error if we cannot relocate them even at load-time.
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, R_ABS, i);
break;
}
case R_X86_64_64: {
// Unlike the above, we can use R_X86_64_RELATIVE and R_86_64_64
// relocations.
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, DYNREL, PLT }, // PDE
};
dispatch(ctx, table, R_ABS, i);
break;
}
case R_X86_64_PC8:
case R_X86_64_PC16:
case R_X86_64_PC32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, R_PC, i);
break;
}
case R_X86_64_PC64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ BASEREL, NONE, ERROR, ERROR }, // DSO
{ BASEREL, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, R_PC, i);
break;
}
case R_X86_64_GOT32:
case R_X86_64_GOT64:
sym.flags |= NEEDS_GOT;
rel_types[i] = R_GOT;
break;
case R_X86_64_GOTPC32:
case R_X86_64_GOTPC64:
sym.flags |= NEEDS_GOT;
rel_types[i] = R_GOTPC;
break;
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCREL64:
sym.flags |= NEEDS_GOT;
rel_types[i] = R_GOTPCREL;
break;
case R_X86_64_GOTPCRELX: {
if (rel.r_addend != -4)
Fatal(ctx) << *this << ": bad r_addend for R_X86_64_GOTPCRELX";
if (ctx.arg.relax && !sym.is_imported && sym.is_relative(ctx) &&
relax_gotpcrelx(loc - 2)) {
rel_types[i] = R_GOTPCRELX_RELAX;
} else {
sym.flags |= NEEDS_GOT;
rel_types[i] = R_GOTPCREL;
}
break;
}
case R_X86_64_REX_GOTPCRELX:
if (rel.r_addend != -4)
Fatal(ctx) << *this << ": bad r_addend for R_X86_64_REX_GOTPCRELX";
if (ctx.arg.relax && !sym.is_imported && sym.is_relative(ctx) &&
relax_rex_gotpcrelx(loc - 3)) {
rel_types[i] = R_REX_GOTPCRELX_RELAX;
} else {
sym.flags |= NEEDS_GOT;
rel_types[i] = R_GOTPCREL;
}
break;
case R_X86_64_PLT32:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
rel_types[i] = R_PC;
break;
case R_X86_64_TLSGD: {
if (i + 1 == rels.size())
Fatal(ctx) << *this
<< ": TLSGD reloc must be followed by PLT32 or GOTPCREL";
if (ctx.arg.relax && !ctx.arg.shared && !sym.is_imported) {
rel_types[i++] = R_TLSGD_RELAX_LE;
} else {
sym.flags |= NEEDS_TLSGD;
rel_types[i] = R_TLSGD;
}
break;
}
case R_X86_64_TLSLD:
if (i + 1 == rels.size())
Fatal(ctx) << *this
<< ": TLSGD reloc must be followed by PLT32 or GOTPCREL";
if (sym.is_imported)
Fatal(ctx) << *this << ": TLSLD reloc refers external symbol " << sym;
if (ctx.arg.relax && !ctx.arg.shared) {
rel_types[i++] = R_TLSLD_RELAX_LE;
} else {
sym.flags |= NEEDS_TLSLD;
rel_types[i] = R_TLSLD;
}
break;
case R_X86_64_DTPOFF32:
case R_X86_64_DTPOFF64:
if (sym.is_imported)
Fatal(ctx) << *this << ": DTPOFF reloc refers external symbol " << sym;
if (ctx.arg.relax && !ctx.arg.shared)
rel_types[i] = R_DTPOFF_RELAX;
else
rel_types[i] = R_DTPOFF;
break;
case R_X86_64_TPOFF32:
case R_X86_64_TPOFF64:
rel_types[i] = R_TPOFF;
break;
case R_X86_64_GOTTPOFF:
ctx.has_gottp_rel = true;
if (ctx.arg.relax && !ctx.arg.shared && !sym.is_imported &&
relax_gottpoff(loc - 3)) {
rel_types[i] = R_GOTTPOFF_RELAX;
} else {
sym.flags |= NEEDS_GOTTP;
rel_types[i] = R_GOTTPOFF;
}
break;
case R_X86_64_GOTPC32_TLSDESC:
if (memcmp(loc - 3, "\x48\x8d\x05", 3))
Fatal(ctx) << *this << ": GOTPC32_TLSDESC relocation is used"
<< " against an invalid code sequence";
if (ctx.arg.relax && !ctx.arg.shared) {
rel_types[i] = R_GOTPC_TLSDESC_RELAX_LE;
} else {
sym.flags |= NEEDS_TLSDESC;
rel_types[i] = R_GOTPC_TLSDESC;
}
break;
case R_X86_64_SIZE32:
case R_X86_64_SIZE64:
rel_types[i] = R_SIZE;
break;
case R_X86_64_TLSDESC_CALL:
if (ctx.arg.relax && !ctx.arg.shared)
rel_types[i] = R_TLSDESC_CALL_RELAX;
else
rel_types[i] = R_NONE;
break;
default:
Error(ctx) << *this << ": unknown relocation: "
<< rel_to_string<X86_64>(rel.r_type);
}
}
}
+153
View File
@@ -0,0 +1,153 @@
#pragma once
#include "mold.h"
#include "filetype.h"
namespace mold {
struct ArHdr {
char ar_name[16];
char ar_date[12];
char ar_uid[6];
char ar_gid[6];
char ar_mode[8];
char ar_size[10];
char ar_fmag[2];
bool starts_with(std::string_view s) const {
return std::string_view(ar_name, s.size()) == s;
}
bool is_strtab() const {
return starts_with("// ");
}
bool is_symtab() const {
return starts_with("/ ") || starts_with("/SYM64/ ");
}
std::string read_name(std::string_view strtab, u8 *&ptr) const {
// BSD-style long filename
if (starts_with("#1/")) {
int namelen = atoi(ar_name + 3);
std::string name{(char *)ptr, (size_t)namelen};
ptr += namelen;
if (size_t pos = name.find('\0'))
name = name.substr(0, pos);
return name;
}
// SysV-style long filename
if (starts_with("/")) {
const char *start = strtab.data() + atoi(ar_name + 1);
return {start, (const char *)strstr(start, "/\n")};
}
// Short fileanme
if (const char *end = (char *)memchr(ar_name, '/', sizeof(ar_name)))
return {ar_name, end};
return {ar_name, sizeof(ar_name)};
}
};
template <typename C>
std::vector<MappedFile<C> *>
read_thin_archive_members(C &ctx, MappedFile<C> *mf) {
u8 *begin = mf->data;
u8 *data = begin + 8;
std::vector<MappedFile<C> *> vec;
std::string_view strtab;
while (data < begin + mf->size) {
// Each header is aligned to a 2 byte boundary.
if ((begin - data) % 2)
data++;
ArHdr &hdr = *(ArHdr *)data;
u8 *body = data + sizeof(hdr);
u64 size = atol(hdr.ar_size);
// Read a string table.
if (hdr.is_strtab()) {
strtab = {(char *)body, (size_t)size};
data = body + size;
continue;
}
// Skip a symbol table.
if (hdr.is_symtab()) {
data = body + size;
continue;
}
if (!hdr.starts_with("#1/") && !hdr.starts_with("/"))
Fatal(ctx) << mf->name << ": filename is not stored as a long filename";
std::string name = hdr.read_name(strtab, body);
// Skip if symbol table
if (name == "__.SYMDEF" || name == "__.SYMDEF SORTED")
continue;
std::string path = name.starts_with('/') ?
name : (filepath(mf->name).parent_path() / name).string();
vec.push_back(MappedFile<C>::must_open(ctx, path));
data = body;
}
return vec;
}
template <typename C>
std::vector<MappedFile<C> *>
read_fat_archive_members(C &ctx, MappedFile<C> *mf) {
u8 *begin = mf->data;
u8 *data = begin + 8;
std::vector<MappedFile<C> *> vec;
std::string_view strtab;
while (begin + mf->size - data >= 2) {
if ((begin - data) % 2)
data++;
ArHdr &hdr = *(ArHdr *)data;
u8 *body = data + sizeof(hdr);
u64 size = atol(hdr.ar_size);
data = body + size;
// Read if string table
if (hdr.is_strtab()) {
strtab = {(char *)body, (size_t)size};
continue;
}
// Skip if symbol table
if (hdr.is_symtab())
continue;
// Read the name field
std::string name = hdr.read_name(strtab, body);
// Skip if symbol table
if (name == "__.SYMDEF" || name == "__.SYMDEF SORTED")
continue;
vec.push_back(mf->slice(ctx, name, body - begin, data - body));
}
return vec;
}
template <typename C>
std::vector<MappedFile<C> *>
read_archive_members(C &ctx, MappedFile<C> *mf) {
switch (get_file_type(mf)) {
case FileType::AR:
return read_fat_archive_members(ctx, mf);
case FileType::THIN_AR:
return read_thin_archive_members(ctx, mf);
default:
unreachable();
}
}
} // namespace mold
-118
View File
@@ -1,118 +0,0 @@
#include "mold.h"
struct ArHdr {
char ar_name[16];
char ar_date[12];
char ar_uid[6];
char ar_gid[6];
char ar_mode[8];
char ar_size[10];
char ar_fmag[2];
};
template <typename E>
std::vector<MemoryMappedFile<E> *>
read_thin_archive_members(Context<E> &ctx, MemoryMappedFile<E> *mb) {
u8 *begin = mb->data(ctx);
u8 *data = begin + 8;
std::vector<MemoryMappedFile<E> *> vec;
std::string_view strtab;
while (data < begin + mb->size()) {
// Each header is aligned to a 2 byte boundary.
if ((begin - data) % 2)
data++;
ArHdr &hdr = *(ArHdr *)data;
u8 *body = data + sizeof(hdr);
u64 size = atol(hdr.ar_size);
// Read a string table.
if (!memcmp(hdr.ar_name, "// ", 3)) {
strtab = {(char *)body, size};
data = body + size;
continue;
}
// Skip a symbol table.
if (!memcmp(hdr.ar_name, "/ ", 2)) {
data = body + size;
continue;
}
if (hdr.ar_name[0] != '/')
Fatal(ctx) << mb->name << ": filename is not stored as a long filename";
const char *start = strtab.data() + atoi(hdr.ar_name + 1);
std::string name(start, (const char *)strstr(start, "/\n"));
std::string path = name.starts_with('/') ?
name : std::string(path_dirname(mb->name)) + "/" + name;
vec.push_back(MemoryMappedFile<E>::must_open(ctx, path));
data = body;
}
return vec;
}
template <typename E>
std::vector<MemoryMappedFile<E> *>
read_fat_archive_members(Context<E> &ctx, MemoryMappedFile<E> *mb) {
u8 *begin = mb->data(ctx);
u8 *data = begin + 8;
std::vector<MemoryMappedFile<E> *> vec;
std::string_view strtab;
while (begin + mb->size() - data >= 2) {
if ((begin - data) % 2)
data++;
ArHdr &hdr = *(ArHdr *)data;
u8 *body = data + sizeof(hdr);
u64 size = atol(hdr.ar_size);
data = body + size;
if (!memcmp(hdr.ar_name, "// ", 3)) {
strtab = {(char *)body, size};
continue;
}
if (!memcmp(hdr.ar_name, "/ ", 2) ||
!memcmp(hdr.ar_name, "__.SYMDEF/", 10))
continue;
std::string name;
if (hdr.ar_name[0] == '/') {
const char *start = strtab.data() + atoi(hdr.ar_name + 1);
name = {start, (const char *)strstr(start, "/\n")};
} else {
name = {hdr.ar_name, strchr(hdr.ar_name, '/')};
}
vec.push_back(mb->slice(ctx, name, body - begin, size));
}
return vec;
}
template <typename E>
std::vector<MemoryMappedFile<E> *>
read_archive_members(Context<E> &ctx, MemoryMappedFile<E> *mb) {
switch (get_file_type(ctx, mb)) {
case FileType::AR:
return read_fat_archive_members(ctx, mb);
case FileType::THIN_AR:
return read_thin_archive_members(ctx, mb);
default:
unreachable(ctx);
}
}
#define INSTANTIATE(E) \
template std::vector<MemoryMappedFile<E> *> \
read_fat_archive_members(Context<E> &, MemoryMappedFile<E> *); \
template std::vector<MemoryMappedFile<E> *> \
read_thin_archive_members(Context<E> &, MemoryMappedFile<E> *); \
template std::vector<MemoryMappedFile<E> *> \
read_archive_members(Context<E> &, MemoryMappedFile<E> *);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
+81
View File
@@ -0,0 +1,81 @@
#pragma once
#include <cstdint>
#include <cstring>
#ifdef __BIG_ENDIAN__
#error "mold does not support big-endian hosts"
#endif
namespace mold {
template <typename T>
class BigEndian {
public:
BigEndian() = delete;
operator T() const {
T x;
memcpy(&x, val, sizeof(T));
return bswap(x);
}
BigEndian &operator=(T x) {
x = bswap(x);
memcpy(&val, &x, sizeof(T));
return *this;
}
BigEndian &operator++() {
return *this = *this + 1;
}
BigEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
BigEndian &operator--() {
return *this = *this - 1;
}
BigEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
BigEndian &operator+=(T x) {
return *this = *this + x;
}
BigEndian &operator&=(T x) {
return *this = *this & x;
}
BigEndian &operator|=(T x) {
return *this = *this | x;
}
private:
uint8_t val[sizeof(T)];
static T bswap(T x) {
if constexpr (sizeof(T) == 2)
return __builtin_bswap16(x);
else if constexpr (sizeof(T) == 4)
return __builtin_bswap32(x);
else
return __builtin_bswap64(x);
}
};
using ibig16 = BigEndian<int16_t>;
using ibig32 = BigEndian<int32_t>;
using ibig64 = BigEndian<int64_t>;
using ubig16 = BigEndian<uint16_t>;
using ubig32 = BigEndian<uint32_t>;
using ubig64 = BigEndian<uint64_t>;
} // namespace mold
-40
View File
@@ -1,40 +0,0 @@
#!/bin/bash -x
# This is a shell script to build a statically-linked mold
# executable using Docker, so that it is easy to build mold
# on non-Ubuntu 20.04 machines.
#
# A docker image used for building mold is persistent.
# Run `docker image rm mold-build-ubuntu20` to remove the image
# from disk.
set -e
# If the existing file is not statically-linked, remove it.
[ -f mold ] && ! ldd mold 2>&1 | grep -q 'not a dynamic executable' && \
rm mold
cat <<EOF | docker build -t mold-build-ubuntu20 -
FROM ubuntu:20.04
RUN apt-get update && \
TZ=Europe/London apt-get install -y tzdata && \
apt-get install -y build-essential git clang lld cmake \
libstdc++-10-dev libxxhash-dev zlib1g-dev libssl-dev && \
rm -rf /var/lib/apt/lists/*
EOF
if ! [ -d oneTBB ]; then
git clone https://github.com/oneapi-src/oneTBB.git
(cd oneTBB; git checkout --quiet v2020.3)
fi
docker run -it --rm -v `pwd`/oneTBB:/oneTBB -u $(id -u):$(id -g) \
mold-build-ubuntu20 \
make -C /oneTBB -j$(nproc) extra_inc=big_iron.inc
tbb_bindir=$(ls -d oneTBB/build/linux_intel64_*_release)
docker run -it --rm -v `pwd`:/mold -u $(id -u):$(id -g) \
mold-build-ubuntu20 \
make -C /mold -j$(nproc) \
EXTRA_CPPFLAGS=-IoneTBB/include \
EXTRA_LDFLAGS="-fuse-ld=lld -static -L$tbb_bindir"
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include "mold.h"
namespace mold {
template <typename C>
std::vector<std::string_view>
read_response_file(C &ctx, std::string_view path) {
std::vector<std::string_view> vec;
MappedFile<C> *mf = MappedFile<C>::must_open(ctx, std::string(path));
u8 *data = mf->data;
auto read_quoted = [&](i64 i, char quote) {
std::string buf;
while (i < mf->size && data[i] != quote) {
if (data[i] == '\\') {
buf.append(1, data[i + 1]);
i += 2;
} else {
buf.append(1, data[i++]);
}
}
if (i >= mf->size)
Fatal(ctx) << path << ": premature end of input";
vec.push_back(save_string(ctx, buf));
return i + 1;
};
auto read_unquoted = [&](i64 i) {
std::string buf;
while (i < mf->size && !isspace(data[i]))
buf.append(1, data[i++]);
vec.push_back(save_string(ctx, buf));
return i;
};
for (i64 i = 0; i < mf->size;) {
if (isspace(data[i]))
i++;
else if (data[i] == '\'')
i = read_quoted(i + 1, '\'');
else if (data[i] == '\"')
i = read_quoted(i + 1, '\"');
else
i = read_unquoted(i);
}
return vec;
}
template <typename C>
std::vector<std::string_view> expand_response_files(C &ctx, char **argv) {
std::vector<std::string_view> vec;
for (i64 i = 0; argv[i]; i++) {
if (argv[i][0] == '@')
append(vec, read_response_file(ctx, argv[i] + 1));
else
vec.push_back(argv[i]);
}
return vec;
}
} // namespace mold
+92 -10
View File
@@ -17,6 +17,10 @@
#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;
static std::vector<std::string_view> split(std::string_view input) {
@@ -33,13 +37,14 @@ static std::vector<std::string_view> split(std::string_view input) {
static std::vector<u8> do_compress(std::string_view input) {
// Initialize zlib stream. Since debug info is generally compressed
// pretty well, we chose compression level 3.
// pretty well with lower compression levels, we chose compression
// level 1.
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
int r = deflateInit2(&strm, 3, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY);
assert(r == Z_OK);
CHECK(deflateInit2(&strm, 1, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY));
// Set an input buffer
strm.avail_in = input.size();
@@ -49,19 +54,42 @@ 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;
}
Compressor::Compressor(std::string_view input) {
ZlibCompressor::ZlibCompressor(std::string_view input) {
std::vector<std::string_view> inputs = split(input);
std::vector<u64> adlers(inputs.size());
shards.resize(inputs.size());
@@ -78,14 +106,14 @@ Compressor::Compressor(std::string_view input) {
checksum = adler32_combine(checksum, adlers[i], inputs[i].size());
}
i64 Compressor::size() const {
i64 ZlibCompressor::size() const {
i64 size = 2; // +2 for header
for (const std::vector<u8> &shard : shards)
size += shard.size();
return size + 6; // +6 for trailer and checksum
}
void Compressor::write_to(u8 *buf) {
void ZlibCompressor::write_to(u8 *buf) {
// Write a zlib-format header
buf[0] = 0x78;
buf[1] = 0x9c;
@@ -106,5 +134,59 @@ void Compressor::write_to(u8 *buf) {
end[-5] = 0;
// Write a checksum
write32be(end - 4, checksum);
*(ubig32 *)(end - 4) = checksum;
}
GzipCompressor::GzipCompressor(std::string_view input) {
std::vector<std::string_view> inputs = split(input);
std::vector<u32> crc(inputs.size());
shards.resize(inputs.size());
// Compress each shard
tbb::parallel_for((i64)0, (i64)inputs.size(), [&](i64 i) {
crc[i] = crc32(0, (u8 *)inputs[i].data(), inputs[i].size());
shards[i] = do_compress(inputs[i]);
});
// Combine checksums
checksum = crc[0];
for (i64 i = 1; i < inputs.size(); i++)
checksum = crc32_combine(checksum, crc[i], inputs[i].size());
uncompressed_size = input.size();
}
i64 GzipCompressor::size() const {
i64 size = 10; // +10 for header
for (const std::vector<u8> &shard : shards)
size += shard.size();
return size + 10; // +10 for trailer and checksum
}
void GzipCompressor::write_to(u8 *buf) {
// Write a zlib-format header
memset(buf, 0, 10);
buf[0] = 0x1f; // magic
buf[1] = 0x8b; // magic
buf[2] = 0x08; // compression method is zlib
buf[9] = 0xff; // made on unknown OS
// Copy compressed data
std::vector<i64> offsets(shards.size());
offsets[0] = 10; // +10 for header
for (i64 i = 1; i < shards.size(); i++)
offsets[i] = offsets[i - 1] + shards[i - 1].size();
tbb::parallel_for((i64)0, (i64)shards.size(), [&](i64 i) {
memcpy(&buf[offsets[i]], shards[i].data(), shards[i].size());
});
// Write a trailer
u8 *end = buf + size();
end[-10] = 0x3; // two-byte zlib stream terminator
end[-9] = 0;
*(u32 *)(end - 8) = checksum;
*(u32 *)(end - 4) = uncompressed_size;
}
} // namespace mold
+24
View File
@@ -0,0 +1,24 @@
#include "mold.h"
#include <cstdlib>
#include <cxxabi.h>
namespace mold {
std::string_view demangle(std::string_view name) {
if (name.starts_with("_Z")) {
static thread_local char *buf;
static thread_local size_t buflen;
int status;
char *p = abi::__cxa_demangle(std::string(name).c_str(), buf, &buflen, &status);
if (status == 0) {
buf = p;
return p;
}
}
return name;
}
} // namespace mold
Executable
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash -x
# This script creates a mold binary distribution. The output is
# written in this directory as `mold-$version-$arch-linux.tar.gz`
# (e.g. `mold-1.0.3-x86_64-linux.tar.gz`).
#
# The mold executable created by this script is statically-linked to
# libstdc++ and libcrypto but dynamically-linked to libc, libm, libz
# and librt, as they almost always exist on any Linux systems.
set -e
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
arch=$(uname -m)
dest=mold-$version-$arch-linux
if [ $arch = x86_64 ]; then
image=rui314/mold-builder:v1-x86_64
elif [ $arch = aarch64 -o $arch = arm64 ]; then
image=rui314/mold-builder:v1-aarch64
else
echo "Error: no docker image for $arch"
exit 1
fi
docker images -q $image 2> /dev/null || docker pull $image
docker run -it --rm -v "$(pwd):/mold:Z" -u "$(id -u):$(id -g)" $image \
bash -c "cp -r /mold /tmp/mold &&
cd /tmp/mold &&
make clean &&
make -j\$(nproc) CXX=clang++-14 CXXFLAGS='-I/openssl/include -O2 $CXXFLAGS' LDFLAGS='-static-libstdc++ /openssl/libcrypto.a' NEEDS_LIBCRYPTO=0 LTO=${LTO:-0} &&
make install PREFIX=/ DESTDIR=$dest &&
ln -sfr $dest/bin/mold $dest/libexec/mold/ld &&
tar czf /mold/$dest.tar.gz $dest &&
cp mold /mold"
+2 -2
View File
@@ -31,7 +31,7 @@ If a program is statically-linked, there's no dynamic loader that
rewrites the GOT entries. Therefore, if a program is
statically-linked, a libc's startup routine does that on behalf of the
dynamic loader. Concretely, a startup routine interprets all dynamic
relocations between `__rela_iplt_start` and `__rela_iplt_start`
relocations between `__rela_iplt_start` and `__rela_iplt_stop`
symbols. It is linker's responsibility to emit dynamic relocations
for IFUNC symbols even if it is linking a statically-linked program
and mark the beginning and the ending of a `.rela.dyn` section with
@@ -41,7 +41,7 @@ The bug was my linker didn't define `__rela_iplt_start` and
`__rela_iplt_stop` symbols. Since these symbols are weak, they are
initialized to zero. From the point of the initializer function,
there's no dynamic relocations between `__rela_iplt_start` and
`__rela_iplt_start` symbols. That left GOT entries for IFUNC symbols
`__rela_iplt_stop` symbols. That left GOT entries for IFUNC symbols
untouched.
The proper fix was to emit dynamic relocations for IFUNC symbols and
+46
View File
@@ -0,0 +1,46 @@
# Coding Guidelines
mold is written in C++20, but as is the case with every C++ project,
it has local coding rules. In this document, I'll explain some of them
and try to give justifications for why I chose such rules.
## DOs
- Always use `i64` (which is a type alias for `int64_t` in mold) for
integers unless you have a reason not to. For example, even if you know
that a loop counter won't exceed 100, you should stop thinking about it
and just use `i64`.
Justification: Local variables are usually on CPU registers, so on
64-bit CPUs, there's no performance peanlty on choosing `i64` over
`i32`. Even if a compiler has to spill register values to the stack,
I don't think there's an observable difference between `i32` and
`i64`. Therefore, extra 32 bits are essentially free. On 32-bit CPUs,
they are not free, but that's OK because we are writing mold for modern
computers. mold will still run on 32-bit computers but a bit slowly.
By always using `i64`, we can eliminate the need to think about the
"right" size for each variable. It also reduces the risk of integer
overflow.
Exceptions: If you have to allocate a very large number (e.g. millions)
of the same object, its size matters. In that case, use a smaller type.
## DON'Ts
- Don't use `auto` unless its actual type is obvious in the very narrow
context. Currently, we use `auto` only for lambdas.
Justification: I think `auto` makes code writing easier but code reading
harder, because readers have to make a guess as to what is the actual
type of `auto`. If you are already familiar with the existing codebase,
you may be able to guess it easily, but that's not always the case.
I want to keep the mold codebase friendly to first-time readers.
- Don't over-use inheritance. In mold, most classes don't have parents,
and even if they do, their class hierarchy is very shallow. Currently
its height is just two (i.e. abstract classes and their implementations).
Justification: Designing class hierarchies is fun as it feels like
taxonomy, but I don't think that always help writing code. It looks like
simpler class hierarchy makes its code simpler.
Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

+486
View File
@@ -0,0 +1,486 @@
## Design and implementation of mold
For the rest of this documentation, I'll explain the design and the
implementation of mold. If you are only interested in using mold, you
don't need to read the below.
### Motivation
Here is why I'm writing a new linker:
- Even though lld has significantly improved the situation, linking is
still one of the slowest steps in a build. It is especially
annoying when I changed one line of code and had to wait for a few
seconds or even more for a linker to complete. It should be
instantaneous. There's a need for a faster linker.
- The number of cores on a PC has increased a lot lately, and this
trend is expected to continue. However, the existing linkers can't
take the advantage of the trend because they don't scale well for more
cores. I have a 64-core/128-thread machine, so my goal is to create
a linker that uses the CPU nicely. mold should be much faster than
other linkers on 4 or 8-core machines too, though.
- It looks to me that the designs of the existing linkers are somewhat
too similar, and I believe there are a lot of drastically different
designs that haven't been explored yet. Developers generally don't
care about linkers as long as they work correctly, and they don't
even think about creating a new one. So there may be lots of low
hanging fruits there in this area.
### Basic design
- In order to achieve a `cp`-like performance, the most important
thing is to fix the layout of an output file as quickly as possible, so
that we can start copying actual data from input object files to an
output file as soon as possible.
- Copying data from input files to an output file is I/O-bounded, so
there should be room for doing computationally-intensive tasks while
copying data from one file to another.
- We should allow the linker to preload object files from disk and
parse them in memory before a complete set of input object files
is ready. To do so, we need
to split the linker into two in such a way that the latter half of
the process finishes as quickly as possible by speculatively parsing
and preprocessing input files in the first half of the process.
- One of the most computationally-intensive stage among linker stages
is symbol resolution. To resolve symbols, we basically have to throw
all symbol strings into a hash table to match undefined symbols with
defined symbols. But this can be done in the preloading stage using
[string interning](https://en.wikipedia.org/wiki/String_interning).
- Object files may contain a special section called a mergeable string
section. The section contains lots of null-terminated strings, and
the linker is expected to gather all mergeable string sections and
merge their contents. So, if two object files contain the same
string literal, for example, the resulting output will contain a
single merged string. This step is computationally intensive, but string
merging can be done in the preloading stage using string interning.
- Static archives (.a files) contain object files, but the static
archive's string table contains only defined symbols of member
object files and lacks other types of symbols. That makes static
archives unsuitable for speculative parsing. Therefore, the linker
should ignore the symbol table of static archive and directly read
static archive members.
- If there's a relocation that uses a GOT of a symbol, then we have to
create a GOT entry for that symbol. Otherwise, we shouldn't. That
means we need to scan all relocation tables to fix the length and
the contents of a .got section. This is computationally intensive,
but this step is parallelizable.
### Linker Script
Linker script is an embedded language for the linker. It is mainly
used to control how input sections are mapped to output sections and
the layout of the output, but it can also do a lot of tricky stuff.
Its feature is useful especially for embedded programming, but it's
also an awfully underdocumented and complex language.
We have to implement a subset of the linker script language anwyay,
because on Linux, /usr/lib/x86_64-linux-gnu/libc.so is (despite its
name) not a shared object file but actually an ASCII file containing
linker script code to load the _actual_ libc.so file. But the feature
set for this purpose is very limited, and it is okay to implement them
to mold.
Besides that, we really don't want to implement the linker script
language. But at the same time, we want to satisfy the user needs that
are currently satisfied with the linker script language. So, what
should we do? Here is my observation:
- Linker script allows doing a lot of tricky stuff, such as specifying
the exact layout of a file, inserting arbitrary bytes between
sections, etc. But most of them can be done with a post-link binary
editing tool (such as `objcopy`).
- It looks like there are two things that truly cannot be done by a
post-link editing tool: (a) mapping input sections to output
sections, and (b) applying relocations.
From the above observation, I believe we need to provide only the
following features instead of the entire linker script language:
- A method to specify how input sections are mapped to output
sections, and
- a method to set addresses to output sections, so that relocations
are applied based on desired addresses.
I believe everything else can be done with a post-link binary editing
tool.
### Details
- As we aim to the 1-second goal for Chromium, every millisecond
counts. We can't ignore the latency of process exit. If we mmap a
lot of files, \_exit(2) is not instantaneous but takes a few hundred
milliseconds because the kernel has to clean up a lot of
resources. As a workaround, we should organize the linker command as
two processes; the first process forks the second process, and the
second process does the actual work. As soon as the second process
writes a result file to a filesystem, it notifies the first process,
and the first process exits. The second process can take time to
exit, because it is not an interactive process.
- At least on Linux, it looks like the filesystem's performance to
allocate new blocks to a new file is the limiting factor when
creating a new large file and filling its contents using mmap.
If you already have a large file in the buffer cache, writing to it is
much faster than creating a new fresh file and writing to it.
Based on this observation, mold overwrites to an existing
executable file if exists. My quick benchmark showed that I could
save 300 milliseconds when creating a 2 GiB output file.
Linux doesn't allow to open an executable for writing if it is
running (you'll get a "text busy" error if you attempt). mold
falls back to the usual way if it fails to open an output file.
- The output from the linker should be deterministic for the sake of
[build reproducibility](https://en.wikipedia.org/wiki/Reproducible_builds)
and ease of debugging. This might add a little bit of overhead to
the linker, but that shouldn't be too much.
- A .build-id, a unique ID embedded to an output file, is usually
computed by applying a cryptographic hash function (e.g. SHA-1) to
an output file. This is a slow step, but we can speed it up by
splitting a file into small chunks, computing SHA-1 for each chunk,
and then computing SHA-1 of the concatenated SHA-1 hashes
(i.e. constructing a [Merkle
Tree](https://en.wikipedia.org/wiki/Merkle_tree) of height 2).
Modern x86 processors have purpose-built instructions for SHA-1 and
can compute SHA-1 pretty quickly at about 2 GiB/s. Using 16
cores, a build-id for a 2 GiB executable can be computed in 60 to 70
milliseconds.
- BFD, gold, and lld support section garbage collection. That is, a
linker runs a mark-sweep garbage collection on an input graph, where
sections are vertices and relocations are edges, to discard all
sections that are not reachable from the entry point symbol
(i.e. `_start`) or a few other root sections. In mold, we are using
multiple threads to mark sections concurrently.
- Similarly, BFD, gold an lld support Identical Comdat Folding (ICF)
as yet another size optimization. ICF merges two or more read-only
sections that happen to have the same contents and relocations.
To do that, we have to find isomorphic subgraphs from larger graphs.
I implemented a new algorithm for mold, which is 5x faster than lld
to do ICF for Chromium (from 5 seconds to 1 second).
- [Intel Threading Building
Blocks](https://github.com/oneapi-src/oneTBB) (TBB) is a good
library for parallel execution and has several concurrent
containers. We are particularly interested in using
`parallel_for_each` and `concurrent_hash_map`.
- TBB provides `tbbmalloc` which works better for multi-threaded
applications than the glib'c malloc, but it looks like
[jemalloc](https://github.com/jemalloc/jemalloc) and
[mimalloc](https://github.com/microsoft/mimalloc) are a little bit
more scalable than `tbbmalloc`.
### Size of the problem
When linking Chrome, a linker reads 3,430,966,844 bytes of data in
total. The data contains the following items:
| Data item | Number
| ------------------------ | ------
| Object files | 30,723
| Public undefined symbols | 1,428,149
| Mergeable strings | 1,579,996
| Comdat groups | 9,914,510
| Regular sections¹ | 10,345,314
| Public defined symbols | 10,512,135
| Symbols | 23,953,607
| Sections | 27,543,225
| Relocations against SHF_ALLOC sections | 39,496,375
| Relocations | 62,024,719
¹ Sections that have to be copied from input object files to an
output file. Sections that contain relocations or symbols are for
example excluded.
### Internals
In this section, I'll explain the internals of mold linker.
#### A brief history of Unix and the Unix linker
Conceptually, what a linker does is pretty simple. A compiler compiles
a fragment of a program (a single source file) into a fragment of
machine code and data (an object file, which typically has the .o
extension), and a linker stitches them together into a single
executable or a shared library image.
In reality, modern linkers for Unix-like systems are much more
complicated than the naive understanding because they have gradually
gained one feature at a time over the 50 years history of Unix, and
they are now something like a bag of lots of miscellaneous features in
which none of the features is more important than the others. It is
very easy to miss the forest for the trees, since for those who don't
know the details of the Unix linker, it is not clear which feature is
essential and which is not.
That being said, one thing is clear that at any point of Unix history,
a Unix linker has a coherent feature set for the Unix of that age. So,
let me entangle the history to see how the operating system, runtime,
and linker have gained features that we see today. That should give
you an idea of why a particular feature has been added to a linker in the
first place.
1. Original Unix didn't support shared libraries, and a program was
always loaded to a fixed address. An executable was something like
a memory dump that was just loaded to a particular address by the
kernel. After loading, the kernel started executing the program by
setting the instruction pointer to a particular address.
The most essential feature for any linker is relocation processing.
The original Unix linker of course supported that. Let me explain
what that is.
Individual object files are inevitably incomplete as a program,
because when a compiler created them, it only see a part of an
entire program. For example, if an object file contains a function
call that refers to another object file, the `call` instruction in the
object cannot be complete, as the compiler has no idea as to what
is the called function's address. To deal with this, the compiler
emits a placeholder value (typically just zero) instead of a real
address and leaves metadata in an object file saying "fix offset X
of this file with an address of Y". That metadata is called
"relocation". Relocations are typically processed by the linker.
It is easy for a linker to apply relocations for the original Unix
because a program is always loaded to a fixed address. It exactly
knows the addresses of all functions and data when linking a
program.
Static library support, which is still an important feature of Unix
linker, also dates back to this early period of Unix history.
To understand what it is, imagine that you are trying to compile
a program for the early Unix. You don't want to waste time to
compile libc functions every time you compile your program (the
computers of the era were incredibly slow), so you have already
placed each libc function into a separate source file and compiled
them individually. That means you have object files for each libc
function, e.g., printf.o, scanf.o, atoi.o, write.o, etc.
Given this configuration, all you have to do to link your program
against libc functions is to pick up the right set of libc object
files and give them to the linker along with the object files of your
program. But, keeping the linker command line in sync with the
libc functions you are using in your program is bothersome. You can
be conservative; you can specify all libc object files to the
command line, but that leads to program bloat because the linker
unconditionally link all object files given to it no matter whether
they are used or not. So, a new feature was added to the linker to
fix the problem. That is the static library, which is also called
the archive file.
An archive file is just a bundle of object files, just like zip
file but in an uncompressed form. An archive file typically has the
.a file extension and named after its contents. For example, the
archive file containing all libc objects is named `libc.a`.
If you pass an archive file along with other object files to the
linker, the linker pulls out an object file from the archive _only
when_ it is referenced by other object files. In other words,
unlike object files directly given to a linker, object files
wrapped in an archive are not linked to the output by default.
An archive works as a supplement to complete your program.
Even today, you can still find a libc archive file. Run `ar t
/usr/lib/x86_64-linux-gnu/libc.a` on Linux should give you a list
of object files in the libc archive.
2. In the '80s, Sun Microsystems, a leading commercial Unix vendor at the
time, added shared library support to their Unix variant, SunOS.
(This section is incomplete.)
### Concurrency strategy
In this section, I'll explain the high-level concurrency strategy of
mold.
In most places, mold adopts data parallelism. That is, we have a huge
number of pieces of data of the same kind, and we process each of them
individually using parallel for-loop. For example, after identifying
the exact set of input object files, we need to scan all relocation
tables to determine the sizes of .got and .plt sections. We do that
using a parallel for-loop. The granularity of parallel processing in
this case is the relocation table.
Data parallelism is very efficient and scalable because there's no
need for threads to communicate with each other while working on each
element of data. In addition to that, data parallelism is easy to
understand, as it is just a for-loop in which multiple iterations may
be executed in parallel. We don't use high-level communication or
synchronization mechanisms such as channels, futures, promises,
latches or something like that in mold.
In some cases, we need to share a little bit of data between threads
while executing a parallel for-loop. For example, the loop to scan
relocations turns on "requires GOT" or "requires PLT" flags in a
symbol. Symbol is a shared resource, and writing to them from multiple
threads without synchronization is unsafe. To deal with it, we made
the flag an atomic variable.
The other common pattern you can find in mold which is build on top of
the parallel for-loop is the map-reduce pattern. That is, we run a
parallel for-loop on a large data set to produce a small data set and
process the small data set with a single thread. Let me take a
build-id computation as an example. Build-id is typically computed by
applying a cryptographic hash function such as SHA-1 on a linker's
output file. To compute it, we first consider an output as a sequence
of 1 MiB blocks and compute a SHA-1 hash for each block in parallel.
Then, we concatenate the SHA-1 hashes and compute a SHA-1 hash on the
hashes to get a final build-id.
Finally, we use concurrent hashmap at a few places in mold. Concurrent
hashmap is a hashmap to which multiple threads can safely insert items
in parallel. We use it in the symbol resolution stage, for example.
To resolve symbols, we basically have to throw in all defined symbols
into a hash table, so that we can find a matching defined symbol for
an undefined symbol by name. We do the hash table insertion from a
parallel for-loop which iterates over a list of input files.
Overall, even though mold is highly scalable, it succeeded to avoid
complexties you often find in complex parallel programs. From high
level, mold just serially executes the linker's internal passes one by
one. Each pass is parallelized using parallel for-loops.
### Rejected ideas
In this section, I'll explain the alternative designs I currently do
not plan to implement and why I turned them down.
- Placing variable-length sections at end of an output file and start
copying file contents before fixing the output file layout
Idea: Fixing the layout of regular sections seems easy, and if we
place them at beginning of a file, we can start copying their
contents from their input files to an output file. While copying
file contents, we can compute the sizes of variable-length sections
such as .got or .plt and place them at end of the file.
Reason for rejection: I did not choose this design because I doubt
if it could actually shorten link time and I think I don't need it
anyway.
The linker has to de-duplicate comdat sections (i.e. inline
functions that are included in multiple object files), so we
cannot compute the layout of regular sections until we resolve all
symbols and de-duplicate comdats. That takes a few hundred
milliseconds. After that, we can compute the sizes of
variable-length sections in less than 100 milliseconds. It's quite
fast, so it doesn't seem to make much sense to proceed without
fixing the final file layout.
The other reason to reject this idea is because there's good a
chance for this idea to have a negative impact on linker's overall
performance. If we copy file contents before fixing the layout, we
can't apply relocations to them while copying because symbol
addresses are not available yet. If we fix the file layout first, we
can apply relocations while copying, which is effectively zero-cost
due to a very good data locality. On the other hand, if we apply
relocations long after we copy file contents, it's pretty expensive
because section contents are very likely to have been evicted from
CPU cache.
- Incremental linking
Idea: Incremental linking is a technique to patch a previous linker's
output file so that only functions or data that are updated from the
previous build are written to it. It is expected to significantly
reduce the amount of data copied from input files to an output file
and thus speed up linking. GNU BFD and gold linkers support it.
Reason for rejection: I turned it down because it (1) is
complicated, (2) doesn't seem to speed it up that much and (3) has
several practical issues. Let me explain each of them.
First, incremental linking for real C/C++ programs is not as easy as
one might think. Let me take malloc as an example. malloc is usually
defined by libc, but you can implement it in your program, and if
that's the case, the symbol `malloc` will be resolved to your
function instead of the one in libc. If you include a library that
defines malloc (such as libjemalloc or libtbbmallc) before libc,
their malloc will override libc's malloc.
Assume that you are using a nonstandard malloc. What if you remove
your malloc from your code, or remove `-ljemalloc` from your
Makefile? The linker has to include a malloc from libc, which may
include more object files to satisfy its dependencies. Such code
change can affect the entire program rather than just replacing one
function. The same is true for adding malloc to your program. Making
a local change doesn't necessarily result in a local change in the
binary level. It can easily have cascading effects.
Some ELF fancy features make incremental linking even harder to
implement. Take the weak symbol as an example. If you define `atoi`
as a weak symbol in your program, and if you are not using `atoi`
at all in your program, that symbol will be resolved to address
0. But if you start using some libc function that indirectly calls
`atoi`, then `atoi` will be included in your program, and your weak
symbol will be resolved to that function. I don't know how to
efficiently fix up a binary for this case.
This is a hard problem, so existing linkers don't try too hard to
solve it. For example, IIRC, gold falls back to full link if any
function is removed from a previous build. If you want to not annoy
users in the fallback case, you need to make full link fast anyway.
Second, incremental linking itself has an overhead. It has to detect
updated files, patch an existing output file and write additional
data to an output file for future incremental linking. GNU gold, for
instance, takes almost 30 seconds on my machine to do a null
incremental link (i.e. no object files are updated from a previous
build) for chrome. It's just too slow.
Third, there are other practical issues in incremental linking. It's
not reproducible, so your binary isn't going to be the same as other
binaries even if you are compiling the same source tree using the
same compiler toolchain. Or, it is complex and there might be a bug
in it. If something doesn't work correctly, "remove --incremental
from your Makefile and try again" could be a piece of advice, but
that isn't ideal.
So, all in all, incremental linking is tricky. I wanted to make full
link as fast as possible, so that we don't have to think about how
to work around the slowness of full link.
- Defining a completely new file format and use it
Idea: Sometimes, the ELF file format itself seems to be a limiting
factor in improving the linker's performance. We might be able to make a
far better one if we create a new file format.
Reason for rejection: I rejected the idea because it apparently has
a practical issue (backward compatibility issue) and also doesn't
seem to improve the performance of linkers that much. As clearly
demonstrated by mold, we can create a fast linker for ELF. I believe
ELF isn't that bad, after all. The semantics of the existing Unix
linkers, such as the name resolution algorithm or the linker script,
have slowed the linkers down, but that's not a problem of the file
format itself.
- Watching object files using inotify(2)
Idea: When mold is running as a daemon for preloading, use
inotify(2) to watch file system updates so that it can reload files
as soon as they are updated.
Reason for rejection: Just like the maximum number of files you can
simultaneously open, the maximum number of files you can watch using
inotify(2) isn't that large. Maybe just a single instance of mold is
fine with inotify(2), but it may fail if you run multiple of it.
The other reason for not doing it is because mold is quite fast
without it anyway. Invoking stat(2) on each file for file update
check takes less than 100 milliseconds for Chrome, and if most of
the input files are not updated, parsing updated files takes almost
no time.
+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.
+1059 -654
View File
File diff suppressed because it is too large. Load diff
-702
View File
@@ -1,702 +0,0 @@
#pragma once
#include <cstdint>
#include <endian.h>
#include <string>
#if __BYTE_ORDER == __BIG_ENDIAN
#error "mold does not support big-endian hosts"
#endif
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
template <typename E>
static std::string rel_to_string(u32 r_type);
struct X86_64;
struct I386;
static constexpr u32 SHN_UNDEF = 0;
static constexpr u32 SHN_ABS = 0xfff1;
static constexpr u32 SHN_COMMON = 0xfff2;
static constexpr u32 SHN_XINDEX = 0xffff;
static constexpr u32 SHT_NULL = 0;
static constexpr u32 SHT_PROGBITS = 1;
static constexpr u32 SHT_SYMTAB = 2;
static constexpr u32 SHT_STRTAB = 3;
static constexpr u32 SHT_RELA = 4;
static constexpr u32 SHT_HASH = 5;
static constexpr u32 SHT_DYNAMIC = 6;
static constexpr u32 SHT_NOTE = 7;
static constexpr u32 SHT_NOBITS = 8;
static constexpr u32 SHT_REL = 9;
static constexpr u32 SHT_SHLIB = 10;
static constexpr u32 SHT_DYNSYM = 11;
static constexpr u32 SHT_INIT_ARRAY = 14;
static constexpr u32 SHT_FINI_ARRAY = 15;
static constexpr u32 SHT_PREINIT_ARRAY = 16;
static constexpr u32 SHT_GROUP = 17;
static constexpr u32 SHT_SYMTAB_SHNDX = 18;
static constexpr u32 SHT_GNU_HASH = 0x6ffffff6;
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 SHF_WRITE = 0x1;
static constexpr u32 SHF_ALLOC = 0x2;
static constexpr u32 SHF_EXECINSTR = 0x4;
static constexpr u32 SHF_MERGE = 0x10;
static constexpr u32 SHF_STRINGS = 0x20;
static constexpr u32 SHF_INFO_LINK = 0x40;
static constexpr u32 SHF_LINK_ORDER = 0x80;
static constexpr u32 SHF_GROUP = 0x200;
static constexpr u32 SHF_TLS = 0x400;
static constexpr u32 SHF_COMPRESSED = 0x800;
static constexpr u32 SHF_EXCLUDE = 0x80000000;
static constexpr u32 GRP_COMDAT = 1;
static constexpr u32 STT_NOTYPE = 0;
static constexpr u32 STT_OBJECT = 1;
static constexpr u32 STT_FUNC = 2;
static constexpr u32 STT_SECTION = 3;
static constexpr u32 STT_FILE = 4;
static constexpr u32 STT_COMMON = 5;
static constexpr u32 STT_TLS = 6;
static constexpr u32 STT_GNU_IFUNC = 10;
static constexpr u32 STB_LOCAL = 0;
static constexpr u32 STB_GLOBAL = 1;
static constexpr u32 STB_WEAK = 2;
static constexpr u32 STB_GNU_UNIQUE = 10;
static constexpr u32 STV_DEFAULT = 0;
static constexpr u32 STV_INTERNAL = 1;
static constexpr u32 STV_HIDDEN = 2;
static constexpr u32 STV_PROTECTED = 3;
static constexpr u32 VER_NDX_LOCAL = 0;
static constexpr u32 VER_NDX_GLOBAL = 1;
static constexpr u32 VER_NDX_LAST_RESERVED = 1;
static constexpr u32 VER_FLG_BASE = 1;
static constexpr u32 VER_FLG_WEAK = 2;
static constexpr u32 VER_FLG_INFO = 4;
static constexpr u32 VERSYM_HIDDEN = 0x8000;
static constexpr u32 PT_NULL = 0;
static constexpr u32 PT_LOAD = 1;
static constexpr u32 PT_DYNAMIC = 2;
static constexpr u32 PT_INTERP = 3;
static constexpr u32 PT_NOTE = 4;
static constexpr u32 PT_SHLIB = 5;
static constexpr u32 PT_PHDR = 6;
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 PF_X = 1;
static constexpr u32 PF_W = 2;
static constexpr u32 PF_R = 4;
static constexpr u32 ET_NONE = 0;
static constexpr u32 ET_REL = 1;
static constexpr u32 ET_EXEC = 2;
static constexpr u32 ET_DYN = 3;
static constexpr u32 ELFDATA2LSB = 1;
static constexpr u32 ELFDATA2MSB = 2;
static constexpr u32 ELFCLASS32 = 1;
static constexpr u32 ELFCLASS64 = 2;
static constexpr u32 EV_CURRENT = 1;
static constexpr u32 EM_386 = 3;
static constexpr u32 EM_X86_64 = 62;
static constexpr u32 EI_CLASS = 4;
static constexpr u32 EI_DATA = 5;
static constexpr u32 EI_VERSION = 6;
static constexpr u32 EI_OSABI = 7;
static constexpr u32 EI_ABIVERSION = 8;
static constexpr u32 ELFOSABI_NONE = 0;
static constexpr u32 ELFOSABI_GNU = 3;
static constexpr u32 DT_NULL = 0;
static constexpr u32 DT_NEEDED = 1;
static constexpr u32 DT_PLTRELSZ = 2;
static constexpr u32 DT_PLTGOT = 3;
static constexpr u32 DT_HASH = 4;
static constexpr u32 DT_STRTAB = 5;
static constexpr u32 DT_SYMTAB = 6;
static constexpr u32 DT_RELA = 7;
static constexpr u32 DT_RELASZ = 8;
static constexpr u32 DT_RELAENT = 9;
static constexpr u32 DT_STRSZ = 10;
static constexpr u32 DT_SYMENT = 11;
static constexpr u32 DT_INIT = 12;
static constexpr u32 DT_FINI = 13;
static constexpr u32 DT_SONAME = 14;
static constexpr u32 DT_RPATH = 15;
static constexpr u32 DT_SYMBOLIC = 16;
static constexpr u32 DT_REL = 17;
static constexpr u32 DT_RELSZ = 18;
static constexpr u32 DT_RELENT = 19;
static constexpr u32 DT_PLTREL = 20;
static constexpr u32 DT_DEBUG = 21;
static constexpr u32 DT_TEXTREL = 22;
static constexpr u32 DT_JMPREL = 23;
static constexpr u32 DT_BIND_NOW = 24;
static constexpr u32 DT_INIT_ARRAY = 25;
static constexpr u32 DT_FINI_ARRAY = 26;
static constexpr u32 DT_INIT_ARRAYSZ = 27;
static constexpr u32 DT_FINI_ARRAYSZ = 28;
static constexpr u32 DT_RUNPATH = 29;
static constexpr u32 DT_FLAGS = 30;
static constexpr u32 DT_GNU_HASH = 0x6ffffef5;
static constexpr u32 DT_VERSYM = 0x6ffffff0;
static constexpr u32 DT_RELACOUNT = 0x6ffffff9;
static constexpr u32 DT_RELCOUNT = 0x6ffffffa;
static constexpr u32 DT_FLAGS_1 = 0x6ffffffb;
static constexpr u32 DT_VERDEF = 0x6ffffffc;
static constexpr u32 DT_VERDEFNUM = 0x6ffffffd;
static constexpr u32 DT_VERNEED = 0x6ffffffe;
static constexpr u32 DT_VERNEEDNUM = 0x6fffffff;
static constexpr u32 DT_AUXILIARY = 0x7ffffffd;
static constexpr u32 DT_FILTER = 0x7fffffff;
static constexpr u32 DF_ORIGIN = 0x01;
static constexpr u32 DF_SYMBOLIC = 0x02;
static constexpr u32 DF_TEXTREL = 0x04;
static constexpr u32 DF_BIND_NOW = 0x08;
static constexpr u32 DF_STATIC_TLS = 0x10;
static constexpr u32 DF_1_NOW = 0x00000001;
static constexpr u32 DF_1_NODELETE = 0x00000008;
static constexpr u32 DF_1_INITFIRST = 0x00000020;
static constexpr u32 DF_1_NOOPEN = 0x00000040;
static constexpr u32 DF_1_ORIGIN = 0x00000080;
static constexpr u32 DF_1_INTERPOSE = 0x00000400;
static constexpr u32 DF_1_NODUMP = 0x00001000;
static constexpr u32 DF_1_PIE = 0x08000000;
static constexpr u32 NT_GNU_ABI_TAG = 1;
static constexpr u32 NT_GNU_HWCAP = 2;
static constexpr u32 NT_GNU_BUILD_ID = 3;
static constexpr u32 NT_GNU_GOLD_VERSION = 4;
static constexpr u32 NT_GNU_PROPERTY_TYPE_0 = 5;
static constexpr u32 GNU_PROPERTY_AARCH64_FEATURE_1_AND = 0xc0000000;
static constexpr u32 GNU_PROPERTY_X86_FEATURE_1_AND = 0xc0000002;
static constexpr u32 ELFCOMPRESS_ZLIB = 1;
static constexpr u32 R_X86_64_NONE = 0;
static constexpr u32 R_X86_64_64 = 1;
static constexpr u32 R_X86_64_PC32 = 2;
static constexpr u32 R_X86_64_GOT32 = 3;
static constexpr u32 R_X86_64_PLT32 = 4;
static constexpr u32 R_X86_64_COPY = 5;
static constexpr u32 R_X86_64_GLOB_DAT = 6;
static constexpr u32 R_X86_64_JUMP_SLOT = 7;
static constexpr u32 R_X86_64_RELATIVE = 8;
static constexpr u32 R_X86_64_GOTPCREL = 9;
static constexpr u32 R_X86_64_32 = 10;
static constexpr u32 R_X86_64_32S = 11;
static constexpr u32 R_X86_64_16 = 12;
static constexpr u32 R_X86_64_PC16 = 13;
static constexpr u32 R_X86_64_8 = 14;
static constexpr u32 R_X86_64_PC8 = 15;
static constexpr u32 R_X86_64_DTPMOD64 = 16;
static constexpr u32 R_X86_64_DTPOFF64 = 17;
static constexpr u32 R_X86_64_TPOFF64 = 18;
static constexpr u32 R_X86_64_TLSGD = 19;
static constexpr u32 R_X86_64_TLSLD = 20;
static constexpr u32 R_X86_64_DTPOFF32 = 21;
static constexpr u32 R_X86_64_GOTTPOFF = 22;
static constexpr u32 R_X86_64_TPOFF32 = 23;
static constexpr u32 R_X86_64_PC64 = 24;
static constexpr u32 R_X86_64_GOTOFF64 = 25;
static constexpr u32 R_X86_64_GOTPC32 = 26;
static constexpr u32 R_X86_64_GOT64 = 27;
static constexpr u32 R_X86_64_GOTPCREL64 = 28;
static constexpr u32 R_X86_64_GOTPC64 = 29;
static constexpr u32 R_X86_64_GOTPLT64 = 30;
static constexpr u32 R_X86_64_PLTOFF64 = 31;
static constexpr u32 R_X86_64_SIZE32 = 32;
static constexpr u32 R_X86_64_SIZE64 = 33;
static constexpr u32 R_X86_64_GOTPC32_TLSDESC = 34;
static constexpr u32 R_X86_64_TLSDESC_CALL = 35;
static constexpr u32 R_X86_64_TLSDESC = 36;
static constexpr u32 R_X86_64_IRELATIVE = 37;
static constexpr u32 R_X86_64_GOTPCRELX = 41;
static constexpr u32 R_X86_64_REX_GOTPCRELX = 42;
template <>
inline std::string rel_to_string<X86_64>(u32 r_type) {
switch (r_type) {
case R_X86_64_NONE: return "R_X86_64_NONE";
case R_X86_64_64: return "R_X86_64_64";
case R_X86_64_PC32: return "R_X86_64_PC32";
case R_X86_64_GOT32: return "R_X86_64_GOT32";
case R_X86_64_PLT32: return "R_X86_64_PLT32";
case R_X86_64_COPY: return "R_X86_64_COPY";
case R_X86_64_GLOB_DAT: return "R_X86_64_GLOB_DAT";
case R_X86_64_JUMP_SLOT: return "R_X86_64_JUMP_SLOT";
case R_X86_64_RELATIVE: return "R_X86_64_RELATIVE";
case R_X86_64_GOTPCREL: return "R_X86_64_GOTPCREL";
case R_X86_64_32: return "R_X86_64_32";
case R_X86_64_32S: return "R_X86_64_32S";
case R_X86_64_16: return "R_X86_64_16";
case R_X86_64_PC16: return "R_X86_64_PC16";
case R_X86_64_8: return "R_X86_64_8";
case R_X86_64_PC8: return "R_X86_64_PC8";
case R_X86_64_DTPMOD64: return "R_X86_64_DTPMOD64";
case R_X86_64_DTPOFF64: return "R_X86_64_DTPOFF64";
case R_X86_64_TPOFF64: return "R_X86_64_TPOFF64";
case R_X86_64_TLSGD: return "R_X86_64_TLSGD";
case R_X86_64_TLSLD: return "R_X86_64_TLSLD";
case R_X86_64_DTPOFF32: return "R_X86_64_DTPOFF32";
case R_X86_64_GOTTPOFF: return "R_X86_64_GOTTPOFF";
case R_X86_64_TPOFF32: return "R_X86_64_TPOFF32";
case R_X86_64_PC64: return "R_X86_64_PC64";
case R_X86_64_GOTOFF64: return "R_X86_64_GOTOFF64";
case R_X86_64_GOTPC32: return "R_X86_64_GOTPC32";
case R_X86_64_GOT64: return "R_X86_64_GOT64";
case R_X86_64_GOTPCREL64: return "R_X86_64_GOTPCREL64";
case R_X86_64_GOTPC64: return "R_X86_64_GOTPC64";
case R_X86_64_GOTPLT64: return "R_X86_64_GOTPLT64";
case R_X86_64_PLTOFF64: return "R_X86_64_PLTOFF64";
case R_X86_64_SIZE32: return "R_X86_64_SIZE32";
case R_X86_64_SIZE64: return "R_X86_64_SIZE64";
case R_X86_64_GOTPC32_TLSDESC: return "R_X86_64_GOTPC32_TLSDESC";
case R_X86_64_TLSDESC_CALL: return "R_X86_64_TLSDESC_CALL";
case R_X86_64_TLSDESC: return "R_X86_64_TLSDESC";
case R_X86_64_IRELATIVE: return "R_X86_64_IRELATIVE";
case R_X86_64_GOTPCRELX: return "R_X86_64_GOTPCRELX";
case R_X86_64_REX_GOTPCRELX: return "R_X86_64_REX_GOTPCRELX";
}
return "unknown (" + std::to_string(r_type) + ")";
}
static constexpr u32 R_386_NONE = 0;
static constexpr u32 R_386_32 = 1;
static constexpr u32 R_386_PC32 = 2;
static constexpr u32 R_386_GOT32 = 3;
static constexpr u32 R_386_PLT32 = 4;
static constexpr u32 R_386_COPY = 5;
static constexpr u32 R_386_GLOB_DAT = 6;
static constexpr u32 R_386_JUMP_SLOT = 7;
static constexpr u32 R_386_RELATIVE = 8;
static constexpr u32 R_386_GOTOFF = 9;
static constexpr u32 R_386_GOTPC = 10;
static constexpr u32 R_386_32PLT = 11;
static constexpr u32 R_386_TLS_TPOFF = 14;
static constexpr u32 R_386_TLS_IE = 15;
static constexpr u32 R_386_TLS_GOTIE = 16;
static constexpr u32 R_386_TLS_LE = 17;
static constexpr u32 R_386_TLS_GD = 18;
static constexpr u32 R_386_TLS_LDM = 19;
static constexpr u32 R_386_16 = 20;
static constexpr u32 R_386_PC16 = 21;
static constexpr u32 R_386_8 = 22;
static constexpr u32 R_386_PC8 = 23;
static constexpr u32 R_386_TLS_GD_32 = 24;
static constexpr u32 R_386_TLS_GD_PUSH = 25;
static constexpr u32 R_386_TLS_GD_CALL = 26;
static constexpr u32 R_386_TLS_GD_POP = 27;
static constexpr u32 R_386_TLS_LDM_32 = 28;
static constexpr u32 R_386_TLS_LDM_PUSH = 29;
static constexpr u32 R_386_TLS_LDM_CALL = 30;
static constexpr u32 R_386_TLS_LDM_POP = 31;
static constexpr u32 R_386_TLS_LDO_32 = 32;
static constexpr u32 R_386_TLS_IE_32 = 33;
static constexpr u32 R_386_TLS_LE_32 = 34;
static constexpr u32 R_386_TLS_DTPMOD32 = 35;
static constexpr u32 R_386_TLS_DTPOFF32 = 36;
static constexpr u32 R_386_TLS_TPOFF32 = 37;
static constexpr u32 R_386_SIZE32 = 38;
static constexpr u32 R_386_TLS_GOTDESC = 39;
static constexpr u32 R_386_TLS_DESC_CALL = 40;
static constexpr u32 R_386_TLS_DESC = 41;
static constexpr u32 R_386_IRELATIVE = 42;
static constexpr u32 R_386_GOT32X = 43;
template <>
std::string rel_to_string<I386>(u32 r_type) {
switch (r_type) {
case R_386_NONE: return "R_386_NONE";
case R_386_32: return "R_386_32";
case R_386_PC32: return "R_386_PC32";
case R_386_GOT32: return "R_386_GOT32";
case R_386_PLT32: return "R_386_PLT32";
case R_386_COPY: return "R_386_COPY";
case R_386_GLOB_DAT: return "R_386_GLOB_DAT";
case R_386_JUMP_SLOT: return "R_386_JUMP_SLOT";
case R_386_RELATIVE: return "R_386_RELATIVE";
case R_386_GOTOFF: return "R_386_GOTOFF";
case R_386_GOTPC: return "R_386_GOTPC";
case R_386_32PLT: return "R_386_32PLT";
case R_386_TLS_TPOFF: return "R_386_TLS_TPOFF";
case R_386_TLS_IE: return "R_386_TLS_IE";
case R_386_TLS_GOTIE: return "R_386_TLS_GOTIE";
case R_386_TLS_LE: return "R_386_TLS_LE";
case R_386_TLS_GD: return "R_386_TLS_GD";
case R_386_TLS_LDM: return "R_386_TLS_LDM";
case R_386_16: return "R_386_16";
case R_386_PC16: return "R_386_PC16";
case R_386_8: return "R_386_8";
case R_386_PC8: return "R_386_PC8";
case R_386_TLS_GD_32: return "R_386_TLS_GD_32";
case R_386_TLS_GD_PUSH: return "R_386_TLS_GD_PUSH";
case R_386_TLS_GD_CALL: return "R_386_TLS_GD_CALL";
case R_386_TLS_GD_POP: return "R_386_TLS_GD_POP";
case R_386_TLS_LDM_32: return "R_386_TLS_LDM_32";
case R_386_TLS_LDM_PUSH: return "R_386_TLS_LDM_PUSH";
case R_386_TLS_LDM_CALL: return "R_386_TLS_LDM_CALL";
case R_386_TLS_LDM_POP: return "R_386_TLS_LDM_POP";
case R_386_TLS_LDO_32: return "R_386_TLS_LDO_32";
case R_386_TLS_IE_32: return "R_386_TLS_IE_32";
case R_386_TLS_LE_32: return "R_386_TLS_LE_32";
case R_386_TLS_DTPMOD32: return "R_386_TLS_DTPMOD32";
case R_386_TLS_DTPOFF32: return "R_386_TLS_DTPOFF32";
case R_386_TLS_TPOFF32: return "R_386_TLS_TPOFF32";
case R_386_SIZE32: return "R_386_SIZE32";
case R_386_TLS_GOTDESC: return "R_386_TLS_GOTDESC";
case R_386_TLS_DESC_CALL: return "R_386_TLS_DESC_CALL";
case R_386_TLS_DESC: return "R_386_TLS_DESC";
case R_386_IRELATIVE: return "R_386_IRELATIVE";
case R_386_GOT32X: return "R_386_GOT32X";
}
return "unknown (" + std::to_string(r_type) + ")";
}
static constexpr u32 DW_EH_PE_absptr = 0;
static constexpr u32 DW_EH_PE_omit = 0xff;
static constexpr u32 DW_EH_PE_uleb128 = 0x01;
static constexpr u32 DW_EH_PE_udata2 = 0x02;
static constexpr u32 DW_EH_PE_udata4 = 0x03;
static constexpr u32 DW_EH_PE_udata8 = 0x04;
static constexpr u32 DW_EH_PE_signed = 0x08;
static constexpr u32 DW_EH_PE_sleb128 = 0x09;
static constexpr u32 DW_EH_PE_sdata2 = 0x0a;
static constexpr u32 DW_EH_PE_sdata4 = 0x0b;
static constexpr u32 DW_EH_PE_sdata8 = 0x0c;
static constexpr u32 DW_EH_PE_pcrel = 0x10;
static constexpr u32 DW_EH_PE_textrel = 0x20;
static constexpr u32 DW_EH_PE_datarel = 0x30;
static constexpr u32 DW_EH_PE_funcrel = 0x40;
static constexpr u32 DW_EH_PE_aligned = 0x50;
struct Elf64Sym {
bool is_defined() const { return !is_undef(); }
bool is_undef() const { return st_shndx == SHN_UNDEF; }
bool is_abs() const { return st_shndx == SHN_ABS; }
bool is_common() const { return st_shndx == SHN_COMMON; }
bool is_weak() const { return st_bind == STB_WEAK; }
bool is_undef_strong() const {
return st_shndx == SHN_UNDEF && st_bind != STB_WEAK;
}
bool is_undef_weak() const {
return st_shndx == SHN_UNDEF && st_bind == STB_WEAK;
}
u32 st_name;
u8 st_type : 4;
u8 st_bind : 4;
u8 st_visibility : 2;
u16 st_shndx;
u64 st_value;
u64 st_size;
};
struct Elf32Sym {
bool is_defined() const { return !is_undef(); }
bool is_undef() const { return st_shndx == SHN_UNDEF; }
bool is_abs() const { return st_shndx == SHN_ABS; }
bool is_common() const { return st_shndx == SHN_COMMON; }
bool is_weak() const { return st_bind == STB_WEAK; }
bool is_undef_strong() const {
return st_shndx == SHN_UNDEF && st_bind != STB_WEAK;
}
bool is_undef_weak() const {
return st_shndx == SHN_UNDEF && st_bind == STB_WEAK;
}
u32 st_name;
u32 st_value;
u32 st_size;
u8 st_type : 4;
u8 st_bind : 4;
u8 st_visibility : 2;
u16 st_shndx;
};
struct Elf64Shdr {
u32 sh_name;
u32 sh_type;
u64 sh_flags;
u64 sh_addr;
u64 sh_offset;
u64 sh_size;
u32 sh_link;
u32 sh_info;
u64 sh_addralign;
u64 sh_entsize;
};
struct Elf32Shdr {
u32 sh_name;
u32 sh_type;
u32 sh_flags;
u32 sh_addr;
u32 sh_offset;
u32 sh_size;
u32 sh_link;
u32 sh_info;
u32 sh_addralign;
u32 sh_entsize;
};
struct Elf64Ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u64 e_entry;
u64 e_phoff;
u64 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
};
struct Elf32Ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u32 e_entry;
u32 e_phoff;
u32 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
};
struct Elf64Phdr {
u32 p_type;
u32 p_flags;
u64 p_offset;
u64 p_vaddr;
u64 p_paddr;
u64 p_filesz;
u64 p_memsz;
u64 p_align;
};
struct Elf32Phdr {
u32 p_type;
u32 p_offset;
u32 p_vaddr;
u32 p_paddr;
u32 p_filesz;
u32 p_memsz;
u32 p_flags;
u32 p_align;
};
struct Elf64Rel {
u64 r_offset;
u32 r_type;
u32 r_sym;
};
struct Elf32Rel {
u32 r_offset;
u32 r_type : 8;
u32 r_sym : 24;
};
struct Elf64Rela {
u64 r_offset;
u32 r_type;
u32 r_sym;
i64 r_addend;
};
struct Elf32Rela {
u32 r_offset;
u32 r_type : 8;
u32 r_sym : 24;
i32 r_addend;
};
struct Elf64Dyn {
u64 d_tag;
u64 d_val;
};
struct Elf32Dyn {
u32 d_tag;
u32 d_val;
};
struct Elf64Verneed {
u16 vn_version;
u16 vn_cnt;
u32 vn_file;
u32 vn_aux;
u32 vn_next;
};
struct Elf64Vernaux {
u32 vna_hash;
u16 vna_flags;
u16 vna_other;
u32 vna_name;
u32 vna_next;
};
struct Elf64Verdef {
u16 vd_version;
u16 vd_flags;
u16 vd_ndx;
u16 vd_cnt;
u32 vd_hash;
u32 vd_aux;
u32 vd_next;
};
struct Elf64Verdaux {
u32 vda_name;
u32 vda_next;
};
struct Elf64Chdr {
u32 ch_type;
u32 ch_reserved;
u64 ch_size;
u64 ch_addralign;
};
struct Elf32Chdr {
u32 ch_type;
u32 ch_size;
u32 ch_addralign;
};
struct Elf64Nhdr {
u32 n_namesz;
u32 n_descsz;
u32 n_type;
};
template <typename E> struct ElfSym;
template <typename E> struct ElfShdr;
template <typename E> struct ElfEhdr;
template <typename E> struct ElfPhdr;
template <typename E> struct ElfRel;
template <typename E> struct ElfDyn;
template <typename E> struct ElfVerneed;
template <typename E> struct ElfVernaux;
template <typename E> struct ElfVerdef;
template <typename E> struct ElfVerdaux;
template <typename E> struct ElfChdr;
template <typename E> struct ElfNhdr;
struct X86_64 {
typedef u64 WordTy;
static constexpr u32 R_NONE = R_X86_64_NONE;
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_RELATIVE = R_X86_64_RELATIVE;
static constexpr u32 R_IRELATIVE = R_X86_64_IRELATIVE;
static constexpr u32 R_DTPOFF = R_X86_64_DTPOFF64;
static constexpr u32 R_TPOFF = R_X86_64_TPOFF64;
static constexpr u32 R_DTPMOD = R_X86_64_DTPMOD64;
static constexpr u32 R_TLSDESC = R_X86_64_TLSDESC;
static constexpr u32 wordsize = 8;
static constexpr u32 e_machine = EM_X86_64;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 8;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
};
template <> struct ElfSym<X86_64> : public Elf64Sym {};
template <> struct ElfShdr<X86_64> : public Elf64Shdr {};
template <> struct ElfEhdr<X86_64> : public Elf64Ehdr {};
template <> struct ElfPhdr<X86_64> : public Elf64Phdr {};
template <> struct ElfRel<X86_64> : public Elf64Rela {};
template <> struct ElfDyn<X86_64> : public Elf64Dyn {};
template <> struct ElfVerneed<X86_64> : public Elf64Verneed {};
template <> struct ElfVernaux<X86_64> : public Elf64Vernaux {};
template <> struct ElfVerdef<X86_64> : public Elf64Verdef {};
template <> struct ElfVerdaux<X86_64> : public Elf64Verdaux {};
template <> struct ElfChdr<X86_64> : public Elf64Chdr {};
template <> struct ElfNhdr<X86_64> : public Elf64Nhdr {};
struct I386 {
typedef u32 WordTy;
static constexpr u32 R_NONE = R_386_NONE;
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_RELATIVE = R_386_RELATIVE;
static constexpr u32 R_IRELATIVE = R_386_IRELATIVE;
static constexpr u32 R_DTPOFF = R_386_TLS_DTPOFF32;
static constexpr u32 R_TPOFF = R_386_TLS_TPOFF;
static constexpr u32 R_DTPMOD = R_386_TLS_DTPMOD32;
static constexpr u32 R_TLSDESC = R_386_TLS_DESC;
static constexpr u32 wordsize = 4;
static constexpr u32 e_machine = EM_386;
static constexpr u32 plt_size = 16;
static constexpr u32 pltgot_size = 8;
static constexpr bool is_rel = true;
static constexpr bool is_le = true;
};
template <> struct ElfSym<I386> : public Elf32Sym {};
template <> struct ElfShdr<I386> : public Elf32Shdr {};
template <> struct ElfEhdr<I386> : public Elf32Ehdr {};
template <> struct ElfPhdr<I386> : public Elf32Phdr {};
template <> struct ElfRel<I386> : public Elf32Rel {};
template <> struct ElfDyn<I386> : public Elf32Dyn {};
template <> struct ElfVerneed<I386> : public Elf64Verneed {};
template <> struct ElfVernaux<I386> : public Elf64Vernaux {};
template <> struct ElfVerdef<I386> : public Elf64Verdef {};
template <> struct ElfVerdaux<I386> : public Elf64Verdaux {};
template <> struct ElfChdr<I386> : public Elf32Chdr {};
template <> struct ElfNhdr<I386> : public Elf64Nhdr {};
+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
+798
View File
@@ -0,0 +1,798 @@
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
using E = ARM64;
static void write_adr(u8 *buf, u64 val) {
u32 hi = (val & 0x1ffffc) << 3;
u32 lo = (val & 3) << 29;
*(u32 *)buf = (*(u32 *)buf & 0x9f00001f) | hi | lo;
}
// Returns [hi:lo] bits of val.
static u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
}
static u64 page(u64 val) {
return val & ~(u64)0xfff;
}
static void write_plt_header(Context<E> &ctx, u8 *buf) {
// Write PLT header
static const u32 plt0[] = {
0xa9bf7bf0, // stp x16, x30, [sp,#-16]!
0x90000010, // adrp x16, .got.plt[2]
0xf9400211, // ldr x17, [x16, .got.plt[2]]
0x91000210, // add x16, x16, .got.plt[2]
0xd61f0220, // br x17
0xd503201f, // nop
0xd503201f, // nop
0xd503201f, // nop
};
u64 gotplt = ctx.gotplt->shdr.sh_addr + 16;
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, plt0, sizeof(plt0));
write_adr(buf + 4, bits(page(gotplt) - page(plt + 4), 32, 12));
*(u32 *)(buf + 8) |= bits(gotplt, 11, 3) << 10;
*(u32 *)(buf + 12) |= ((gotplt) & 0xfff) << 10;
}
static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u8 *ent = buf + ctx.plt_hdr_size + sym.get_plt_idx(ctx) * ctx.plt_size;
static const u32 data[] = {
0x90000010, // adrp x16, .got.plt[n]
0xf9400211, // ldr x17, [x16, .got.plt[n]]
0x91000210, // add x16, x16, .got.plt[n]
0xd61f0220, // br x17
};
u64 gotplt = sym.get_gotplt_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_adr(ent, bits(page(gotplt) - page(plt), 32, 12));
*(u32 *)(ent + 4) |= bits(gotplt, 11, 3) << 10;
*(u32 *)(ent + 8) |= (gotplt & 0xfff) << 10;
}
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
write_plt_header(ctx, buf);
for (Symbol<E> *sym : symbols)
write_plt_entry(ctx, buf, *sym);
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
for (Symbol<E> *sym : symbols) {
u8 *ent = buf + sym->get_pltgot_idx(ctx) * ARM64::pltgot_size;
static const u32 data[] = {
0x90000010, // adrp x16, GOT[n]
0xf9400211, // ldr x17, [x16, GOT[n]]
0xd61f0220, // br x17
0xd503201f, // nop
};
u64 got = sym->get_got_addr(ctx);
u64 plt = sym->get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_adr(ent, bits(page(got) - page(plt), 32, 12));
*(u32 *)(ent + 4) |= bits(got, 11, 3) << 10;
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_AARCH64_ABS64:
*(u64 *)loc = val;
return;
case R_AARCH64_PREL32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
case R_AARCH64_PREL64:
*(u64 *)loc = val - this->shdr.sh_addr - offset;
return;
}
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<RangeExtensionRef> range_extn = get_range_extn();
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_AARCH64_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
frag_ref = &rel_fragments[frag_idx++];
auto overflow_check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + 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_AARCH64_ABS64:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u64 *)loc = S + A;
} else if (sym.is_imported) {
*dynrel++ = {P, R_AARCH64_ABS64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_AARCH64_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
}
continue;
case R_AARCH64_LDST8_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 0) << 10;
continue;
case R_AARCH64_LDST16_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 1) << 10;
continue;
case R_AARCH64_LDST32_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 2) << 10;
continue;
case R_AARCH64_LDST64_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 3) << 10;
continue;
case R_AARCH64_LDST128_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 4) << 10;
continue;
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;
case R_AARCH64_MOVW_UABS_G3:
*(u32 *)loc |= bits(S + A, 63, 48) << 5;
continue;
case R_AARCH64_ADR_GOT_PAGE: {
i64 val = page(G + GOT + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
continue;
}
case R_AARCH64_ADR_PREL_PG_HI21: {
i64 val = page(S + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
continue;
}
case R_AARCH64_ADR_PREL_LO21: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 20), (i64)1 << 20);
write_adr(loc, val);
continue;
}
case R_AARCH64_CALL26:
case R_AARCH64_JUMP26: {
if (sym.esym().is_undef_weak()) {
// On ARM, calling an weak undefined symbol jumps to the
// next instruction.
*(u32 *)loc |= 1;
continue;
}
i64 lo = -(1 << 27);
i64 hi = 1 << 27;
i64 val = S + A - P;
if (val < lo || hi <= val) {
RangeExtensionRef ref = range_extn[i];
val = output_section->thunks[ref.thunk_idx]->get_addr(ref.sym_idx) + A - P;
assert(lo <= val && val < hi);
}
*(u32 *)loc |= (val >> 2) & 0x3ffffff;
continue;
}
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 20), (i64)1 << 20);
*(u32 *)loc |= bits(val, 20, 2) << 5;
continue;
}
case R_AARCH64_PREL16: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 15), (i64)1 << 15);
*(u16 *)loc = val;
continue;
}
case R_AARCH64_PREL32: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 31), (i64)1 << 32);
*(u32 *)loc = val;
continue;
}
case R_AARCH64_PREL64:
*(u64 *)loc = S + A - P;
continue;
case R_AARCH64_LD64_GOT_LO12_NC:
*(u32 *)loc |= bits(G + GOT + A, 11, 3) << 10;
continue;
case R_AARCH64_LD64_GOTPAGE_LO15: {
i64 val = G + GOT + A - page(GOT);
overflow_check(val, 0, 1 << 15);
*(u32 *)loc |= bits(val, 14, 3) << 10;
continue;
}
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: {
i64 val = page(sym.get_gottp_addr(ctx) + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
continue;
}
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
*(u32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
continue;
case R_AARCH64_TLSLE_ADD_TPREL_HI12: {
i64 val = S + A - ctx.tls_begin + 16;
overflow_check(val, 0, (i64)1 << 24);
*(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;
case R_AARCH64_TLSGD_ADR_PAGE21: {
i64 val = page(sym.get_tlsgd_addr(ctx) + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
continue;
}
case R_AARCH64_TLSGD_ADD_LO12_NC:
*(u32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A, 11, 0) << 10;
continue;
case R_AARCH64_TLSDESC_ADR_PAGE21: {
if (ctx.relax_tlsdesc && !sym.is_imported) {
// adrp x0, 0 -> movz x0, #tls_ofset_hi, lsl #16
i64 val = (S + A - ctx.tls_begin + 16);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
*(u32 *)loc = 0xd2a00000 | (bits(val, 32, 16) << 5);
} else {
i64 val = page(sym.get_tlsdesc_addr(ctx) + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
}
continue;
}
case R_AARCH64_TLSDESC_LD64_LO12:
if (ctx.relax_tlsdesc && !sym.is_imported) {
// ldr x2, [x0] -> movk x0, #tls_ofset_lo
u32 offset_lo = (S + A - ctx.tls_begin + 16) & 0xffff;
*(u32 *)loc = 0xf2800000 | (offset_lo << 5);
} else {
*(u32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 3) << 10;
}
continue;
case R_AARCH64_TLSDESC_ADD_LO12:
if (ctx.relax_tlsdesc && !sym.is_imported) {
// add x0, x0, #0 -> nop
*(u32 *)loc = 0xd503201f;
} else {
*(u32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 0) << 10;
}
continue;
case R_AARCH64_TLSDESC_CALL:
if (ctx.relax_tlsdesc && !sym.is_imported) {
// blr x2 -> nop
*(u32 *)loc = 0xd503201f;
}
continue;
default:
unreachable();
}
#undef S
#undef A
#undef P
#undef G
#undef GOT
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_AARCH64_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
SectionFragment<E> *frag;
i64 addend;
std::tie(frag, addend) = get_fragment(ctx, rel);
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
switch (rel.r_type) {
case R_AARCH64_ABS64:
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;
continue;
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_AARCH64_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_AARCH64_ABS64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_AARCH64_ADR_GOT_PAGE:
case R_AARCH64_LD64_GOT_LO12_NC:
case R_AARCH64_LD64_GOTPAGE_LO15:
sym.flags |= NEEDS_GOT;
break;
case R_AARCH64_CALL26:
case R_AARCH64_JUMP26:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21:
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
sym.flags |= NEEDS_GOTTP;
break;
case R_AARCH64_ADR_PREL_PG_HI21: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_AARCH64_TLSGD_ADR_PAGE21:
sym.flags |= NEEDS_TLSGD;
break;
case R_AARCH64_TLSDESC_ADR_PAGE21:
case R_AARCH64_TLSDESC_LD64_LO12:
case R_AARCH64_TLSDESC_ADD_LO12:
if (!ctx.relax_tlsdesc || sym.is_imported)
sym.flags |= NEEDS_TLSDESC;
break;
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:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = -1;
sym->extra.thunk_sym_idx = -1;
sym->flags &= (u8)~NEEDS_RANGE_EXTN_THUNK;
}
}
static bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
InputSection<E> &isec, const ElfRel<E> &rel) {
// We always create a thunk for an absolute symbol conservatively
// because `shrink_sections` may increase a distance between a
// branch instruction and an absolute symbol. Branching to an
// absolute location is extremely rare in real code, though.
if (sym.is_absolute())
return false;
// Compute a distance between the relocated place and the symbol
// and check if they are within reach.
i64 S = sym.get_addr(ctx);
i64 A = rel.r_addend;
i64 P = isec.get_addr() + rel.r_offset;
i64 val = S + A - P;
return -(1 << 27) <= val && val < (1 << 27);
}
// We create a thunk no further than 100 MiB from any section.
static constexpr i64 MAX_DISTANCE = 100 * 1024 * 1024;
// We create a thunk for each 10 MiB input sections.
static constexpr i64 GROUP_SIZE = 10 * 1024 * 1024;
static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
std::span<InputSection<E> *> members = osec.members;
members[0]->offset = 0;
// Initialize input sections with very large dummy offsets so that
// sections that have got real offsets are separated from the ones
// without in the virtual address space.
tbb::parallel_for((i64)1, (i64)members.size(), [&](i64 i) {
members[i]->offset = 1 << 31;
});
// We create thunks from the beginning of the section to the end.
// We manage progress using four offsets which increase monotonically.
// The locations they point to are always A <= B <= C <= D.
i64 a = 0;
i64 b = 0;
i64 c = 0;
i64 d = 0;
i64 offset = 0;
while (b < members.size()) {
// Move D foward as far as we can jump from B to D.
while (d < members.size() && offset - members[b]->offset < MAX_DISTANCE) {
offset = align_to(offset, 1 << members[d]->p2align);
members[d]->offset = offset;
offset += members[d]->sh_size;
d++;
}
// Move C forward so that C is apart from B by GROUP_SIZE.
while (c < members.size() &&
members[c]->offset - members[b]->offset < GROUP_SIZE)
c++;
// Move A forward so that A is reachable from C.
if (c > 0) {
i64 c_end = members[c - 1]->offset + members[c - 1]->sh_size;
while (a < osec.thunks.size() &&
osec.thunks[a]->offset < c_end - MAX_DISTANCE)
reset_thunk(*osec.thunks[a++]);
}
// Create a thunk for input sections between B and C and place it at D.
osec.thunks.emplace_back(new RangeExtensionThunk<E>{osec});
RangeExtensionThunk<E> &thunk = *osec.thunks.back();
thunk.thunk_idx = osec.thunks.size() - 1;
thunk.offset = offset;
// Scan relocations between B and C to collect symbols that need thunks.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](InputSection<E> *isec) {
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
std::vector<RangeExtensionRef> &range_extn = isec->get_range_extn();
range_extn.resize(rels.size());
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type != R_AARCH64_CALL26 && rel.r_type != R_AARCH64_JUMP26)
continue;
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
// Skip if the destination is within reach.
if (is_reachable(ctx, sym, *isec, rel))
continue;
// If the symbol is already in another thunk, reuse it.
if (sym.extra.thunk_idx != -1) {
range_extn[i] = {sym.extra.thunk_idx, sym.extra.thunk_sym_idx};
continue;
}
// Otherwise, add the symbol to this thunk if it's not added already.
range_extn[i] = {thunk.thunk_idx, -1};
if (!(sym.flags.fetch_or(NEEDS_RANGE_EXTN_THUNK) &
NEEDS_RANGE_EXTN_THUNK)) {
std::scoped_lock lock(thunk.mu);
thunk.symbols.push_back(&sym);
}
}
});
// Now that we know the number of symbols in the thunk, we can compute
// its size.
offset += thunk.size();
// Sort symbols added to the thunk to make the output deterministic.
sort(thunk.symbols, [](Symbol<E> *a, Symbol<E> *b) { return *a < *b; });
// Assign offsets within the thunk to the symbols.
for (i64 i = 0; Symbol<E> *sym : thunk.symbols) {
sym->extra.thunk_idx = thunk.thunk_idx;
sym->extra.thunk_sym_idx = i++;
}
// Scan relocations again to fix symbol offsets in the last thunk.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](InputSection<E> *isec) {
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
std::span<RangeExtensionRef> range_extn = isec->get_range_extn();
for (i64 i = 0; i < rels.size(); i++) {
if (range_extn[i].thunk_idx == thunk.thunk_idx) {
Symbol<E> &sym = *isec->file.symbols[rels[i].r_sym];
range_extn[i].sym_idx = sym.extra.thunk_sym_idx;
}
}
});
// Move B forward to point to the begining of the next group.
b = c;
}
while (a < osec.thunks.size())
reset_thunk(*osec.thunks[a++]);
osec.shdr.sh_size = offset;
}
static void gc_thunk_symbols(Context<E> &ctx, OutputSection<E> &osec) {
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec.thunks) {
i64 sz = thunk->symbols.size();
thunk->symbol_map.resize(sz);
thunk->used.reset(new std::atomic_bool[sz]{});
}
// Mark referenced thunk symbols
tbb::parallel_for_each(osec.members, [&](InputSection<E> *isec) {
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
std::span<RangeExtensionRef> range_extn = isec->get_range_extn();
for (i64 i = 0; i < rels.size(); i++) {
RangeExtensionRef &ref = range_extn[i];
if (ref.thunk_idx == -1)
continue;
Symbol<E> &sym = *isec->file.symbols[rels[i].r_sym];
if (!is_reachable(ctx, sym, *isec, rels[i]))
osec.thunks[ref.thunk_idx]->used[ref.sym_idx] = true;
}
});
// Remove unreferenced thunk symbols
tbb::parallel_for_each(osec.thunks,
[&](std::unique_ptr<RangeExtensionThunk<E>> &thunk) {
i64 i = 0;
for (i64 j = 0; j < thunk->symbols.size(); j++) {
if (thunk->used[j]) {
thunk->symbol_map[j] = i;
thunk->symbols[i] = thunk->symbols[j];
i++;
}
}
thunk->symbols.resize(i);
});
}
static void shrink_section(Context<E> &ctx, OutputSection<E> &osec) {
std::span<std::unique_ptr<RangeExtensionThunk<E>>> thunks = osec.thunks;
std::span<InputSection<E> *> members = osec.members;
i64 offset = 0;
auto add_thunk = [&] {
thunks[0]->offset = offset;
offset += thunks[0]->size();
thunks = thunks.subspan(1);
};
auto add_isec = [&] {
offset = align_to(offset, 1 << members[0]->p2align);
members[0]->offset = offset;
offset += members[0]->sh_size;
members = members.subspan(1);
};
while (!thunks.empty() && !members.empty()) {
if (thunks[0]->offset < members[0]->offset)
add_thunk();
else
add_isec();
}
while (!thunks.empty())
add_thunk();
while (!members.empty())
add_isec();
assert(offset <= osec.shdr.sh_size);
osec.shdr.sh_size = offset;
}
// ARM64's call/jump instructions take 27 bits displacement, so they
// can refer only up to ±128 MiB. If a branch target is further than
// that, we need to let it branch to a linker-synthesized code
// sequence that construct a full 32 bit address in a register and
// jump there. That linker-synthesized code is called "thunk".
i64 create_range_extension_thunks(Context<E> &ctx) {
Timer t(ctx, "create_range_extension_thunks");
for (ObjectFile<E> *file : ctx.objs)
file->range_extn.resize(file->sections.size());
// First, we create thunks with a pessimistic assumption that all
// out-of-section relocations would need thunks. To do so, we start
// with an initial layout in which output sections are separated far
// apart.
for (i64 i = 0; Chunk<E> *chunk : ctx.chunks)
if (chunk->shdr.sh_flags & SHF_ALLOC)
chunk->shdr.sh_addr = i++ << 31;
std::vector<OutputSection<E> *> sections;
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
if (!osec->members.empty() && (osec->shdr.sh_flags & SHF_EXECINSTR))
sections.push_back(osec.get());
for (OutputSection<E> *osec : sections)
create_thunks(ctx, *osec);
// Recompute file layout.
set_osec_offsets(ctx);
// Based on the current file layout, remove thunk symbols that turned
// out to be unnecessary.
tbb::parallel_for_each(sections, [&](OutputSection<E> *osec) {
gc_thunk_symbols(ctx, *osec);
});
// Recompute output section sizes that contain thunks. New section
// sizes must be equal to or smaller than previous values, so all
// relocations that were previously reachable will still be reachable
// after this step.
for (OutputSection<E> *osec : sections)
shrink_section(ctx, *osec);
// Compute the final layout.
return set_osec_offsets(ctx);
}
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.shdr.sh_offset + offset;
static const u32 data[] = {
0x90000010, // adrp x16, 0 # R_AARCH64_ADR_PREL_PG_HI21
0x91000210, // add x16, x16 # R_AARCH64_ADD_ABS_LO12_NC
0xd61f0200, // br x16
};
static_assert(ENTRY_SIZE == sizeof(data));
for (i64 i = 0; i < symbols.size(); i++) {
u64 S = symbols[i]->get_addr(ctx);
u64 P = output_section.shdr.sh_addr + offset + i * ENTRY_SIZE;
u8 *loc = buf + i * ENTRY_SIZE;
memcpy(loc , data, sizeof(data));
write_adr(loc, bits(page(S) - page(P), 32, 12));
*(u32 *)(loc + 4) |= bits(S, 11, 0) << 10;
}
}
void write_thunks(Context<E> &ctx) {
Timer t(ctx, "write_thunks");
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
tbb::parallel_for_each(osec->thunks,
[&](std::unique_ptr<RangeExtensionThunk<E>> &thunk) {
thunk->copy_buf(ctx);
});
});
}
} // namespace mold::elf
+555
View File
@@ -0,0 +1,555 @@
#include "mold.h"
namespace mold::elf {
using E = I386;
static void write_plt_header(Context<E> &ctx, u8 *buf) {
if (ctx.arg.pic) {
static const u8 plt0[] = {
0xff, 0xb3, 0, 0, 0, 0, // pushl GOTPLT+4(%ebx)
0xff, 0xa3, 0, 0, 0, 0, // jmp *GOTPLT+8(%ebx)
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 8;
} else {
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushl GOTPLT+4
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+8
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr + 8;
}
}
static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
i64 idx) {
u8 *ent = buf + ctx.plt_hdr_size + sym.get_plt_idx(ctx) * ctx.plt_size;
if (ctx.arg.pic) {
static const u8 data[] = {
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0x68, 0, 0, 0, 0, // pushl $reloc_offset
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
};
memcpy(ent, data, sizeof(data));
*(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
0x68, 0, 0, 0, 0, // pushl $reloc_offset
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx);
}
*(u32 *)(ent + 7) = idx * sizeof(ElfRel<E>);
*(u32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx) - 16;
}
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
write_plt_header(ctx, buf);
for (i64 i = 0; i < symbols.size(); i++)
write_plt_entry(ctx, buf, *symbols[i], i);
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
if (ctx.arg.pic) {
static const u8 data[] = {
0xff, 0xa3, 0, 0, 0, 0, // jmp *foo@GOT(%ebx)
0x66, 0x90, // nop
};
for (i64 i = 0; i < symbols.size(); i++) {
u8 *ent = buf + i * sizeof(data);
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.got->shdr.sh_addr;
}
} else {
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x66, 0x90, // nop
};
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);
}
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_386_NONE:
return;
case R_386_32:
*(u32 *)loc = val;
return;
case R_386_PC32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
}
unreachable();
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
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_386_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
frag_ref = &rel_fragments[frag_idx++];
auto overflow_check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto write8 = [&](u64 val) {
overflow_check(val, 0, 1 << 8);
*loc = val;
};
auto write8s = [&](u64 val) {
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
};
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
};
auto write32 = [&](u64 val) {
*(u32 *)loc = val;
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : this->get_addend(rel))
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_386_8:
write8(S + A);
continue;
case R_386_16:
write16(S + A);
continue;
case R_386_32:
if (sym.is_absolute() || !ctx.arg.pic) {
write32(S + A);
} else if (sym.is_imported) {
*dynrel++ = {P, R_386_32, (u32)sym.get_dynsym_idx(ctx)};
write32(A);
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_386_RELATIVE, 0};
write32(S + A);
}
continue;
case R_386_PC8:
write8s(S + A);
continue;
case R_386_PC16:
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(G + A);
continue;
case R_386_GOTOFF:
write32(S + A - GOT);
continue;
case R_386_GOTPC:
write32(GOT + A - P);
continue;
case R_386_TLS_GOTIE:
write32(sym.get_gottp_addr(ctx) + A - GOT);
continue;
case R_386_TLS_LE:
write32(S + A - ctx.tls_end);
continue;
case R_386_TLS_IE:
write32(sym.get_gottp_addr(ctx) + A);
continue;
case R_386_TLS_GD:
if (sym.get_tlsgd_idx(ctx) == -1) {
// Relax GD to LE
switch (rels[i + 1].r_type) {
case R_386_PLT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %rax
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
*(u32 *)(loc + 5) = ctx.tls_end - S - A;
break;
}
case R_386_GOT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %rax
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 2, insn, sizeof(insn));
*(u32 *)(loc + 6) = ctx.tls_end - S - A;
break;
}
default:
unreachable();
}
i++;
} else {
write32(sym.get_tlsgd_addr(ctx) + A - GOT);
}
continue;
case R_386_TLS_LDM:
if (ctx.got->tlsld_idx == -1) {
// Relax LD to LE
switch (rels[i + 1].r_type) {
case R_386_PLT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x8d, 0x74, 0x26, 0x00, // lea (%esi,1), %esi
0x90, // nop
};
memcpy(loc - 2, insn, sizeof(insn));
break;
}
case R_386_GOT32: {
static const u8 insn[] = {
0x65, 0xa1, 0, 0, 0, 0, // mov %gs:0, %eax
0x8d, 0x74, 0x26, 0x00, // lea (%esi,1), %esi
0x66, 0x90,
};
memcpy(loc - 2, insn, sizeof(insn));
break;
}
default:
unreachable();
}
i++;
} else {
write32(ctx.got->get_tlsld_addr(ctx) + A - GOT);
}
continue;
case R_386_TLS_LDO_32:
if (ctx.got->tlsld_idx == -1)
write32(S + A - ctx.tls_end);
else
write32(S + A - ctx.tls_begin);
continue;
case R_386_SIZE32:
write32(sym.esym().st_size + A);
continue;
case R_386_TLS_GOTDESC:
if (sym.get_tlsdesc_idx(ctx) == -1) {
static const u8 insn[] = {
0x8d, 0x05, 0, 0, 0, 0, // lea 0, %eax
};
memcpy(loc - 2, insn, sizeof(insn));
write32(S + A - ctx.tls_end);
} else {
write32(sym.get_tlsdesc_addr(ctx) + A - GOT);
}
continue;
case R_386_TLS_DESC_CALL:
if (ctx.arg.relax && !ctx.arg.shared) {
// call *(%rax) -> nop
loc[0] = 0x66;
loc[1] = 0x90;
}
continue;
default:
unreachable();
}
#undef S
#undef A
#undef P
#undef G
#undef GOT
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_386_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);
auto overflow_check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto write8 = [&](u64 val) {
overflow_check(val, 0, 1 << 8);
*loc = val;
};
auto write8s = [&](u64 val) {
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
};
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
};
#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:
write8(S + A);
continue;
case R_386_16:
write16(S + A);
continue;
case R_386_32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
continue;
case R_386_PC8:
write8s(S + A);
continue;
case R_386_PC16:
write16s(S + A);
continue;
case R_386_PC32:
*(u32 *)loc = S + A;
continue;
case R_386_GOTPC:
*(u32 *)loc = GOT + A;
continue;
case R_386_GOTOFF:
*(u32 *)loc = S + A - GOT;
continue;
case R_386_TLS_LDO_32:
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;
continue;
default:
unreachable();
}
#undef S
#undef A
#undef GOT
}
}
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_386_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_386_8:
case R_386_16: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_386_32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_386_PC8:
case R_386_PC16: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_386_PC32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, DYNREL, DYNREL }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_386_GOT32:
case R_386_GOT32X:
case R_386_GOTPC:
sym.flags |= NEEDS_GOT;
break;
case R_386_PLT32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, NONE, PLT, PLT }, // DSO
{ NONE, NONE, PLT, PLT }, // PIE
{ NONE, NONE, PLT, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_386_TLS_GOTIE:
case R_386_TLS_LE:
case R_386_TLS_IE:
sym.flags |= NEEDS_GOTTP;
break;
case R_386_TLS_GD:
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
if (u32 ty = rels[i + 1].r_type;
ty != R_386_PLT32 && ty != R_386_GOT32)
Fatal(ctx) << *this << ": TLS_GD reloc must be followed by PLT or GOT32";
if (ctx.arg.relax && !ctx.arg.shared && !sym.is_imported)
i++;
else
sym.flags |= NEEDS_TLSGD;
break;
case R_386_TLS_LDM:
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
if (u32 ty = rels[i + 1].r_type;
ty != R_386_PLT32 && ty != R_386_GOT32)
Fatal(ctx) << *this << ": TLS_LDM reloc must be followed by PLT or GOT32";
if (ctx.arg.relax && !ctx.arg.shared)
i++;
else
ctx.needs_tlsld = true;
break;
case R_386_TLS_GOTDESC:
if (!ctx.arg.relax || ctx.arg.shared)
sym.flags |= NEEDS_TLSDESC;
break;
case R_386_GOTOFF:
case R_386_TLS_LDO_32:
case R_386_SIZE32:
case R_386_TLS_DESC_CALL:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
} // namespace mold::elf
+813
View File
@@ -0,0 +1,813 @@
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::elf {
using E = RISCV64;
static u32 bit(u32 val, i64 pos) {
return (val >> pos) & 1;
}
// Returns [hi:lo] bits of val.
static u32 bits(u32 val, i64 hi, i64 lo) {
return (val >> lo) & ((1LL << (hi - lo + 1)) - 1);
}
static u32 itype(u32 val) {
return val << 20;
}
static u32 stype(u32 val) {
return bits(val, 11, 5) << 25 | bits(val, 4, 0) << 7;
}
static u32 btype(u32 val) {
return bit(val, 12) << 31 | bits(val, 10, 5) << 25 |
bits(val, 4, 1) << 8 | bit(val, 11) << 7;
}
static u32 utype(u32 val) {
// U-type instructions are used in combination with I-type
// instructions. U-type insn sets an immediate to the upper 20-bits
// of a register. I-type insn sign-extends a 12-bits immediate and
// add it to a register value to construct a complete value. 0x800
// is added here to compensate for the sign-extension.
return bits(val + 0x800, 31, 12) << 12;
}
static u32 jtype(u32 val) {
return bit(val, 20) << 31 | bits(val, 10, 1) << 21 |
bit(val, 11) << 20 | bits(val, 19, 12) << 12;
}
static u32 cbtype(u32 val) {
return bit(val, 8) << 12 | bit(val, 4) << 11 | bit(val, 3) << 10 |
bit(val, 7) << 6 | bit(val, 6) << 5 | bit(val, 2) << 4 |
bit(val, 1) << 3 | bit(val, 5) << 2;
}
static u32 cjtype(u32 val) {
return bit(val, 11) << 12 | bit(val, 4) << 11 | bit(val, 9) << 10 |
bit(val, 8) << 9 | bit(val, 10) << 8 | bit(val, 6) << 7 |
bit(val, 7) << 6 | bit(val, 3) << 5 | bit(val, 2) << 4 |
bit(val, 1) << 3 | bit(val, 5) << 2;
}
static void write_itype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'11111'111'11111'1111111;
*loc = (*loc & mask) | itype(val);
}
static void write_stype(u32 *loc, u32 val) {
u32 mask = 0b000000'11111'11111'111'00000'1111111;
*loc = (*loc & mask) | stype(val);
}
static void write_btype(u32 *loc, u32 val) {
u32 mask = 0b000000'11111'11111'111'00000'1111111;
*loc = (*loc & mask) | btype(val);
}
static void write_utype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'00000'000'11111'1111111;
*loc = (*loc & mask) | utype(val);
}
static void write_jtype(u32 *loc, u32 val) {
u32 mask = 0b000000'00000'00000'000'11111'1111111;
*loc = (*loc & mask) | jtype(val);
}
static void write_cbtype(u16 *loc, u32 val) {
u32 mask = 0b1110001110000011;
*loc = (*loc & mask) | cbtype(val);
}
static void write_cjtype(u16 *loc, u32 val) {
u32 mask = 0b1110000000000011;
*loc = (*loc & mask) | cjtype(val);
}
static void write_plt_header(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + ctx.plt->shdr.sh_offset);
static const u32 plt0[] = {
0x00000397, // auipc t2, %pcrel_hi(.got.plt)
0x41c30333, // sub t1, t1, t3 # .plt entry + hdr + 12
0x0003be03, // ld t3, %pcrel_lo(1b)(t2) # _dl_runtime_resolve
0xfd430313, // addi t1, t1, -44 # .plt entry
0x00038293, // addi t0, t2, %pcrel_lo(1b) # &.got.plt
0x00135313, // srli t1, t1, 1 # .plt entry offset
0x0082b283, // ld t0, 8(t0) # link map
0x000e0067, // jr t3
};
u64 gotplt = ctx.gotplt->shdr.sh_addr;
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, plt0, sizeof(plt0));
write_utype(buf, gotplt - plt);
write_itype(buf + 2, gotplt - plt);
write_itype(buf + 4, gotplt - plt);
}
static void write_plt_entry(Context<E> &ctx, Symbol<E> &sym) {
u32 *ent = (u32 *)(ctx.buf + ctx.plt->shdr.sh_offset + ctx.plt_hdr_size +
sym.get_plt_idx(ctx) * ctx.plt_size);
static const u32 data[] = {
0x00000e17, // auipc t3, %pcrel_hi([email protected])
0x000e3e03, // ld t3, %pcrel_lo(1b)(t3)
0x000e0367, // jalr t1, t3
0x00000013, // nop
};
u64 gotplt = sym.get_gotplt_addr(ctx);
u64 plt = sym.get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_utype(ent, gotplt - plt);
write_itype(ent + 1, gotplt - plt);
}
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
write_plt_header(ctx);
for (Symbol<E> *sym : symbols)
write_plt_entry(ctx, *sym);
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->shdr.sh_offset);
static const u32 data[] = {
0x00000e17, // auipc t3, %pcrel_hi([email protected])
0x000e3e03, // ld t3, %pcrel_lo(1b)(t3)
0x000e0367, // jalr t1, t3
0x00000013, // nop
};
for (Symbol<E> *sym : symbols) {
u32 *ent = buf + sym->get_pltgot_idx(ctx) * 4;
u64 got = sym->get_got_addr(ctx);
u64 plt = sym->get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_utype(ent, got - plt);
write_itype(ent + 1, got - plt);
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_RISCV_ADD32:
*(u32 *)loc += val;
return;
case R_RISCV_SUB8:
*loc -= val;
return;
case R_RISCV_SUB16:
*(u16 *)loc -= val;
return;
case R_RISCV_SUB32:
*(u32 *)loc -= val;
return;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - val) & 0b0011'1111);
return;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | (val & 0b0011'1111);
return;
case R_RISCV_SET8:
*loc = val;
return;
case R_RISCV_SET16:
*(u16 *)loc = val;
return;
case R_RISCV_32_PCREL:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
}
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<i32> r_deltas = get_r_deltas();
i64 frag_idx = 0;
if (ctx.reldyn)
dynrel = (ElfRel<E> *)(ctx.buf + ctx.reldyn->shdr.sh_offset +
file.reldyn_offset + this->reldyn_offset);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE || rel.r_type == R_RISCV_RELAX ||
rel.r_type == R_RISCV_ALIGN)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
i64 r_offset = rel.r_offset + r_deltas[i];
u8 *loc = base + r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
frag_ref = &rel_fragments[frag_idx++];
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
switch (rel.r_type) {
case R_RISCV_32:
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u64 *)loc = S + A;
} else if (sym.is_imported) {
*dynrel++ = {P, R_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
}
break;
case R_RISCV_TLS_DTPMOD32:
case R_RISCV_TLS_DTPMOD64:
case R_RISCV_TLS_DTPREL32:
case R_RISCV_TLS_DTPREL64:
case R_RISCV_TLS_TPREL32:
case R_RISCV_TLS_TPREL64:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_BRANCH:
write_btype((u32 *)loc, S + A - P);
break;
case R_RISCV_JAL:
write_jtype((u32 *)loc, S + A - P);
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
if (r_deltas[i + 1] - r_deltas[i] != 0) {
// auipc + jalr -> jal
assert(r_deltas[i + 1] - r_deltas[i] == -4);
u32 jalr = *(u32 *)&contents[rels[i].r_offset + 4];
*(u32 *)loc = (0b11111'000000 & jalr) | 0b101111;
write_jtype((u32 *)loc, S + A - P);
} else {
u64 val = sym.esym().is_undef_weak() ? 0 : S + A - P;
write_utype((u32 *)loc, val);
write_itype((u32 *)(loc + 4), val);
}
break;
}
case R_RISCV_GOT_HI20:
*(u32 *)loc = G + GOT + A - P;
break;
case R_RISCV_TLS_GOT_HI20:
*(u32 *)loc = sym.get_gottp_addr(ctx) + A - P;
break;
case R_RISCV_TLS_GD_HI20:
*(u32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
break;
case R_RISCV_PCREL_HI20:
if (sym.esym().is_undef_weak()) {
// Calling an undefined weak symbol does not make sense.
// We make such call into an infinite loop. This should
// help debugging of a faulty program.
*(u32 *)loc = P;
} else {
*(u32 *)loc = S + A - P;
}
break;
case R_RISCV_PCREL_LO12_I:
assert(sym.get_input_section() == this);
assert(sym.value < r_offset);
write_itype((u32 *)loc, *(u32 *)(base + sym.value));
break;
case R_RISCV_LO12_I:
case R_RISCV_TPREL_LO12_I:
write_itype((u32 *)loc, S + A);
break;
case R_RISCV_PCREL_LO12_S:
assert(sym.get_input_section() == this);
assert(sym.value < r_offset);
write_stype((u32 *)loc, *(u32 *)(base + sym.value));
break;
case R_RISCV_LO12_S:
case R_RISCV_TPREL_LO12_S:
write_stype((u32 *)loc, S + A);
break;
case R_RISCV_HI20:
write_utype((u32 *)loc, S + A);
break;
case R_RISCV_TPREL_HI20:
write_utype((u32 *)loc, S + A - ctx.tls_begin);
break;
case R_RISCV_TPREL_ADD:
break;
case R_RISCV_ADD8:
loc += S + A;
break;
case R_RISCV_ADD16:
*(u16 *)loc += S + A;
break;
case R_RISCV_ADD32:
*(u32 *)loc += S + A;
break;
case R_RISCV_ADD64:
*(u64 *)loc += S + A;
break;
case R_RISCV_SUB8:
loc -= S + A;
break;
case R_RISCV_SUB16:
*(u16 *)loc -= S + A;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= S + A;
break;
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
break;
case R_RISCV_RVC_BRANCH:
write_cbtype((u16 *)loc, S + A - P);
break;
case R_RISCV_RVC_JUMP:
write_cjtype((u16 *)loc, S + A - P);
break;
case R_RISCV_RVC_LUI:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
break;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
break;
case R_RISCV_SET8:
*loc = S + A;
break;
case R_RISCV_SET16:
*(u16 *)loc = S + A;
break;
case R_RISCV_SET32:
*(u32 *)loc = S + A;
break;
case R_RISCV_32_PCREL:
*(u32 *)loc = S + A - P;
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
#undef S
#undef A
#undef P
#undef G
#undef GOT
}
// In the above loop, PC-relative HI20 relocations overwrote
// instructions with full 32-bit values to allow their corresponding
// PCREL_LO12 relocations to read their values. This loop restore
// the original instructions.
for (i64 i = 0; i < rels.size(); i++) {
switch (rels[i].r_type) {
case R_RISCV_GOT_HI20:
case R_RISCV_PCREL_HI20:
case R_RISCV_TLS_GOT_HI20:
case R_RISCV_TLS_GD_HI20: {
u32 *loc = (u32 *)(base + rels[i].r_offset + r_deltas[i]);
u32 val = *loc;
*loc = *(u32 *)&contents[rels[i].r_offset];
write_utype(loc, val);
}
}
}
}
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
SectionFragment<E> *frag;
i64 addend;
std::tie(frag, addend) = get_fragment(ctx, rel);
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
switch (rel.r_type) {
case R_RISCV_32:
*(u32 *)loc = S + A;
break;
case R_RISCV_64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
break;
case R_RISCV_ADD8:
*loc += S + A;
break;
case R_RISCV_ADD16:
*(u16 *)loc += S + A;
break;
case R_RISCV_ADD32:
*(u32 *)loc += S + A;
break;
case R_RISCV_ADD64:
*(u64 *)loc += S + A;
break;
case R_RISCV_SUB8:
*loc -= S + A;
break;
case R_RISCV_SUB16:
*(u16 *)loc -= S + A;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= S + A;
break;
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
break;
case R_RISCV_SET6:
*loc = (*loc & 0b1100'0000) | ((S + A) & 0b0011'1111);
break;
case R_RISCV_SET8:
*loc = S + A;
break;
case R_RISCV_SET16:
*(u16 *)loc = S + A;
break;
case R_RISCV_SET32:
*(u32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
#undef S
#undef A
}
}
template <>
void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
// A non-alloc section isn't relaxed, so just copy it as one big chunk.
if (!(shdr().sh_flags & SHF_ALLOC)) {
if (is_compressed())
uncompress(ctx, buf);
else
memcpy(buf, contents.data(), contents.size());
return;
}
// Memory-allocated sections may be relaxed, so copy each segment
// individually.
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<i32> r_deltas = get_r_deltas();
i64 pos = 0;
for (i64 i = 0; i < rels.size(); i++) {
i64 delta = r_deltas[i + 1] - r_deltas[i];
if (delta == 0)
continue;
assert(delta < 0);
const ElfRel<E> &r = rels[i];
memcpy(buf, contents.data() + pos, r.r_offset - pos);
buf += r.r_offset - pos;
pos = r.r_offset - delta;
}
memcpy(buf, contents.data() + pos, contents.size() - pos);
}
template <>
void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
if (rel.r_type == R_RISCV_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
switch (rel.r_type) {
case R_RISCV_32:
case R_RISCV_HI20: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_RISCV_64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_RISCV_TLS_DTPMOD32:
case R_RISCV_TLS_DTPMOD64:
case R_RISCV_TLS_DTPREL32:
case R_RISCV_TLS_DTPREL64:
case R_RISCV_TLS_TPREL32:
case R_RISCV_TLS_TPREL64:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_BRANCH:
case R_RISCV_JAL:
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT:
if (sym.is_imported)
sym.flags |= NEEDS_PLT;
break;
case R_RISCV_GOT_HI20:
sym.flags |= NEEDS_GOT;
break;
case R_RISCV_TLS_GOT_HI20:
ctx.has_gottp_rel = true;
sym.flags |= NEEDS_GOTTP;
break;
case R_RISCV_TLS_GD_HI20:
sym.flags |= NEEDS_TLSGD;
break;
case R_RISCV_PCREL_HI20:
case R_RISCV_PCREL_LO12_I:
case R_RISCV_PCREL_LO12_S:
case R_RISCV_LO12_I:
case R_RISCV_LO12_S:
case R_RISCV_TPREL_HI20:
case R_RISCV_TPREL_LO12_I:
case R_RISCV_TPREL_LO12_S:
case R_RISCV_TPREL_ADD:
case R_RISCV_ADD8:
case R_RISCV_ADD16:
case R_RISCV_ADD32:
case R_RISCV_ADD64:
case R_RISCV_SUB8:
case R_RISCV_SUB16:
case R_RISCV_SUB32:
case R_RISCV_SUB64:
case R_RISCV_ALIGN:
break;
case R_RISCV_RVC_BRANCH:
case R_RISCV_RVC_JUMP:
break;
case R_RISCV_RVC_LUI:
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_RELAX:
case R_RISCV_SUB6:
case R_RISCV_SET6:
case R_RISCV_SET8:
case R_RISCV_SET16:
case R_RISCV_SET32:
break;
case R_RISCV_32_PCREL: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
static bool is_resizable(Context<E> &ctx, InputSection<E> *isec) {
return isec && (isec->shdr().sh_flags & SHF_ALLOC);
}
// Initializes r_deltas and sorted_symbols.
static void initialize_storage(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->r_deltas.resize(file->sections.size());
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(ctx, isec.get()))
isec->get_r_deltas().resize(isec->get_rels(ctx).size() + 1);
file->sorted_symbols.resize(file->sections.size());
for (Symbol<E> *sym : file->symbols)
if (sym->file == file)
if (InputSection<E> *isec = sym->get_input_section())
file->sorted_symbols[isec->shndx].push_back(sym);
for (std::vector<Symbol<E> *> &vec : file->sorted_symbols)
sort(vec, [](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; });
});
}
// Returns the distance between a relocated place and a symbol.
static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
InputSection<E> &isec, const ElfRel<E> &rel) {
// We handle absolute symbols as if they were infinitely far away
// because `relax_section` may increase a distance between a branch
// instruction and an absolute symbol. Branching to an absolute
// location is extremely rare in real code, though.
if (sym.is_absolute())
return INT32_MAX;
// Likewise, relocations against weak undefined symbols won't be relaxed.
if (sym.esym().is_undef_weak())
return INT32_MAX;
// Compute a distance between the relocated place and the symbol.
i64 S = sym.get_addr(ctx);
i64 A = rel.r_addend;
i64 P = isec.get_addr() + rel.r_offset;
return S + A - P;
}
// Relax R_RISCV_CALL and R_RISCV_CALL_PLT relocations.
static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
std::span<i32> r_deltas = isec.get_r_deltas();
std::span<Symbol<E> *> syms = isec.get_sorted_symbols();
i64 delta = 0;
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &r = rels[i];
i64 delta2 = 0;
r_deltas[i] += delta;
switch (r.r_type) {
case R_RISCV_ALIGN: {
// R_RISCV_ALIGN refers NOP instructions. We need to eliminate
// some or all of the instructions so that the instruction that
// immediately follows the NOPs is aligned to a specified
// alignment boundary.
u64 loc = isec.get_addr() + r.r_offset + delta;
// The total bytes of NOPs is stored to r_addend, so the next
// instruction is r_addend away.
u64 next_loc = loc + r.r_addend;
u64 alignment = (std::popcount<u64>(r.r_addend) == 1)
? r.r_addend : next_power_of_two(r.r_addend);
if (next_loc % alignment)
delta2 = align_to(loc, alignment) - next_loc;
break;
}
case R_RISCV_CALL:
case R_RISCV_CALL_PLT:
if (ctx.arg.relax) {
if (i == rels.size() - 1 || rels[i + 1].r_type != R_RISCV_RELAX)
break;
// If the jump target is within ±1 MiB, we can replace AUIPC+JALR
// with JAL, saving 4 bytes.
Symbol<E> &sym = *isec.file.symbols[r.r_sym];
i64 dist = compute_distance(ctx, sym, isec, r);
if (dist % 2 == 0 && -(1 << 20) <= dist && dist < (1 << 20))
delta2 = -4;
}
}
if (delta2 == 0)
continue;
while (!syms.empty() && syms[0]->value <= r.r_offset) {
syms[0]->value += delta;
syms = syms.subspan(1);
}
delta += delta2;
}
for (Symbol<E> *sym : syms)
sym->value += delta;
r_deltas[rels.size()] += delta;
isec.sh_size += delta;
}
// RISC-V instructions are 16 or 32 bits long, so immediates encoded
// in instructions can't be 32 bits long. Therefore, branch and load
// instructions can't refer the 4 GiB address space unlike x86-64.
// In fact, JAL (jump and link) instruction can jump to only within
// ±1 MiB as their immediate is only 21 bits long.
//
// If you want to jump to somewhere further than that, you need to
// construct a full 32-bit offset using multiple instruction and
// branch to that place (e.g. AUIPC and JALR instead of JAL).
// In this comment, we refer instructions such as JAL as the short
// encoding and ones such as AUIPC+JALR as the long encoding.
//
// By default, compiler always uses the long encoding so that branch
// targets are always encodable. This is a safe bet for them but
// may result in inefficient code. Therefore, the RISC-V psABI defines
// a mechanism for the linker to replace long encoding instructions
// with short ones, shrinking the section and increasing the code
// density.
//
// This is contrary to the psABIs for the other RISC processors such as
// ARM64. Typically, they use short instructions by default, and a
// linker creates so-called "thunks" to extend ranges of short jumps.
// On RISC-V, instructions are in the long encoding by default, and
// the linker shrinks them if it can.
//
// When we shrink a section, we need to adjust relocation offsets and
// symbol values. For example, if we replace AUIPC+JALR with JAL
// (which saves 4 bytes), all relocations pointing to anywhere after
// that location need to be shifted by 4. In addition to that, any
// symbol that refers anywhere after that locatioin need to be shifted
// by 4 bytes as well.
//
// For relocations, we use `r_deltas` array to memorize how many bytes
// have be adjusted. For symbols, we directly mutate their `value`
// member.
//
// This operation seems to be optional, as by default instructions are
// using the long encoding, but calling this function is actually
// mandatory because of R_RISCV_ALIGN. R_RISCV_ALIGN relocation is a
// directive to the linker to align the location referred to by the
// relocation to a specified byte boundary. We at least have to
// interpret them satisfy the constraints imposed by R_RISCV_ALIGN
// relocations.
i64 riscv_resize_sections(Context<E> &ctx) {
Timer t(ctx, "riscv_resize_sections");
initialize_storage(ctx);
// Find R_RISCV_CALL AND R_RISCV_CALL_PLT that can be relaxed.
// This step should only shrink sections.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (is_resizable(ctx, isec.get()))
relax_section(ctx, *isec);
});
// Re-compute section offset again to finalize them.
compute_section_sizes(ctx);
return set_osec_offsets(ctx);
}
} // namespace mold::elf
+830
View File
@@ -0,0 +1,830 @@
#include "mold.h"
namespace mold::elf {
using E = X86_64;
// The compact PLT format is used when `-z now` is given. If the flag
// is given, all PLT symbols are resolved eagerly on startup, so we
// can omit code for lazy symbol resolution from PLT in that case.
static void write_compact_plt(Context<E> &ctx) {
u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x66, 0x90, // nop
};
for (Symbol<E> *sym : ctx.plt->symbols) {
u8 *ent = buf + sym->get_plt_idx(ctx) * ctx.plt_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
}
}
// The IBTPLT is a security-enhanced version of the regular PLT.
// It uses Indirect Branch Tracking (IBT) feature which is part of
// Intel Control-Flow Enforcement (CET).
//
// Note that our IBTPLT instruction sequence is different from the one
// used in GNU ld. GNU's IBTPLT implementation uses two separate
// sections (.plt and .plt.sec) in which one PLT entry takes 32 bytes
// in total. Our PLT consists of just .plt and each entry is 16 bytes
// long.
//
// 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 + 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
};
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) = relplt_idx++;
*(u32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 16;
}
}
// The regular PLT.
static void write_plt(Context<E> &ctx) {
u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
// Write PLT header
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushq GOTPLT+8(%rip)
0xff, 0x25, 0, 0, 0, 0, // jmp *GOTPLT+16(%rip)
0x0f, 0x1f, 0x40, 0x00, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 2;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
// Write PLT entries
i64 relplt_idx = 0;
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOTPLT
0x68, 0, 0, 0, 0, // push $index_in_relplt
0xe9, 0, 0, 0, 0, // jmp PLT[0]
};
for (Symbol<E> *sym : ctx.plt->symbols) {
u8 *ent = buf + ctx.plt_hdr_size + sym->get_plt_idx(ctx) * ctx.plt_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
*(u32 *)(ent + 7) = relplt_idx++;
*(u32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym->get_plt_addr(ctx) - 16;
}
}
template <>
void PltSection<E>::copy_buf(Context<E> &ctx) {
if (ctx.arg.z_now)
write_compact_plt(ctx);
else if (ctx.arg.z_ibtplt)
write_ibtplt(ctx);
else
write_plt(ctx);
}
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
0x66, 0x90, // nop
};
for (Symbol<E> *sym : symbols) {
u8 *ent = buf + sym->get_pltgot_idx(ctx) * X86_64::pltgot_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 6;
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_X86_64_NONE:
return;
case R_X86_64_32:
*(u32 *)loc = val;
return;
case R_X86_64_64:
*(u64 *)loc = val;
return;
case R_X86_64_PC32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
return;
case R_X86_64_PC64:
*(u64 *)loc = val - this->shdr.sh_addr - offset;
return;
}
unreachable();
}
static u32 relax_gotpcrelx(u8 *loc) {
switch ((loc[0] << 8) | loc[1]) {
case 0xff15: return 0x90e8; // call *0(%rip) -> call 0
case 0xff25: return 0x90e9; // jmp *0(%rip) -> jmp 0
}
return 0;
}
static u32 relax_rex_gotpcrelx(u8 *loc) {
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
case 0x488b05: return 0x488d05; // mov 0(%rip), %rax -> lea 0(%rip), %rax
case 0x488b0d: return 0x488d0d; // mov 0(%rip), %rcx -> lea 0(%rip), %rcx
case 0x488b15: return 0x488d15; // mov 0(%rip), %rdx -> lea 0(%rip), %rdx
case 0x488b1d: return 0x488d1d; // mov 0(%rip), %rbx -> lea 0(%rip), %rbx
case 0x488b25: return 0x488d25; // mov 0(%rip), %rsp -> lea 0(%rip), %rsp
case 0x488b2d: return 0x488d2d; // mov 0(%rip), %rbp -> lea 0(%rip), %rbp
case 0x488b35: return 0x488d35; // mov 0(%rip), %rsi -> lea 0(%rip), %rsi
case 0x488b3d: return 0x488d3d; // mov 0(%rip), %rdi -> lea 0(%rip), %rdi
case 0x4c8b05: return 0x4c8d05; // mov 0(%rip), %r8 -> lea 0(%rip), %r8
case 0x4c8b0d: return 0x4c8d0d; // mov 0(%rip), %r9 -> lea 0(%rip), %r9
case 0x4c8b15: return 0x4c8d15; // mov 0(%rip), %r10 -> lea 0(%rip), %r10
case 0x4c8b1d: return 0x4c8d1d; // mov 0(%rip), %r11 -> lea 0(%rip), %r11
case 0x4c8b25: return 0x4c8d25; // mov 0(%rip), %r12 -> lea 0(%rip), %r12
case 0x4c8b2d: return 0x4c8d2d; // mov 0(%rip), %r13 -> lea 0(%rip), %r13
case 0x4c8b35: return 0x4c8d35; // mov 0(%rip), %r14 -> lea 0(%rip), %r14
case 0x4c8b3d: return 0x4c8d3d; // mov 0(%rip), %r15 -> lea 0(%rip), %r15
}
return 0;
}
static u32 relax_gottpoff(u8 *loc) {
switch ((loc[0] << 16) | (loc[1] << 8) | loc[2]) {
case 0x488b05: return 0x48c7c0; // mov 0(%rip), %rax -> mov $0, %rax
case 0x488b0d: return 0x48c7c1; // mov 0(%rip), %rcx -> mov $0, %rcx
case 0x488b15: return 0x48c7c2; // mov 0(%rip), %rdx -> mov $0, %rdx
case 0x488b1d: return 0x48c7c3; // mov 0(%rip), %rbx -> mov $0, %rbx
case 0x488b25: return 0x48c7c4; // mov 0(%rip), %rsp -> mov $0, %rsp
case 0x488b2d: return 0x48c7c5; // mov 0(%rip), %rbp -> mov $0, %rbp
case 0x488b35: return 0x48c7c6; // mov 0(%rip), %rsi -> mov $0, %rsi
case 0x488b3d: return 0x48c7c7; // mov 0(%rip), %rdi -> mov $0, %rdi
case 0x4c8b05: return 0x49c7c0; // mov 0(%rip), %r8 -> mov $0, %r8
case 0x4c8b0d: return 0x49c7c1; // mov 0(%rip), %r9 -> mov $0, %r9
case 0x4c8b15: return 0x49c7c2; // mov 0(%rip), %r10 -> mov $0, %r10
case 0x4c8b1d: return 0x49c7c3; // mov 0(%rip), %r11 -> mov $0, %r11
case 0x4c8b25: return 0x49c7c4; // mov 0(%rip), %r12 -> mov $0, %r12
case 0x4c8b2d: return 0x49c7c5; // mov 0(%rip), %r13 -> mov $0, %r13
case 0x4c8b35: return 0x49c7c6; // mov 0(%rip), %r14 -> mov $0, %r14
case 0x4c8b3d: return 0x49c7c7; // mov 0(%rip), %r15 -> mov $0, %r15
}
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().
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_X86_64_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = base + rel.r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
if (rel_fragments && rel_fragments[frag_idx].idx == i)
frag_ref = &rel_fragments[frag_idx++];
auto overflow_check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto write8 = [&](u64 val) {
overflow_check(val, 0, 1 << 8);
*loc = val;
};
auto write8s = [&](u64 val) {
overflow_check(val, -(1 << 7), 1 << 7);
*loc = val;
};
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
};
auto write32 = [&](u64 val) {
overflow_check(val, 0, (i64)1 << 32);
*(u32 *)loc = val;
};
auto write32s = [&](u64 val) {
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
*(u32 *)loc = val;
};
auto write64 = [&](u64 val) {
*(u64 *)loc = val;
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr)
#define GOT ctx.gotplt->shdr.sh_addr
switch (rel.r_type) {
case R_X86_64_8:
write8(S + A);
continue;
case R_X86_64_16:
write16(S + A);
continue;
case R_X86_64_32:
write32(S + A);
continue;
case R_X86_64_32S:
write32s(S + A);
continue;
case R_X86_64_64:
if (sym.is_absolute() || !ctx.arg.pic) {
write64(S + A);
} else if (sym.is_imported) {
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
write64(A);
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_X86_64_RELATIVE, 0, (i64)(S + A)};
write64(S + A);
}
continue;
case R_X86_64_PC8:
write8s(S + A - P);
continue;
case R_X86_64_PC16:
write16s(S + A - P);
continue;
case R_X86_64_PC32:
write32s(S + A - P);
continue;
case R_X86_64_PC64:
if (sym.is_absolute() || !sym.is_imported || !ctx.arg.shared) {
write64(S + A - P);
} else {
*dynrel++ = {P, R_X86_64_64, (u32)sym.get_dynsym_idx(ctx), A};
write64(A);
}
continue;
case R_X86_64_PLT32:
write32s(S + A - P);
continue;
case R_X86_64_PLTOFF64:
write64(S + A - GOT);
break;
case R_X86_64_GOT32:
write32s(G + A);
continue;
case R_X86_64_GOT64:
write64(G + A);
continue;
case R_X86_64_GOTOFF64:
write64(S + A - GOT);
continue;
case R_X86_64_GOTPC32:
write32s(GOT + A - P);
continue;
case R_X86_64_GOTPC64:
write64(GOT + A - P);
continue;
case R_X86_64_GOTPCREL:
write32s(G + GOT + A - P);
continue;
case R_X86_64_GOTPCREL64:
write64(G + GOT + A - P);
continue;
case R_X86_64_GOTPCRELX:
if (sym.get_got_idx(ctx) == -1) {
u32 insn = relax_gotpcrelx(loc - 2);
loc[-2] = insn >> 8;
loc[-1] = insn;
write32s(S + A - P);
} else {
write32s(G + GOT + A - P);
}
continue;
case R_X86_64_REX_GOTPCRELX:
if (sym.get_got_idx(ctx) == -1) {
u32 insn = relax_rex_gotpcrelx(loc - 3);
loc[-3] = insn >> 16;
loc[-2] = insn >> 8;
loc[-1] = insn;
write32s(S + A - P);
} else {
write32s(G + GOT + A - P);
}
continue;
case R_X86_64_TLSGD:
if (sym.get_tlsgd_idx(ctx) == -1) {
// Relax GD to LE
i64 val = S - ctx.tls_end + A + 4;
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
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);
}
continue;
case R_X86_64_TLSLD:
if (ctx.got->tlsld_idx == -1) {
// Relax LD to LE
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);
}
continue;
case R_X86_64_DTPOFF32:
if (ctx.arg.relax && !ctx.arg.shared)
write32s(S + A - ctx.tls_end);
else
write32s(S + A - ctx.tls_begin);
continue;
case R_X86_64_DTPOFF64:
if (ctx.arg.relax && !ctx.arg.shared)
write64(S + A - ctx.tls_end);
else
write64(S + A - ctx.tls_begin);
continue;
case R_X86_64_TPOFF32:
write32s(S + A - ctx.tls_end);
continue;
case R_X86_64_TPOFF64:
write64(S + A - ctx.tls_end);
continue;
case R_X86_64_GOTTPOFF:
if (sym.get_gottp_idx(ctx) == -1) {
u32 insn = relax_gottpoff(loc - 3);
loc[-3] = insn >> 16;
loc[-2] = insn >> 8;
loc[-1] = insn;
write32s(S + A - ctx.tls_end + 4);
} else {
write32s(sym.get_gottp_addr(ctx) + A - P);
}
continue;
case R_X86_64_GOTPC32_TLSDESC:
if (sym.get_tlsdesc_idx(ctx) == -1) {
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);
}
continue;
case R_X86_64_SIZE32:
write32(sym.esym().st_size + A);
continue;
case R_X86_64_SIZE64:
write64(sym.esym().st_size + A);
continue;
case R_X86_64_TLSDESC_CALL:
if (sym.get_tlsdesc_idx(ctx) == -1) {
// call *(%rax) -> nop
loc[0] = 0x66;
loc[1] = 0x90;
}
continue;
default:
unreachable();
}
#undef S
#undef A
#undef P
#undef G
#undef GOT
}
}
// This function is responsible for applying relocations against
// non-SHF_ALLOC sections (i.e. sections that are not mapped to memory
// at runtime).
//
// Relocations against non-SHF_ALLOC sections are much easier to
// handle than that against SHF_ALLOC sections. It is because, since
// they are not mapped to memory, they don't contain any variable or
// function and never need PLT or GOT. Non-SHF_ALLOC sections are
// mostly debug info sections.
//
// Relocations against non-SHF_ALLOC sections are not scanned by
// scan_relocations.
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_X86_64_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);
auto overflow_check = [&](i64 val, i64 lo, i64 hi) {
if (val < lo || hi <= val)
Error(ctx) << *this << ": relocation " << rel << " against "
<< sym << " out of range: " << val << " is not in ["
<< lo << ", " << hi << ")";
};
auto write8 = [&](u64 val) {
overflow_check(val, 0, 1 << 8);
*loc = val;
};
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
};
auto write32 = [&](u64 val) {
overflow_check(val, 0, (i64)1 << 32);
*(u32 *)loc = val;
};
auto write32s = [&](u64 val) {
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
*(u32 *)loc = val;
};
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
switch (rel.r_type) {
case R_X86_64_8:
write8(S + A);
break;
case R_X86_64_16:
write16(S + A);
break;
case R_X86_64_32:
write32(S + A);
break;
case R_X86_64_32S:
write32s(S + A);
break;
case R_X86_64_64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
break;
case R_X86_64_DTPOFF32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
write32s(S + A - ctx.tls_begin);
break;
case R_X86_64_DTPOFF64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A - ctx.tls_begin;
break;
case R_X86_64_SIZE32:
write32(sym.esym().st_size + A);
break;
case R_X86_64_SIZE64:
*(u64 *)loc = sym.esym().st_size + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
<< rel;
break;
}
#undef S
#undef A
}
}
// Linker has to create data structures in an output file to apply
// some type of relocations. For example, if a relocation refers a GOT
// or a PLT entry of a symbol, linker has to create an entry in .got
// or in .plt for that symbol. In order to fix the file layout, we
// need to scan relocations.
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_X86_64_NONE)
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (!sym.file) {
report_undef(ctx, file, sym);
continue;
}
if (sym.get_type() == STT_GNU_IFUNC) {
sym.flags |= NEEDS_GOT;
sym.flags |= NEEDS_PLT;
}
switch (rel.r_type) {
case R_X86_64_8:
case R_X86_64_16:
case R_X86_64_32:
case R_X86_64_32S: {
// Dynamic linker does not support 8, 16 or 32-bit dynamic
// relocations for these types of relocations. We report an
// error if we cannot relocate them even at load-time.
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, ERROR, ERROR, ERROR }, // DSO
{ NONE, ERROR, ERROR, ERROR }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_X86_64_64: {
// Unlike the above, we can use R_X86_64_RELATIVE and R_86_64_64
// relocations.
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, BASEREL, DYNREL, DYNREL }, // DSO
{ NONE, BASEREL, DYNREL, DYNREL }, // PIE
{ NONE, NONE, COPYREL, CPLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_X86_64_PC8:
case R_X86_64_PC16:
case R_X86_64_PC32: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, ERROR, ERROR }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_X86_64_PC64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ ERROR, NONE, DYNREL, DYNREL }, // DSO
{ ERROR, NONE, COPYREL, PLT }, // PIE
{ NONE, NONE, COPYREL, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_X86_64_GOT32:
case R_X86_64_GOT64:
case R_X86_64_GOTPC32:
case R_X86_64_GOTPC64:
case R_X86_64_GOTPCREL:
case R_X86_64_GOTPCREL64:
sym.flags |= NEEDS_GOT;
break;
case R_X86_64_GOTPCRELX: {
if (rel.r_addend != -4)
Fatal(ctx) << *this << ": bad r_addend for R_X86_64_GOTPCRELX";
bool do_relax = ctx.arg.relax && !sym.is_imported &&
sym.is_relative() && relax_gotpcrelx(loc - 2);
if (!do_relax)
sym.flags |= NEEDS_GOT;
break;
}
case R_X86_64_REX_GOTPCRELX: {
if (rel.r_addend != -4)
Fatal(ctx) << *this << ": bad r_addend for R_X86_64_REX_GOTPCRELX";
bool do_relax = ctx.arg.relax && !sym.is_imported &&
sym.is_relative() && relax_rex_gotpcrelx(loc - 3);
if (!do_relax)
sym.flags |= NEEDS_GOT;
break;
}
case R_X86_64_PLT32:
case R_X86_64_PLTOFF64: {
Action table[][4] = {
// Absolute Local Imported data Imported code
{ NONE, NONE, PLT, PLT }, // DSO
{ NONE, NONE, PLT, PLT }, // PIE
{ NONE, NONE, PLT, PLT }, // PDE
};
dispatch(ctx, table, i, rel, sym);
break;
}
case R_X86_64_TLSGD: {
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLSGD reloc must be followed by PLT or GOTPCREL";
if (u32 ty = rels[i + 1].r_type;
ty != R_X86_64_PLT32 && ty != R_X86_64_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: {
if (i + 1 == rels.size())
Fatal(ctx) << *this << ": TLSLD reloc must be followed by PLT or GOTPCREL";
if (u32 ty = rels[i + 1].r_type;
ty != R_X86_64_PLT32 && ty != R_X86_64_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;
bool do_relax = ctx.arg.relax && !ctx.arg.shared &&
!sym.is_imported && relax_gottpoff(loc - 3);
if (!do_relax)
sym.flags |= NEEDS_GOTTP;
break;
}
case R_X86_64_GOTPC32_TLSDESC: {
if (relax_gotpc32_tlsdesc(loc - 3) == 0)
Fatal(ctx) << *this << ": GOTPC32_TLSDESC relocation is used"
<< " against an invalid code sequence";
bool do_relax = ctx.relax_tlsdesc && !sym.is_imported;
if (!do_relax)
sym.flags |= NEEDS_TLSDESC;
break;
}
case R_X86_64_GOTOFF64:
case R_X86_64_DTPOFF32:
case R_X86_64_DTPOFF64:
case R_X86_64_TPOFF32:
case R_X86_64_TPOFF64:
case R_X86_64_SIZE32:
case R_X86_64_SIZE64:
case R_X86_64_TLSDESC_CALL:
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
}
}
}
} // namespace mold::elf
+409 -145
View File
@@ -3,10 +3,11 @@
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <tbb/global_control.h>
#include <unistd.h>
#include <unordered_set>
namespace mold::elf {
static const char helpmsg[] = R"(
Options:
--help Report usage information
@@ -35,17 +36,22 @@ Options:
-h LIBNAME, --soname LIBNAME
Set shared library name
-l LIBNAME Search for a given library
-m EMULATION Ignored
-m TARGET Set target
-o FILE, --output FILE Set output filename
-q, --emit-relocs Leaves relocation sections in the output
-r, --relocatable Generate relocatable output
-s, --strip-all Strip .symtab section
-u SYMBOL, --undefined SYMBOL
Force to resolve SYMBOL
--Bdynamic Link against shared libraries (default)
--Bstatic Do not link against shared libraries
--Bdynamic, --dy Link against shared libraries (default)
--Bstatic, --dn, --static Do not link against shared libraries
--Bsymbolic Bind global symbols locally
--Bsymbolic-functions Bind global functions locally
--Bno-symbolic Cancel --Bsymbolic and --Bsymbolic-functions
--Map FILE Write map file to a given file
--Tbss=ADDR Set address to .bss
--Tdata Set address to .data
--Ttext Set address to .text
--allow-multiple-definition Allow multiple definitions
--as-needed Only set DT_NEEDED if used
--no-as-needed
@@ -53,12 +59,19 @@ Options:
Generate build ID
--no-build-id
--chroot DIR Set a given path to root directory
--color-diagnostics Ignored
--color-diagnostics=[auto,always,never]
Use colors in diagnostics
--color-diagnostics Alias for --color-diagnostics=always
--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
--no-eh-frame-hdr
@@ -71,21 +84,24 @@ 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
--print-gc-sections Print removed unreferenced sections
--no-print-gc-sections
--print-icf-sections Print folded identical sections
@@ -96,18 +112,24 @@ Options:
--relax Optimize instructions (default)
--no-relax
--repro Embed input files to .repro section
--require-defined SYMBOL Require SYMBOL be defined in the final output
--retain-symbols-file FILE Keep only symbols listed in FILE
--reverse-sections Reverses input sections in the output file
--rpath DIR Add DIR to runtime search path
--rpath-link DIR Ignored
--run COMMAND ARG... Run COMMAND with mold as /usr/bin/ld
--section-start=SECTION=ADDR Set address to section
--shared, --Bshareable Create a share library
--shuffle-sections[=SEED] Randomize the output by shuffling input sections
--sort-common Ignored
--sort-section Ignored
--spare-dynamic-tags NUMBER Reserve give number of tags in .dynamic section
--static Do not link against shared libraries
--start-lib Give following object files in-archive-file semantics
--end-lib End the effect of --start-lib
--stats Print input statistics
--sysroot DIR Set target system root directory
--thread-count COUNT Use COUNT number of threads
--thread-count COUNT, --threads=COUNT
Use COUNT number of threads
--threads Use multiple threads (default)
--no-threads
--trace Print name of each input file
@@ -117,6 +139,9 @@ Options:
--version-script FILE Read version script
--warn-common Warn about common symbols
--no-warn-common
--warn-once Only warn once for each undefined symbol
--warn-shared-textrel Warn if the output .so needs text relocations
--warn-textrel Warn if the output file needs text relocations
--warn-unresolved-symbols Report unresolved symbols as warnings
--error-unresolved-symbols
Report unresolved symbols as errors (default)
@@ -125,6 +150,7 @@ Options:
--wrap SYMBOL Use wrapper function for a given symbol
-z defs Report undefined symbols (even with --shared)
-z nodefs
-z common-page-size=VALUE Ignored
-z execstack Require executable stack
-z noexecstack
-z initfirst Mark DSO to be initialized first at runtime
@@ -132,79 +158,26 @@ Options:
-z keep-text-section-prefix Keep .text.{hot,unknown,unlikely,startup,exit} as separate sections in the final binary
-z nokeep-text-section-prefix
-z lazy Enable lazy function resolution (default)
-z max-page-size=VALUE Use VALUE as the memory page size
-z nocopyreloc Do not create copy relocations
-z nodefaultlib Make the dynamic loader to ignore default search paths
-z nodelete Mark DSO non-deletable at runtime
-z nodlopen Mark DSO not available to dlopen
-z nodump Mark DSO not available to dldump
-z now Disable lazy function resolution
-z origin Mark object requiring immediate $ORIGIN processing at runtime
-z separate-loadable-segments
Separate all loadable segments to different pages
-z separate-code Separate code and data into different pages
-z noseparate-code Allow overlap in pages
-z relro Make some sections read-only after relocation (default)
-z norelro
-z text Report error if DT_TEXTREL is set
-z notext
-z textoff
mold: supported targets: elf32-i386 elf64-x86-64
mold: supported emulations: elf_i386 elf_x86_64)";
template <typename E>
static std::vector<std::string_view>
read_response_file(Context<E> &ctx, std::string_view path) {
std::vector<std::string_view> vec;
MemoryMappedFile<E> *mb =
MemoryMappedFile<E>::must_open(ctx, std::string(path));
u8 *data = mb->data(ctx);
auto read_quoted = [&](i64 i, char quote) {
std::string buf;
while (i < mb->size() && data[i] != quote) {
if (data[i] == '\\') {
buf.append(1, data[i + 1]);
i += 2;
} else {
buf.append(1, data[i++]);
}
}
if (i >= mb->size())
Fatal(ctx) << path << ": premature end of input";
vec.push_back(save_string(ctx, buf));
return i + 1;
};
auto read_unquoted = [&](i64 i) {
std::string buf;
while (i < mb->size() && !isspace(data[i]))
buf.append(1, data[i++]);
vec.push_back(save_string(ctx, buf));
return i;
};
for (i64 i = 0; i < mb->size();) {
if (isspace(data[i]))
i++;
else if (data[i] == '\'')
i = read_quoted(i + 1, '\'');
else if (data[i] == '\"')
i = read_quoted(i + 1, '\"');
else
i = read_unquoted(i);
}
return vec;
}
template <typename E>
std::vector<std::string_view>
expand_response_files(Context<E> &ctx, char **argv) {
std::vector<std::string_view> vec;
for (i64 i = 0; argv[i]; i++) {
if (argv[i][0] == '@')
append(vec, read_response_file(ctx, argv[i] + 1));
else
vec.push_back(argv[i]);
}
return vec;
}
mold: supported targets: elf32-i386 elf64-x86-64 elf64-littleaarch64
mold: supported emulations: elf_i386 elf_x86_64 aarch64linux aarch64elf)";
static std::vector<std::string> add_dashes(std::string name) {
// Multi-letter linker options can be preceded by either a single
@@ -255,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) {
@@ -280,50 +266,45 @@ static bool read_z_flag(std::span<std::string_view> &args, std::string name) {
}
template <typename E>
std::string create_response_file(Context<E> &ctx) {
std::string buf;
std::stringstream out;
std::string cwd = get_current_dir();
out << "-C " << cwd.substr(1) << "\n";
if (cwd != "/") {
out << "--chroot ..";
i64 depth = std::count(cwd.begin(), cwd.end(), '/');
for (i64 i = 1; i < depth; i++)
out << "/..";
out << "\n";
bool read_z_arg(Context<E> &ctx, std::span<std::string_view> &args,
std::string_view &arg, std::string name) {
if (args.size() >= 2 && args[0] == "-z" && args[1].starts_with(name + "=")) {
arg = args[1].substr(name.size() + 1);
args = args.subspan(2);
return true;
}
for (i64 i = 1; i < ctx.cmdline_args.size(); i++) {
std::string_view arg = ctx.cmdline_args[i];
if (arg == "-repro" || arg == "--repro") {
i++;
continue;
}
out << arg << "\n";
if (!args.empty() && args[0].starts_with("-z" + name + "=")) {
arg = args[0].substr(name.size() + 3);
args = args.subspan(1);
return true;
}
return out.str();
return false;
}
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;
@@ -372,13 +353,6 @@ split_by_comma_or_colon(std::string_view str) {
return vec;
}
static i64 get_default_thread_count() {
// mold doesn't scale above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
static std::string_view trim(std::string_view str) {
size_t pos = str.find_first_not_of(" \t");
if (pos == str.npos)
@@ -393,9 +367,9 @@ static std::string_view trim(std::string_view str) {
template <typename E>
static void read_retain_symbols_file(Context<E> &ctx, std::string_view path) {
MemoryMappedFile<E> *mb =
MemoryMappedFile<E>::must_open(ctx, std::string(path));
std::string_view data((char *)mb->data(ctx), mb->size());
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, std::string(path));
std::string_view data((char *)mf->data, mf->size);
ctx.arg.retain_symbols_file.reset(new std::unordered_set<std::string_view>);
@@ -423,14 +397,60 @@ 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) {
if constexpr (std::is_same_v<E, X86_64>) {
if (ctx.arg.z_now)
return {0, 8};
if (ctx.arg.z_ibtplt)
return {32, 16};
return {16, 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();
}
template <typename E>
void parse_nonpositional_args(Context<E> &ctx,
std::vector<std::string_view> &remaining) {
std::span<std::string_view> args = ctx.cmdline_args;
args = args.subspan(1);
ctx.arg.thread_count = get_default_thread_count();
ctx.arg.color_diagnostics = isatty(STDERR_FILENO);
ctx.page_size = E::page_size;
bool version_shown = false;
bool warn_shared_textrel = false;
// RISC-V object files contains lots of local symbols, so by default
// we discard them. This is compatible with GNU ld.
if constexpr (std::is_same_v<E, RISCV64>)
ctx.arg.discard_locals = true;
while (!args.empty()) {
std::string_view arg;
@@ -446,18 +466,34 @@ void parse_nonpositional_args(Context<E> &ctx,
} else if (read_arg(ctx, args, arg, "dynamic-linker") ||
read_arg(ctx, args, arg, "I")) {
ctx.arg.dynamic_linker = arg;
} else if (read_arg(ctx, args, arg, "no-dynamic-linker")) {
} else if (read_flag(args, "no-dynamic-linker")) {
ctx.arg.dynamic_linker = "";
} else if (read_flag(args, "v")) {
SyncOut(ctx) << get_version_string();
SyncOut(ctx) << mold_version;
version_shown = true;
} else if (read_flag(args, "version")) {
SyncOut(ctx) << get_version_string();
SyncOut(ctx) << mold_version;
exit(0);
} else if (read_flag(args, "V")) {
SyncOut(ctx) << get_version_string()
SyncOut(ctx) << mold_version
<< "\n Supported emulations:\n elf_x86_64\n elf_i386";
version_shown = true;
} else if (read_arg(ctx, args, arg, "m")) {
if (arg == "elf_x86_64") {
ctx.arg.emulation = EM_X86_64;
} else if (arg == "elf_i386") {
ctx.arg.emulation = EM_386;
} else if (arg == "aarch64linux") {
ctx.arg.emulation = EM_AARCH64;
} else if (arg == "armelf_linux_eabi") {
ctx.arg.emulation = EM_ARM;
} else if (arg == "elf64lriscv") {
ctx.arg.emulation = EM_RISCV;
} else {
Fatal(ctx) << "unknown -m argument: " << arg;
}
} else if (read_flag(args, "end-lib")) {
remaining.push_back("-end-lib");
} else if (read_flag(args, "export-dynamic") || read_flag(args, "E")) {
ctx.arg.export_dynamic = true;
} else if (read_flag(args, "no-export-dynamic")) {
@@ -466,30 +502,64 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.Bsymbolic = true;
} else if (read_flag(args, "Bsymbolic-functions")) {
ctx.arg.Bsymbolic_functions = true;
} else if (read_flag(args, "Bno-symbolic")) {
ctx.arg.Bsymbolic = false;
ctx.arg.Bsymbolic_functions = false;
} else if (read_arg(ctx, args, arg, "exclude-libs")) {
append(ctx.arg.exclude_libs, split_by_comma_or_colon(arg));
} else if (read_flag(args, "q") || read_flag(args, "emit-relocs")) {
ctx.arg.emit_relocs = true;
} else if (read_arg(ctx, args, arg, "e") ||
read_arg(ctx, args, arg, "entry")) {
ctx.arg.entry = arg;
} else if (read_arg(ctx, args, arg, "Map")) {
ctx.arg.Map = arg;
ctx.arg.print_map = true;
} else if (read_flag(args, "print-dependencies")) {
ctx.arg.print_dependencies = 1;
} else if (read_flag(args, "print-dependencies=full")) {
ctx.arg.print_dependencies = 2;
} else if (read_flag(args, "print-map") || read_flag(args, "M")) {
ctx.arg.print_map = true;
} else if (read_flag(args, "static") || read_flag(args, "Bstatic")) {
} else if (read_flag(args, "Bstatic") || read_flag(args, "dn") || read_flag(args, "static")) {
ctx.arg.is_static = true;
remaining.push_back("-Bstatic");
} else if (read_flag(args, "Bdynamic")) {
} else if (read_flag(args, "Bdynamic") || read_flag(args, "dy")) {
ctx.arg.is_static = false;
remaining.push_back("-Bdynamic");
} else if (read_flag(args, "shared") || read_flag(args, "Bshareable")) {
ctx.arg.shared = true;
} else if (read_arg(ctx, args, arg, "spare-dynamic-tags")) {
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.emplace_back(get_symbol(ctx, arg.substr(0, pos)),
parse_defsym_value(ctx, arg.substr(pos + 1)));
} else if (read_flag(args, ":lto-pass2")) {
ctx.arg.lto_pass2 = true;
} else if (read_arg(ctx, args, arg, ":ignore-ir-file")) {
ctx.arg.ignore_ir_file.insert(arg);
} else if (read_flag(args, "demangle")) {
ctx.arg.demangle = true;
} else if (read_flag(args, "no-demangle")) {
ctx.arg.demangle = false;
} else if (read_flag(args, "default-symver")) {
ctx.arg.default_symver = true;
} else if (read_flag(args, "noinhibit-exec")) {
ctx.arg.noinhibit_exec = true;
} else if (read_flag(args, "shuffle-sections")) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_SHUFFLE;
} else if (read_eq(ctx, args, arg, "shuffle-sections")) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_SHUFFLE;
ctx.arg.shuffle_sections_seed = parse_number(ctx, "shuffle-sections", arg);
} else if (read_flag(args, "reverse-sections")) {
ctx.arg.shuffle_sections = SHUFFLE_SECTIONS_REVERSE;
} else if (read_arg(ctx, args, arg, "y") ||
read_arg(ctx, args, arg, "trace-symbol")) {
ctx.arg.trace_symbol.push_back(arg);
@@ -497,21 +567,26 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.filler = parse_hex(ctx, "filler", arg);
} else if (read_arg(ctx, args, arg, "L") ||
read_arg(ctx, args, arg, "library-path")) {
ctx.arg.library_paths.push_back(arg);
ctx.arg.library_paths.push_back(std::string(arg));
} else if (read_arg(ctx, args, arg, "sysroot")) {
ctx.arg.sysroot = arg;
} else if (read_arg(ctx, args, arg, "unique")) {
ctx.arg.unique.reset(new std::regex(glob_to_regex(arg)));
std::optional<GlobPattern> pat = GlobPattern::compile(arg);
if (!pat)
Fatal(ctx) << "-unique: invalid glob pattern: " << arg;
ctx.arg.unique = std::move(*pat);
} else if (read_arg(ctx, args, arg, "unresolved-symbols")) {
if (arg == "report-all" || arg == "ignore-in-shared-libs")
ctx.arg.unresolved_symbols = UnresolvedKind::ERROR;
ctx.arg.unresolved_symbols = UNRESOLVED_ERROR;
else if (arg == "ignore-all" || arg == "ignore-in-object-files")
ctx.arg.unresolved_symbols = UnresolvedKind::IGNORE;
ctx.arg.unresolved_symbols = UNRESOLVED_IGNORE;
else
Fatal(ctx) << "unknown --unresolved-symbols argument: " << arg;
} else if (read_arg(ctx, args, arg, "u") ||
read_arg(ctx, args, arg, "undefined")) {
ctx.arg.undefined.push_back(arg);
} else if (read_arg(ctx, args, arg, "require-defined")) {
ctx.arg.require_defined.push_back(arg);
} else if (read_arg(ctx, args, arg, "init")) {
ctx.arg.init = arg;
} else if (read_arg(ctx, args, arg, "fini")) {
@@ -551,10 +626,18 @@ 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")) {
ctx.arg.perf = true;
} else if (read_flag(args, "pack-dyn-relocs=relr")) {
ctx.arg.pack_dyn_relocs_relr = true;
} else if (read_flag(args, "pack-dyn-relocs=none")) {
ctx.arg.pack_dyn_relocs_relr = false;
} else if (read_flag(args, "stats")) {
ctx.arg.stats = true;
Counter::enabled = true;
@@ -563,10 +646,34 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.directory = arg;
} else if (read_arg(ctx, args, arg, "chroot")) {
ctx.arg.chroot = arg;
} else if (args[0] == "-color-diagnostics=auto" ||
args[0] == "--color-diagnostics=auto") {
ctx.arg.color_diagnostics = isatty(STDERR_FILENO);
args = args.subspan(1);
} else if (args[0] == "-color-diagnostics=always" ||
args[0] == "--color-diagnostics=always") {
ctx.arg.color_diagnostics = true;
args = args.subspan(1);
} else if (args[0] == "-color-diagnostics=never" ||
args[0] == "--color-diagnostics=never") {
ctx.arg.color_diagnostics = false;
args = args.subspan(1);
} else if (read_flag(args, "color-diagnostics")) {
ctx.arg.color_diagnostics = true;
} else if (read_flag(args, "warn-common")) {
ctx.arg.warn_common = true;
} else if (read_flag(args, "no-warn-common")) {
ctx.arg.warn_common = false;
} else if (read_flag(args, "warn-once")) {
ctx.arg.warn_once = true;
} else if (read_flag(args, "warn-shared-textrel")) {
warn_shared_textrel = true;
} else if (read_flag(args, "warn-textrel")) {
ctx.arg.warn_textrel = true;
} else if (read_flag(args, "enable-new-dtags")) {
ctx.arg.enable_new_dtags = true;
} else if (read_flag(args, "disable-new-dtags")) {
ctx.arg.enable_new_dtags = false;
} else if (read_arg(ctx, args, arg, "compress-debug-sections")) {
if (arg == "zlib" || arg == "zlib-gabi")
ctx.arg.compress_debug_sections = COMPRESS_GABI;
@@ -583,16 +690,42 @@ 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")) {
ctx.arg.z_now = true;
} else if (read_z_flag(args, "lazy")) {
ctx.arg.z_now = false;
} else if (read_z_flag(args, "cet-report=none")) {
ctx.arg.z_cet_report = CET_REPORT_NONE;
} else if (read_z_flag(args, "cet-report=warning")) {
ctx.arg.z_cet_report = CET_REPORT_WARNING;
} else if (read_z_flag(args, "cet-report=error")) {
ctx.arg.z_cet_report = CET_REPORT_ERROR;
} else if (read_z_flag(args, "execstack")) {
ctx.arg.z_execstack = true;
} else if (read_z_arg(ctx, args, arg, "max-page-size")) {
ctx.page_size = parse_number(ctx, "-z max-page-size", arg);
if (std::popcount<u64>(ctx.page_size) != 1)
Fatal(ctx) << "-z max-page-size " << arg << ": value must be a power of 2";
} else if (read_z_flag(args, "noexecstack")) {
ctx.arg.z_execstack = false;
} else if (read_z_flag(args, "relro")) {
@@ -615,18 +748,33 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.z_initfirst = true;
} else if (read_z_flag(args, "interpose")) {
ctx.arg.z_interpose = true;
} else if (read_z_flag(args, "ibt")) {
ctx.arg.z_ibt = true;
ctx.arg.z_ibtplt = true;
} else if (read_z_flag(args, "ibtplt")) {
ctx.arg.z_ibtplt = true;
} else if (read_z_flag(args, "muldefs")) {
ctx.arg.allow_multiple_definition = true;
} else if (read_z_flag(args, "keep-text-section-prefix")) {
ctx.arg.z_keep_text_section_prefix = true;
} else if (read_z_flag(args, "nokeep-text-section-prefix")) {
ctx.arg.z_keep_text_section_prefix = false;
} else if (read_z_flag(args, "shstk")) {
ctx.arg.z_shstk = true;
} else if (read_z_flag(args, "text")) {
ctx.arg.z_text = true;
} else if (read_z_flag(args, "notext") || read_z_flag(args, "textoff")) {
ctx.arg.z_text = false;
} else if (read_z_flag(args, "origin")) {
ctx.arg.z_origin = true;
} else if (read_z_flag(args, "nodefaultlib")) {
ctx.arg.z_nodefaultlib = true;
} else if (read_z_flag(args, "separate-loadable-segments")) {
ctx.arg.z_separate_code = SEPARATE_LOADABLE_SEGMENTS;
} else if (read_z_flag(args, "separate-code")) {
ctx.arg.z_separate_code = SEPARATE_CODE;
} else if (read_z_flag(args, "noseparate-code")) {
ctx.arg.z_separate_code = NOSEPARATE_CODE;
} else if (read_flag(args, "no-undefined")) {
ctx.arg.z_defs = true;
} else if (read_flag(args, "fatal-warnings")) {
@@ -656,10 +804,6 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.icf = false;
} else if (read_arg(ctx, args, arg, "image-base")) {
ctx.arg.image_base = parse_number(ctx, "image-base", arg);
} else if (read_flag(args, "quick-exit")) {
ctx.arg.quick_exit = true;
} else if (read_flag(args, "no-quick-exit")) {
ctx.arg.quick_exit = false;
} else if (read_flag(args, "print-icf-sections")) {
ctx.arg.print_icf_sections = true;
} else if (read_flag(args, "no-print-icf-sections")) {
@@ -668,12 +812,72 @@ void parse_nonpositional_args(Context<E> &ctx,
ctx.arg.quick_exit = true;
} else if (read_flag(args, "no-quick-exit")) {
ctx.arg.quick_exit = false;
} else if (read_arg(ctx, args, arg, "plugin")) {
ctx.arg.plugin = arg;
} else if (read_arg(ctx, args, arg, "plugin-opt")) {
ctx.arg.plugin_opt.push_back(std::string(arg));
} else if (read_flag(args, "lto-cs-profile-generate")) {
ctx.arg.plugin_opt.push_back("cs-profile-generate");
} else if (read_arg(ctx, args, arg, "lto-cs-profile-file")) {
ctx.arg.plugin_opt.push_back("cs-profile-path=" + std::string(arg));
} else if (read_flag(args, "lto-debug-pass-manager")) {
ctx.arg.plugin_opt.push_back("debug-pass-manager");
} else if (read_flag(args, "disable-verify")) {
ctx.arg.plugin_opt.push_back("disable-verify");
} else if (read_flag(args, "lto-emit-asm")) {
ctx.arg.plugin_opt.push_back("emit-asm");
} else if (read_arg(ctx, args, arg, "thinlto-jobs")) {
ctx.arg.plugin_opt.push_back("jobs=" + std::string(arg));
} else if (read_flag(args, "no-legacy-pass-manager")) {
ctx.arg.plugin_opt.push_back("legacy-pass-manager");
} else if (read_arg(ctx, args, arg, "lto-partitions")) {
ctx.arg.plugin_opt.push_back("lto-partitions=" + std::string(arg));
} else if (read_flag(args, "no-lto-legacy-pass-manager")) {
ctx.arg.plugin_opt.push_back("new-pass-manager");
} else if (read_arg(ctx, args, arg, "lto-obj-path")) {
ctx.arg.plugin_opt.push_back("obj-path=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "opt-remarks-filename")) {
ctx.arg.plugin_opt.push_back("opt-remarks-filename=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "opt-remarks-format")) {
ctx.arg.plugin_opt.push_back("opt-remarks-format=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "opt-remarks-hotness-threshold")) {
ctx.arg.plugin_opt.push_back("opt-remarks-hotness-threshold=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "opt-remarks-passes")) {
ctx.arg.plugin_opt.push_back("opt-remarks-passes=" + std::string(arg));
} else if (read_flag(args, "opt-remarks-with_hotness")) {
ctx.arg.plugin_opt.push_back("opt-remarks-with-hotness");
} else if (args[0].starts_with("-lto-O")) {
ctx.arg.plugin_opt.push_back("O" + std::string(args[0].substr(6)));
args = args.subspan(1);
} else if (args[0].starts_with("--lto-O")) {
ctx.arg.plugin_opt.push_back("O" + std::string(args[0].substr(7)));
args = args.subspan(1);
} else if (read_arg(ctx, args, arg, "lto-pseudo-probe-for-profiling")) {
ctx.arg.plugin_opt.push_back("pseudo-probe-for-profiling=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "lto-sample-profile")) {
ctx.arg.plugin_opt.push_back("sample-profile=" + std::string(arg));
} else if (read_flag(args, "save-temps")) {
ctx.arg.plugin_opt.push_back("save-temps");
} else if (read_flag(args, "thinlto-emit-imports-files")) {
ctx.arg.plugin_opt.push_back("thinlto-emit-imports-files");
} else if (read_arg(ctx, args, arg, "thinlto-index-only")) {
ctx.arg.plugin_opt.push_back("thinlto-index-only=" + std::string(arg));
} else if (read_flag(args, "thinlto-index-only")) {
ctx.arg.plugin_opt.push_back("thinlto-index-only");
} else if (read_arg(ctx, args, arg, "thinlto-object-suffix-replace")) {
ctx.arg.plugin_opt.push_back("thinlto-object-suffix-replace=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "thinlto-prefix-replace")) {
ctx.arg.plugin_opt.push_back("thinlto-prefix-replace=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "thinlto-jobs")) {
ctx.arg.plugin_opt.push_back("jobs=" + std::string(arg));
} else if (read_arg(ctx, args, arg, "thread-count")) {
ctx.arg.thread_count = parse_number(ctx, "thread-count", arg);
} else if (read_flag(args, "threads")) {
ctx.arg.thread_count = get_default_thread_count();
ctx.arg.thread_count = 0;
} else if (read_flag(args, "no-threads")) {
ctx.arg.thread_count = 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")) {
@@ -683,9 +887,9 @@ void parse_nonpositional_args(Context<E> &ctx,
} else if (read_flag(args, "strip-debug") || read_flag(args, "S")) {
ctx.arg.strip_all = true;
} else if (read_flag(args, "warn-unresolved-symbols")) {
ctx.arg.unresolved_symbols = UnresolvedKind::WARN;
ctx.arg.unresolved_symbols = UNRESOLVED_WARN;
} else if (read_flag(args, "error-unresolved-symbols")) {
ctx.arg.unresolved_symbols = UnresolvedKind::ERROR;
ctx.arg.unresolved_symbols = UNRESOLVED_ERROR;
} else if (read_arg(ctx, args, arg, "rpath")) {
if (!ctx.arg.rpaths.empty())
ctx.arg.rpaths += ":";
@@ -723,6 +927,14 @@ void parse_nonpositional_args(Context<E> &ctx,
}
} else if (read_flag(args, "no-build-id")) {
ctx.arg.build_id.kind = BuildId::NONE;
} else if (read_arg(ctx, args, arg, "format") ||
read_arg(ctx, args, arg, "b")) {
if (arg == "binary")
Fatal(ctx)
<< "mold does not suppor `-b binary`. If you want to convert a binary"
<< " file into an object file, use `objcopy -I binary -O default"
<< " <input-file> <output-file.o>` instead.";
Fatal(ctx) << "unknown command line option: -b " << arg;
} else if (read_arg(ctx, args, arg, "auxiliary") ||
read_arg(ctx, args, arg, "f")) {
ctx.arg.auxiliary.push_back(arg);
@@ -730,17 +942,18 @@ 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;
} else if (read_flag(args, "apply-dynamic-relocs")) {
} else if (read_arg(ctx, args, arg, "O")) {
} else if (read_flag(args, "O0")) {
} else if (read_flag(args, "O1")) {
} else if (read_flag(args, "O2")) {
} else if (read_flag(args, "verbose")) {
} else if (read_arg(ctx, args, arg, "plugin")) {
} else if (read_arg(ctx, args, arg, "plugin-opt")) {
} else if (read_flag(args, "color-diagnostics")) {
} else if (read_flag(args, "gdb-index")) {
} else if (read_arg(ctx, args, arg, "m")) {
} else if (read_flag(args, "eh-frame-hdr")) {
} else if (read_flag(args, "start-group")) {
} else if (read_flag(args, "end-group")) {
@@ -753,16 +966,25 @@ void parse_nonpositional_args(Context<E> &ctx,
} else if (read_flag(args, "allow-shlib-undefined")) {
} else if (read_flag(args, "no-allow-shlib-undefined")) {
} else if (read_flag(args, "no-add-needed")) {
} else if (read_flag(args, "no-call-graph-profile-sort")) {
} else if (read_flag(args, "no-copy-dt-needed-entries")) {
} else if (read_flag(args, "no-undefined-version")) {
} else if (read_arg(ctx, args, arg, "sort-section")) {
} else if (read_flag(args, "sort-common")) {
} else if (read_flag(args, "dc")) {
} else if (read_flag(args, "dp")) {
} else if (read_flag(args, "fix-cortex-a53-835769")) {
} else if (read_flag(args, "fix-cortex-a53-843419")) {
} else if (read_flag(args, "EL")) {
} else if (read_flag(args, "warn-once")) {
} else if (read_flag(args, "nodefaultlibs")) {
} else if (read_flag(args, "warn-constructors")) {
} else if (read_flag(args, "warn-execstack")) {
} else if (read_flag(args, "no-warn-execstack")) {
} else if (read_arg(ctx, args, arg, "rpath-link")) {
} else if (read_z_flag(args, "combreloc")) {
} else if (read_z_flag(args, "nocombreloc")) {
} else if (read_z_arg(ctx, args, arg, "common-page-size")) {
} else if (read_arg(ctx, args, arg, "version-script")) {
remaining.push_back("--version-script");
remaining.push_back(arg);
@@ -787,21 +1009,36 @@ void parse_nonpositional_args(Context<E> &ctx,
remaining.push_back("-push-state");
} else if (read_flag(args, "pop-state")) {
remaining.push_back("-pop-state");
} else if (args[0].starts_with("-z") && args[0].size() > 2) {
Warn(ctx) << "unknown command line option: " << args[0];
args = args.subspan(1);
} else if (args[0] == "-z" && args.size() >= 2) {
Warn(ctx) << "unknown command line option: -z " << args[1];
args = args.subspan(2);
} else {
if (args[0] == "-z" && args.size() >= 2)
Fatal(ctx) << "mold: unknown command line option: -z " << args[1];
if (args[0][0] == '-')
Fatal(ctx) << "mold: unknown command line option: " << args[0];
Fatal(ctx) << "unknown command line option: " << args[0];
remaining.push_back(args[0]);
args = args.subspan(1);
}
}
if (ctx.arg.shared) {
ctx.arg.pic = true;
ctx.arg.dynamic_linker = "";
if (!ctx.arg.sysroot.empty()) {
for (std::string &path : ctx.arg.library_paths) {
if (std::string_view(path).starts_with('='))
path = ctx.arg.sysroot + path.substr(1);
else if (std::string_view(path).starts_with("$SYSROOT"))
path = ctx.arg.sysroot + path.substr(8);
}
}
// 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;
if (ctx.arg.pic)
ctx.arg.image_base = 0;
@@ -820,31 +1057,58 @@ void parse_nonpositional_args(Context<E> &ctx,
Fatal(ctx) << "-auxiliary may not be used without -shared";
}
if (ctx.arg.image_base % ctx.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;
if (ctx.arg.output.empty())
ctx.arg.output = "a.out";
if (ctx.arg.shared || ctx.arg.export_dynamic)
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.default_version = VER_NDX_LOCAL;
if (ctx.arg.default_symver) {
std::string ver = ctx.arg.soname.empty() ?
filepath(ctx.arg.output).filename().string() : std::string(ctx.arg.soname);
ctx.arg.version_definitions.push_back(ver);
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);
// TLSDESC relocs must be always relaxed for statically-linked
// executables even if -no-relax is given. It is because a
// statically-linked executable doesn't contain a tranpoline
// function needed for TLSDESC.
ctx.relax_tlsdesc = ctx.arg.is_static || (ctx.arg.relax && !ctx.arg.shared);
if (version_shown && remaining.empty())
exit(0);
}
#define INSTANTIATE(E) \
template \
std::vector<std::string_view> \
expand_response_files(Context<E> &ctx, char **argv); \
\
template \
bool read_arg(Context<E> &ctx, std::span<std::string_view> &args, \
std::string_view &arg, \
std::string name); \
\
template std::string create_response_file(Context<E> &ctx); \
\
template \
void parse_nonpositional_args(Context<E> &ctx, \
std::vector<std::string_view> &remaining)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+1460
View File
File diff suppressed because it is too large. Load diff
+63 -45
View File
@@ -7,45 +7,48 @@
#include <tbb/concurrent_vector.h>
#include <tbb/parallel_for_each.h>
template <typename E>
using Feeder = tbb::parallel_do_feeder<InputSection<E> *>;
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,
Feeder<E> &feeder, i64 depth) {
assert(isec->is_visited);
tbb::feeder<InputSection<E> *> &feeder, i64 depth) {
assert(isec->extra().is_visited);
// A relocation can refer either a section fragment (i.e. a piece of
// string in a mergeable string section) or a symbol. Mark all
// section fragments as alive.
if (isec->rel_fragments)
for (i64 i = 0; isec->rel_fragments[i].idx >= 0; i++)
isec->rel_fragments[i].frag->is_alive = true;
if (SectionFragmentRef<E> *refs = isec->rel_fragments.get())
for (i64 i = 0; refs[i].idx >= 0; i++)
refs[i].frag->is_alive.store(true, std::memory_order_relaxed);
// If this is a text section, .eh_frame may contain records
// describing how to handle exceptions for that function.
// We want to keep associated .eh_frame records.
for (FdeRecord<E> &fde : isec->get_fdes())
for (ElfRel<E> &rel : fde.get_rels().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];
@@ -53,19 +56,19 @@ static void visit(Context<E> &ctx, InputSection<E> *isec,
// Symbol can refer either a section fragment or an input section.
// Mark a fragment as alive.
if (SectionFragment<E> *frag = sym.get_frag()) {
frag->is_alive = true;
frag->is_alive.store(true, std::memory_order_relaxed);
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());
}
}
@@ -73,19 +76,19 @@ template <typename E>
static tbb::concurrent_vector<InputSection<E> *>
collect_root_set(Context<E> &ctx) {
Timer t(ctx, "collect_root_set");
tbb::concurrent_vector<InputSection<E> *> roots;
tbb::concurrent_vector<InputSection<E> *> rootset;
auto enqueue_section = [&](InputSection<E> *isec) {
if (mark_section(isec))
roots.push_back(isec);
rootset.push_back(isec);
};
auto enqueue_symbol = [&](Symbol<E> *sym) {
if (sym) {
if (SectionFragment<E> *frag = sym->get_frag())
frag->is_alive = true;
frag->is_alive.store(true, std::memory_order_relaxed);
else
enqueue_section(sym->input_section);
enqueue_section(sym->get_input_section());
}
};
@@ -99,11 +102,11 @@ collect_root_set(Context<E> &ctx) {
// reduce the amount of non-memory-mapped segments, you should
// use `strip` command, compile without debug info or use
// -strip-all linker option.
if (!(isec->shdr.sh_flags & SHF_ALLOC))
isec->is_visited = true;
if (!(isec->shdr().sh_flags & SHF_ALLOC))
isec->extra().is_visited = true;
if (is_init_fini(*isec) || is_c_identifier(isec->name()) ||
isec->shdr.sh_type == SHT_NOTE)
isec->shdr().sh_type == SHT_NOTE)
enqueue_section(isec.get());
}
});
@@ -116,10 +119,13 @@ collect_root_set(Context<E> &ctx) {
});
// Add sections referenced by root symbols.
enqueue_symbol(Symbol<E>::intern(ctx, ctx.arg.entry));
enqueue_symbol(get_symbol(ctx, ctx.arg.entry));
for (std::string_view name : ctx.arg.undefined)
enqueue_symbol(Symbol<E>::intern(ctx, name));
enqueue_symbol(get_symbol(ctx, name));
for (std::string_view name : ctx.arg.require_defined)
enqueue_symbol(get_symbol(ctx, name));
// .eh_frame consists of variable-length records called CIE and FDE
// records, and they are a unit of inclusion or exclusion.
@@ -130,16 +136,17 @@ collect_root_set(Context<E> &ctx) {
enqueue_symbol(file->symbols[rel.r_sym]);
});
return roots;
return rootset;
}
// Mark all reachable sections
template <typename E>
static void mark(Context<E> &ctx,
tbb::concurrent_vector<InputSection<E> *> &roots) {
tbb::concurrent_vector<InputSection<E> *> &rootset) {
Timer t(ctx, "mark");
tbb::parallel_do(roots, [&](InputSection<E> *isec, Feeder<E> &feeder) {
tbb::parallel_for_each(rootset, [&](InputSection<E> *isec,
tbb::feeder<InputSection<E> *> &feeder) {
visit(ctx, isec, feeder, 0);
});
}
@@ -151,10 +158,8 @@ static void sweep(Context<E> &ctx) {
static Counter counter("garbage_sections");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = 0; i < file->sections.size(); i++) {
std::unique_ptr<InputSection<E>> &isec = file->sections[i];
if (isec && isec->is_alive && !isec->is_visited) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && !isec->extra().is_visited) {
if (ctx.arg.print_gc_sections)
SyncOut(ctx) << "removing unused section " << *isec;
isec->kill();
@@ -171,9 +176,11 @@ static void mark_nonalloc_fragments(Context<E> &ctx) {
Timer t(ctx, "mark_nonalloc_fragments");
tbb::parallel_for_each(ctx.objs, [](ObjectFile<E> *file) {
for (SectionFragment<E> *frag : file->fragments)
if (!(frag->output_section.shdr.sh_flags & SHF_ALLOC))
frag->is_alive = true;
for (std::unique_ptr<MergeableSection<E>> &m : file->mergeable_sections)
if (m)
for (SectionFragment<E> *frag : m->fragments)
if (!(frag->output_section.shdr.sh_flags & SHF_ALLOC))
frag->is_alive.store(true, std::memory_order_relaxed);
});
}
@@ -181,12 +188,23 @@ 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> *> roots = collect_root_set(ctx);
mark(ctx, roots);
tbb::concurrent_vector<InputSection<E> *> rootset = collect_root_set(ctx);
mark(ctx, rootset);
sweep(ctx);
}
template void gc_sections(Context<X86_64> &ctx);
template void gc_sections(Context<I386> &ctx);
#define INSTANTIATE(E) \
template void gc_sections(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+151
View File
@@ -0,0 +1,151 @@
#include "mold.h"
#include <cstring>
namespace mold::elf {
std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
std::vector<Element> vec;
while (!pat.empty()) {
u8 c = pat[0];
pat = pat.substr(1);
switch (c) {
case '[': {
// Here are a few bracket pattern examples:
//
// [abc]: a, b or c
// [$\]!]: $, ] or !
// [a-czg-i]: a, b, c, z, g, h, or i
// [^a-z]: Any character except lowercase letters
vec.push_back({BRACKET});
std::vector<bool> &bitmap = vec.back().bitmap;
bitmap.resize(256);
bool negate = false;
if (!pat.empty() && pat[0] == '^') {
negate = true;
pat = pat.substr(1);
}
bool closed = false;
while (!pat.empty()) {
if (pat[0] == ']') {
pat = pat.substr(1);
closed = true;
break;
}
if (pat[0] == '\\') {
pat = pat.substr(1);
if (pat.empty())
return {};
}
if (pat.size() >= 3 && pat[1] == '-') {
u8 start = pat[0];
u8 end = pat[2];
pat = pat.substr(3);
if (end == '\\') {
if (pat.empty())
return {};
end = pat[0];
pat = pat.substr(1);
}
if (end < start)
return {};
for (i64 i = start; i <= end; i++)
bitmap[i] = true;
} else {
bitmap[(u8)pat[0]] = true;
pat = pat.substr(1);
}
}
if (!closed)
return {};
if (negate)
for (i64 i = 0; i < 256; i++)
bitmap[i] = !bitmap[i];
break;
}
case '?':
vec.push_back({QUESTION});
break;
case '*':
vec.push_back({STAR});
break;
default:
if (vec.empty() || vec.back().kind != STRING)
vec.push_back({STRING});
vec.back().str += c;
break;
}
}
return {GlobPattern{std::move(vec)}};
}
bool GlobPattern::match(std::string_view str) {
return do_match(str, elements);
}
bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
while (!elements.empty()) {
Element &e = elements[0];
elements = elements.subspan(1);
switch (e.kind) {
case STRING:
if (str.empty() || !str.starts_with(e.str))
return false;
str = str.substr(e.str.size());
break;
case STAR:
if (elements.empty())
return true;
// Patterns like "*foo*bar*" should be much more common than more
// complex ones like "*foo*[abc]*" or "*foo**?bar*", so we optimize
// the former case here.
if (elements[0].kind == STRING) {
for (;;) {
size_t pos = str.find(elements[0].str);
if (pos == str.npos)
break;
if (do_match(str.substr(pos + elements[0].str.size()),
elements.subspan(1)))
return true;
str = str.substr(pos + 1);
}
return false;
}
// Other cases are handled here.
for (i64 j = 0; j < str.size(); j++)
if (do_match(str.substr(j), elements))
return true;
return false;
case QUESTION:
if (str.empty())
return false;
str = str.substr(1);
break;
case BRACKET:
if (str.empty() || !e.bitmap[str[0]])
return false;
str = str.substr(1);
break;
}
}
return str.empty();
}
} // namespace mold::elf
+76 -57
View File
@@ -35,8 +35,10 @@
// This problem boils down to one in graph theory. Input to ICF can be
// considered as a directed graph in which vertices are sections and edges
// are relocations. Vertices have labels (section contents, etc.), and so
// are edges (relocation offsets, etc.). Given this formulation, we want to
// find as many isomorphic subgraphs as possible.
// are edges (relocation offsets, etc.). Two vertices are considered
// identical if and only if the (possibly infinite) their unfoldings into
// regular trees are equal. Given this formulation, we want to find as
// many identical vertices as possible.
//
// Solving such problem is computationally intensive, but mold is quite fast.
// For Chromium, mold's ICF finishes in less than 1 second with 20 threads.
@@ -51,9 +53,9 @@
// thread and even better with multiple threads.
#include "mold.h"
#include "../sha.h"
#include <array>
#include <openssl/sha.h>
#include <tbb/concurrent_unordered_map.h>
#include <tbb/concurrent_vector.h>
#include <tbb/enumerable_thread_specific.h>
@@ -61,18 +63,20 @@
#include <tbb/parallel_for_each.h>
#include <tbb/parallel_sort.h>
static constexpr i64 HASH_SIZE = 16;
static constexpr int64_t HASH_SIZE = 16;
typedef std::array<u8, HASH_SIZE> Digest;
typedef std::array<uint8_t, HASH_SIZE> Digest;
namespace tbb {
template<> struct tbb_hash<Digest> {
namespace std {
template<> struct hash<Digest> {
size_t operator()(const Digest &k) const {
return *(i64 *)&k[0];
return *(int64_t *)&k[0];
}
};
}
namespace mold::elf {
template <typename E>
static void uniquify_cies(Context<E> &ctx) {
Timer t(ctx, "uniquify_cies");
@@ -95,7 +99,7 @@ static void uniquify_cies(Context<E> &ctx) {
template <typename E>
static bool is_eligible(InputSection<E> &isec) {
const ElfShdr<E> &shdr = isec.shdr;
const ElfShdr<E> &shdr = isec.shdr();
std::string_view name = isec.name();
bool is_alloc = (shdr.sh_flags & SHF_ALLOC);
@@ -129,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;
@@ -141,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;
@@ -153,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;
@@ -164,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;
}
@@ -193,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;
}
}
});
@@ -220,8 +225,6 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
SHA256_CTX sha;
SHA256_Init(&sha);
u8 *buf = (u8 *)isec.contents.data();
auto hash = [&](auto val) {
SHA256_Update(&sha, &val, sizeof(val));
};
@@ -232,20 +235,20 @@ 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');
hash((u64)&sym);
} else if (SectionFragment<E> *frag = sym.get_frag()) {
hash('2');
hash_string(frag->data);
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');
@@ -255,24 +258,24 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
};
hash_string(isec.contents);
hash(isec.shdr.sh_flags);
hash(isec.shdr().sh_flags);
hash(isec.get_fdes().size());
hash(isec.get_rels(ctx).size());
for (FdeRecord<E> &fde : isec.get_fdes()) {
hash(fde.cie->icf_idx);
hash(isec.file.cies[fde.cie_idx].icf_idx);
// Bytes 0 to 4 contain the length of this record, and
// bytes 4 to 8 contain an offset to CIE.
hash_string(fde.get_contents().substr(8));
hash_string(fde.get_contents(isec.file).substr(8));
hash(fde.get_rels().size());
hash(fde.get_rels(isec.file).size());
for (ElfRel<E> &rel : fde.get_rels().subspan(1)) {
for (ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
hash_symbol(*isec.file.symbols[rel.r_sym]);
hash(rel.r_type);
hash(rel.r_offset - fde.input_offset);
hash(fde.cie->input_section.get_addend(rel));
hash(isec.file.cies[fde.cie_idx].input_section.get_addend(rel));
}
}
@@ -288,7 +291,7 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
SectionFragmentRef<E> &ref = isec.rel_fragments[frag_idx++];
hash('a');
isec.get_addend(rel);
hash_string(ref.frag->data);
hash((u64)ref.frag);
} else {
hash_symbol(*isec.file.symbols[rel.r_sym]);
}
@@ -306,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]++;
});
@@ -321,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;
@@ -351,12 +354,15 @@ static void gather_edges(Context<E> &ctx,
std::vector<u32> &edge_indices) {
Timer t(ctx, "gather_edges");
if (sections.empty())
return;
std::vector<i64> num_edges(sections.size());
edge_indices.resize(sections.size());
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
InputSection<E> &isec = *sections[i];
assert(isec.icf_eligible);
assert(isec.extra().icf_eligible);
i64 frag_idx = 0;
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
@@ -365,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]++;
}
}
});
@@ -387,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;
}
}
});
@@ -416,8 +424,8 @@ static i64 propagate(std::span<std::vector<Digest>> digests,
i64 begin = edge_indices[i];
i64 end = (i + 1 == num_digests) ? edges.size() : edge_indices[i + 1];
for (i64 j = begin; j < end; j++)
SHA256_Update(&sha, digests[slot][edges[j]].data(), HASH_SIZE);
for (i64 j : edges.subspan(begin, end - begin))
SHA256_Update(&sha, digests[slot][j].data(), HASH_SIZE);
digests[!slot][i] = digest_final(sha);
@@ -450,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()});
}
}
});
@@ -488,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);
@@ -547,11 +558,11 @@ void icf_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
auto it = map->find(digest[i]);
assert(it != map->end());
sections[i]->leader = it->second;
sections[i]->extra().leader = it->second;
});
// Since free'ing the map is slow, postpone it.
ctx.on_exit.push_back([=]() { delete map; });
ctx.on_exit.push_back([=] { delete map; });
}
if (ctx.arg.print_icf_sections)
@@ -562,17 +573,25 @@ 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;
}
}
}
});
}
}
template void icf_sections(Context<X86_64> &ctx);
template void icf_sections(Context<I386> &ctx);
#define INSTANTIATE(E) \
template void icf_sections(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
File diff suppressed because it is too large. Load diff
+258
View File
@@ -0,0 +1,258 @@
#include "mold.h"
#include <limits>
#include <zlib.h>
namespace mold::elf {
template <typename E>
bool CieRecord<E>::equals(const CieRecord<E> &other) const {
if (get_contents() != other.get_contents())
return false;
std::span<ElfRel<E>> x = get_rels();
std::span<ElfRel<E>> y = other.get_rels();
if (x.size() != y.size())
return false;
for (i64 i = 0; i < x.size(); i++) {
if (x[i].r_offset - input_offset != y[i].r_offset - other.input_offset ||
x[i].r_type != y[i].r_type ||
file.symbols[x[i].r_sym] != other.file.symbols[y[i].r_sym] ||
input_section.get_addend(x[i]) != other.input_section.get_addend(y[i]))
return false;
}
return true;
}
static inline i64 to_p2align(u64 alignment) {
if (alignment == 0)
return 0;
return std::countr_zero(alignment);
}
template <typename E>
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
std::string_view name, i64 shndx)
: file(file), shndx(shndx) {
if (shndx < file.elf_sections.size())
contents = {(char *)file.mf->data + shdr().sh_offset, shdr().sh_size};
bool compressed;
if (name.starts_with(".zdebug")) {
sh_size = *(ubig64 *)&contents[4];
p2align = to_p2align(shdr().sh_addralign);
compressed = true;
} else if (shdr().sh_flags & SHF_COMPRESSED) {
ElfChdr<E> &chdr = *(ElfChdr<E> *)&contents[0];
sh_size = chdr.ch_size;
p2align = to_p2align(chdr.ch_addralign);
compressed = true;
} else {
sh_size = shdr().sh_size;
p2align = to_p2align(shdr().sh_addralign);
compressed = false;
}
// Uncompress early if the relocation is REL-type so that we can read
// addends from section contents. If RELA-type, we don't need to do this
// because addends are in relocations.
if (compressed && E::is_rel) {
u8 *buf = new u8[sh_size];
uncompress(ctx, buf);
contents = {(char *)buf, sh_size};
ctx.string_pool.emplace_back(buf);
}
output_section =
OutputSection<E>::get_instance(ctx, name, shdr().sh_type, shdr().sh_flags);
}
template <typename E>
bool InputSection<E>::is_compressed() {
return !E::is_rel &&
(name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED));
}
template <typename E>
void InputSection<E>::uncompress(Context<E> &ctx, u8 *buf) {
auto do_uncompress = [&](std::string_view data) {
unsigned long size = sh_size;
if (::uncompress(buf, &size, (u8 *)data.data(), data.size()) != Z_OK)
Fatal(ctx) << *this << ": uncompress failed";
assert(size == sh_size);
};
if (name().starts_with(".zdebug")) {
// Old-style compressed section
if (!contents.starts_with("ZLIB") || contents.size() <= 12)
Fatal(ctx) << *this << ": corrupted compressed section";
do_uncompress(contents.substr(12));
return;
}
assert(shdr().sh_flags & SHF_COMPRESSED);
// New-style compressed section
if (contents.size() < sizeof(ElfChdr<E>))
Fatal(ctx) << *this << ": corrupted compressed section";
ElfChdr<E> &hdr = *(ElfChdr<E> *)&contents[0];
if (hdr.ch_type != ELFCOMPRESS_ZLIB)
Fatal(ctx) << *this << ": unsupported compression type";
do_uncompress(contents.substr(sizeof(ElfChdr<E>)));
}
template <typename E>
static i64 get_output_type(Context<E> &ctx) {
if (ctx.arg.shared)
return 0;
if (ctx.arg.pie)
return 1;
return 2;
}
template <typename E>
static i64 get_sym_type(Symbol<E> &sym) {
if (sym.is_absolute())
return 0;
if (!sym.is_imported)
return 1;
if (sym.get_type() != STT_FUNC)
return 2;
return 3;
}
template <typename E>
void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4], i64 i,
const ElfRel<E> &rel, Symbol<E> &sym) {
Action action = table[get_output_type(ctx)][get_sym_type(sym)];
bool is_writable = (shdr().sh_flags & SHF_WRITE);
auto error = [&] {
std::string msg = sym.is_absolute() ? "-fno-PIC" : "-fPIC";
Error(ctx) << *this << ": " << rel << " relocation at offset 0x"
<< std::hex << rel.r_offset << " against symbol `"
<< sym << "' can not be used; recompile with " << msg;
};
auto warn_textrel = [&] {
if (ctx.arg.warn_textrel)
Warn(ctx) << *this << ": relocation against symbol `" << sym
<< "' in read-only section";
};
switch (action) {
case NONE:
return;
case ERROR:
error();
return;
case COPYREL:
if (!ctx.arg.z_copyreloc) {
error();
return;
}
if (sym.esym().st_visibility == STV_PROTECTED) {
Error(ctx) << *this
<< ": cannot make copy relocation for protected symbol '"
<< sym << "', defined in " << *sym.file
<< "; recompile with -fPIC";
return;
}
sym.flags |= NEEDS_COPYREL;
return;
case PLT:
sym.flags |= NEEDS_PLT;
return;
case CPLT: {
std::scoped_lock lock(sym.mu);
sym.flags |= NEEDS_PLT;
sym.is_canonical = true;
return;
}
case DYNREL:
if (!is_writable) {
if (ctx.arg.z_text) {
error();
return;
}
warn_textrel();
ctx.has_textrel = true;
}
assert(sym.is_imported);
file.num_dynrel++;
return;
case BASEREL:
if (!is_writable) {
if (ctx.arg.z_text) {
error();
return;
}
warn_textrel();
ctx.has_textrel = true;
}
if (!is_relr_reloc(ctx, rel))
file.num_dynrel++;
return;
default:
unreachable();
}
}
template <typename E>
void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
if (shdr().sh_type == SHT_NOBITS || sh_size == 0)
return;
// Copy data
if constexpr (std::is_same_v<E, RISCV64>) {
copy_contents_riscv(ctx, buf);
} else if (is_compressed()) {
uncompress(ctx, buf);
} else {
memcpy(buf, contents.data(), contents.size());
}
// Apply relocations
if (shdr().sh_flags & SHF_ALLOC)
apply_reloc_alloc(ctx, buf);
else
apply_reloc_nonalloc(ctx, buf);
}
template <typename E>
void report_undef(Context<E> &ctx, InputFile<E> &file, Symbol<E> &sym) {
if (ctx.arg.warn_once && !ctx.warned.insert({(void *)&sym, 1}))
return;
switch (ctx.arg.unresolved_symbols) {
case UNRESOLVED_ERROR:
Error(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UNRESOLVED_WARN:
Warn(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UNRESOLVED_IGNORE:
break;
}
}
#define INSTANTIATE(E) \
template struct CieRecord<E>; \
template class InputSection<E>; \
template void report_undef(Context<E> &, InputFile<E> &, Symbol<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+123 -73
View File
@@ -8,8 +8,13 @@
#include <cctype>
#include <iomanip>
namespace mold::elf {
template <typename E>
static thread_local MemoryMappedFile<E> *current_file;
static thread_local MappedFile<Context<E>> *current_file;
template <typename E>
void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok);
static std::string_view get_line(std::string_view input, const char *pos) {
assert(input.data() <= pos);
@@ -32,7 +37,7 @@ template <typename E>
class SyntaxError {
public:
SyntaxError(Context<E> &ctx, std::string_view errpos) : out(ctx) {
std::string_view contents = current_file<E>->get_contents(ctx);
std::string_view contents = current_file<E>->get_contents();
std::string_view line = get_line(contents, errpos.data());
i64 lineno = 1;
@@ -56,7 +61,7 @@ public:
[[noreturn]] ~SyntaxError() = default;
Fatal<E> out;
Fatal<Context<E>> out;
};
template <typename E>
@@ -140,27 +145,40 @@ read_output_format(Context<E> &ctx, std::span<std::string_view> tok) {
}
template <typename E>
static MemoryMappedFile<E> *resolve_path(Context<E> &ctx, std::string_view tok) {
static bool is_in_sysroot(Context<E> &ctx, std::string path) {
std::string rel =
to_abs_path(path).lexically_relative(to_abs_path(ctx.arg.sysroot));
return rel != "." && !rel.starts_with("../");
}
template <typename E>
static MappedFile<Context<E>> *resolve_path(Context<E> &ctx, std::string_view tok) {
std::string str(unquote(tok));
if (str.starts_with("/"))
return MemoryMappedFile<E>::must_open(ctx, ctx.arg.sysroot + str);
// GNU ld prepends the sysroot if a pathname starts with '/' and the
// script being processed is in the sysroot. We do the same.
if (str.starts_with('/') && is_in_sysroot(ctx, current_file<E>->name))
return MappedFile<Context<E>>::must_open(ctx, ctx.arg.sysroot + str);
if (str.starts_with('=')) {
std::string path;
if (ctx.arg.sysroot.empty())
path = str.substr(1);
else
path = ctx.arg.sysroot + str.substr(1);
return MappedFile<Context<E>>::must_open(ctx, path);
}
if (str.starts_with("-l"))
return find_library(ctx, str.substr(2));
if (std::string dir(path_dirname(current_file<E>->name));
MemoryMappedFile<E> *mb = MemoryMappedFile<E>::open(ctx, dir + "/" + str))
return mb;
if (MemoryMappedFile<E> *mb = MemoryMappedFile<E>::open(ctx, str))
return mb;
if (MappedFile<Context<E>> *mf = open_library(ctx, str))
return mf;
for (std::string_view dir : ctx.arg.library_paths) {
std::string root = dir.starts_with("/") ? ctx.arg.sysroot : "";
std::string path = root + std::string(dir) + "/" + str;
if (MemoryMappedFile<E> *mb = MemoryMappedFile<E>::open(ctx, path))
return mb;
std::string path = std::string(dir) + "/" + str;
if (MappedFile<Context<E>> *mf = open_library(ctx, path))
return mf;
}
SyntaxError(ctx, tok) << "library not found: " << str;
@@ -180,8 +198,8 @@ read_group(Context<E> &ctx, std::span<std::string_view> tok) {
continue;
}
MemoryMappedFile<E> *mb = resolve_path(ctx, tok[0]);
read_file(ctx, mb);
MappedFile<Context<E>> *mf = resolve_path(ctx, tok[0]);
read_file(ctx, mf);
tok = tok.subspan(1);
}
@@ -191,27 +209,35 @@ read_group(Context<E> &ctx, std::span<std::string_view> tok) {
}
template <typename E>
void parse_linker_script(Context<E> &ctx, MemoryMappedFile<E> *mb) {
current_file<E> = mb;
void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
std::vector<std::string_view> vec = tokenize(ctx, mb->get_contents(ctx));
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
std::span<std::string_view> tok = vec;
while (!tok.empty()) {
if (tok[0] == "OUTPUT_FORMAT")
if (tok[0] == "OUTPUT_FORMAT") {
tok = read_output_format(ctx, tok.subspan(1));
else if (tok[0] == "INPUT" || tok[0] == "GROUP")
} else if (tok[0] == "INPUT" || tok[0] == "GROUP") {
tok = read_group(ctx, tok.subspan(1));
else
SyntaxError(ctx, tok[0]) << "unknown token";
} else if (tok[0] == "VERSION") {
tok = tok.subspan(1);
tok = skip(ctx, tok, "{");
read_version_script(ctx, tok);
tok = skip(ctx, tok, "}");
} else if (tok[0] == ";") {
tok = tok.subspan(1);
} else {
SyntaxError(ctx, tok[0]) << "unknown linker script token";
}
}
}
template <typename E>
i64 get_script_output_type(Context<E> &ctx, MemoryMappedFile<E> *mb) {
current_file<E> = mb;
i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
current_file<E> = mf;
std::vector<std::string_view> vec = tokenize(ctx, mb->get_contents(ctx));
std::vector<std::string_view> vec = tokenize(ctx, mf->get_contents());
std::span<std::string_view> tok = vec;
if (tok.size() >= 3 && tok[0] == "OUTPUT_FORMAT" && tok[1] == "(") {
@@ -238,9 +264,9 @@ static bool read_label(std::span<std::string_view> &tok,
}
template <typename E>
static void parse_version_script_commands(Context<E> &ctx,
std::span<std::string_view> &tok,
i16 &ver, bool is_extern_cpp) {
static void
read_version_script_commands(Context<E> &ctx, std::span<std::string_view> &tok,
u16 ver_idx, bool is_cpp) {
bool is_global = true;
while (!tok.empty() && tok[0] != "}") {
@@ -257,26 +283,29 @@ static void parse_version_script_commands(Context<E> &ctx,
if (tok[0] == "extern") {
tok = tok.subspan(1);
bool is_cpp;
if (!tok.empty() && tok[0] == "\"C\"") {
tok = tok.subspan(1);
is_cpp = false;
tok = skip(ctx, tok, "{");
read_version_script_commands( ctx, tok, ver_idx, false);
} else {
tok = skip(ctx, tok, "\"C++\"");
is_cpp = true;
tok = skip(ctx, tok, "{");
read_version_script_commands(ctx, tok, ver_idx, true);
}
tok = skip(ctx, tok, "{");
parse_version_script_commands(ctx, tok, ver, is_cpp);
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");
continue;
}
if (tok[0] == "*")
ctx.arg.default_version = (is_global ? ver : VER_NDX_LOCAL);
else
ctx.arg.version_patterns.push_back({tok[0], ver, is_extern_cpp});
if (tok[0] == "*") {
ctx.default_version = (is_global ? ver_idx : VER_NDX_LOCAL);
} else if (is_global) {
ctx.version_patterns.push_back({unquote(tok[0]), ver_idx, is_cpp});
} else {
ctx.version_patterns.push_back({unquote(tok[0]), VER_NDX_LOCAL, is_cpp});
}
tok = tok.subspan(1);
if (!tok.empty() && tok[0] == "}")
@@ -286,63 +315,79 @@ static void parse_version_script_commands(Context<E> &ctx,
}
template <typename E>
void parse_version_script(Context<E> &ctx, std::string path) {
current_file<E> = MemoryMappedFile<E>::must_open(ctx, path);
std::vector<std::string_view> vec =
tokenize(ctx, current_file<E>->get_contents(ctx));
void read_version_script(Context<E> &ctx, std::span<std::string_view> &tok) {
u16 next_ver = VER_NDX_LAST_RESERVED + ctx.arg.version_definitions.size() + 1;
std::span<std::string_view> tok = vec;
i16 next_ver = VER_NDX_LAST_RESERVED + 1;
while (!tok.empty()) {
i16 ver = VER_NDX_GLOBAL;
if (tok[0] != "{") {
ver = next_ver++;
ctx.arg.version_definitions.push_back(tok[0]);
while (!tok.empty() && tok[0] != "}") {
u16 ver_idx;
if (tok[0] == "{") {
ver_idx = VER_NDX_GLOBAL;
} else {
ver_idx = next_ver++;
ctx.arg.version_definitions.push_back(std::string(tok[0]));
tok = tok.subspan(1);
}
tok = skip(ctx, tok, "{");
parse_version_script_commands(ctx, tok, ver, false);
read_version_script_commands(ctx, tok, ver_idx, false);
tok = skip(ctx, tok, "}");
if (!tok.empty() && tok[0] != ";")
tok = tok.subspan(1);
tok = skip(ctx, tok, ";");
}
}
template <typename E>
void parse_version_script(Context<E> &ctx, std::string path) {
current_file<E> = MappedFile<Context<E>>::must_open(ctx, path);
std::vector<std::string_view> vec =
tokenize(ctx, current_file<E>->get_contents());
std::span<std::string_view> tok = vec;
read_version_script(ctx, tok);
if (!tok.empty())
SyntaxError(ctx, tok[0]) << "trailing garbage token";
}
template <typename E>
void parse_dynamic_list(Context<E> &ctx, std::string path) {
current_file<E> = MemoryMappedFile<E>::must_open(ctx, path);
std::vector<std::string_view> vec =
tokenize(ctx, current_file<E>->get_contents(ctx));
std::span<std::string_view> tok = vec;
tok = skip(ctx, tok, "{");
i16 ver = VER_NDX_GLOBAL;
void read_dynamic_list_commands(Context<E> &ctx, std::span<std::string_view> &tok,
bool is_cpp) {
while (!tok.empty() && tok[0] != "}") {
if (read_label(tok, "global")) {
ver = VER_NDX_GLOBAL;
continue;
}
if (tok[0] == "extern") {
tok = tok.subspan(1);
if (read_label(tok, "local")) {
ver = VER_NDX_LOCAL;
if (!tok.empty() && tok[0] == "\"C\"") {
tok = tok.subspan(1);
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, false);
} else {
tok = skip(ctx, tok, "\"C++\"");
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, true);
}
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");
continue;
}
if (tok[0] == "*")
ctx.arg.default_version = ver;
ctx.default_version = VER_NDX_GLOBAL;
else
ctx.arg.version_patterns.push_back({tok[0], ver, false});
ctx.version_patterns.push_back({unquote(tok[0]), VER_NDX_GLOBAL, is_cpp});
tok = skip(ctx, tok.subspan(1), ";");
}
}
template <typename E>
void parse_dynamic_list(Context<E> &ctx, std::string path) {
current_file<E> = MappedFile<Context<E>>::must_open(ctx, path);
std::vector<std::string_view> vec =
tokenize(ctx, current_file<E>->get_contents());
std::span<std::string_view> tok = vec;
tok = skip(ctx, tok, "{");
read_dynamic_list_commands(ctx, tok, false);
tok = skip(ctx, tok, "}");
tok = skip(ctx, tok, ";");
@@ -352,9 +397,9 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
#define INSTANTIATE(E) \
template \
void parse_linker_script(Context<E> &ctx, MemoryMappedFile<E> *mb); \
void parse_linker_script(Context<E> &ctx, MappedFile<Context<E>> *mf); \
template \
i64 get_script_output_type(Context<E> &ctx, MemoryMappedFile<E> *mb); \
i64 get_script_output_type(Context<E> &ctx, MappedFile<Context<E>> *mf); \
template \
void parse_version_script(Context<E> &ctx, std::string path); \
template \
@@ -362,3 +407,8 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+661
View File
@@ -0,0 +1,661 @@
// This file handles the linker plugin to support LTO (Link-Time
// Optimization).
//
// LTO is a technique to do whole-program optimization to a program. Since
// a linker sees the whole program as opposed to a single compilation
// unit, it in theory can do some optimizations that cannot be done in the
// usual separate compilation model. For example, LTO should be able to
// inline functions that are defined in other compilation unit.
//
// In GCC and Clang, all you have to do to enable LTO is adding the
// `-flto` flag to the compiler and the linker command lines. If `-flto`
// is given, the compiler generates a file that contains not machine code
// but the compiler's IR (intermediate representation). In GCC, the output
// is an ELF file which wraps GCC's IR. In LLVM, it's not even an ELF file
// but just a raw LLVM IR file.
//
// Here is what we have to do if at least one input file is not a usual
// ELF file but an IR object file:
//
// 1. Read symbols both from usual ELF files and from IR object files and
// resolve symbols as usual.
//
// 2. Pass all IR objects to the compiler backend. The compiler backend
// compiles the IRs and returns a few big ELF object files as a
// result.
//
// 3. Parse the returned ELF files and overwrite IR object symbols with
// the returned ones, discarding IR object files.
//
// 4. Continue the rest of the linking process as usual.
//
// When gcc or clang inovkes ld, they pass `-plugin linker-plugin.so` to
// the linker. The given .so file provides a way to call the compiler
// backend.
//
// The linker plugin API is documented at
// https://gcc.gnu.org/wiki/whopr/driver, though the document is a bit
// outdated.
//
// Frankly, the linker plugin API is peculiar and is not very easy to use.
// For some reason, the API functions don't return the result of a
// function call as a return value but instead calls other function with
// the result as its argument to "return" the result.
//
// For example, the first thing you need to do after dlopen()'ing a linker
// plugin .so is to call `onload` function with a list of callback
// functions. `onload` calls callbacks to notify about the pointers to
// other functions the linker plugin provides. I don't know why `onload`
// can't just return a list of functions or why the linker plugin can't
// define not only `onload` but other functions, but that's how it works.
//
// Here is the steps to use the linker plugin:
//
// 1. dlopen() the linker plugin .so and call `onload` to obtain pointers
// to other functions provided by the plugin.
//
// 2. Call `claim_file_hook` with an IR object file to read its symbol
// table. `claim_file_hook` calls the `add_symbols` callback to
// "return" a list of symbols.
//
// 3. `claim_file_hook` returns LDPT_OK only when the plugin wants to
// handle a given file. Since we pass only IR object files to the
// plugin in mold, it always returns LDPT_OK in our case.
//
// 4. Once we made a decision as to which object file to include into the
// output file, we call `all_symbols_read_hook` to compile IR objects
// into a few big ELF files. That function calls the `get_symbols`
// callback to ask us about the symbol resolution results. (The
// compiler backend needs to know whether an undefined symbol in an IR
// object was resolved to a regular object file or a shared object to
// do whole program optimization, for example.)
//
// 5. `all_symbols_read_hook` "returns" the result by calling the
// `add_input_file` callback. The callback is called with a path to an
// LTO'ed ELF file. We parse that ELF file and override symbols
// defined by IR objects with the ELF file's ones.
//
// 6. Lastly, we call `cleanup_hook` to remove temporary files created by
// the compiler backend.
#include "mold.h"
#include "../lto.h"
#include <cstdarg>
#include <cstring>
#include <dlfcn.h>
#include <fcntl.h>
#include <sstream>
#include <unistd.h>
#if 0
# define LOG std::cerr
#else
# define LOG std::ostringstream()
#endif
namespace mold::elf {
// Global variables
// We store LTO-related information to global variables,
// as the LTO plugin is not thread-safe by design anyway.
template <typename E> static Context<E> *gctx;
template <typename E> static std::vector<ObjectFile<E> *> lto_objects;
static int phase = 0;
static std::vector<PluginSymbol> plugin_symbols;
static ClaimFileHandler *claim_file_hook;
static AllSymbolsReadHandler *all_symbols_read_hook;
static CleanupHandler *cleanup_hook;
// Event handlers
static PluginStatus message(int level, const char *fmt, ...) {
LOG << "message\n";
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "mold: ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
return LDPS_OK;
}
template <typename E>
static PluginStatus register_claim_file_hook(ClaimFileHandler fn) {
LOG << "register_claim_file_hook\n";
claim_file_hook = fn;
return LDPS_OK;
}
template <typename E>
static PluginStatus
register_all_symbols_read_hook(AllSymbolsReadHandler fn) {
LOG << "register_all_symbols_read_hook\n";
all_symbols_read_hook = fn;
return LDPS_OK;
}
template <typename E>
static PluginStatus register_cleanup_hook(CleanupHandler fn) {
LOG << "register_cleanup_hook\n";
cleanup_hook = fn;
return LDPS_OK;
}
static PluginStatus
add_symbols(void *handle, int nsyms, const PluginSymbol *psyms) {
LOG << "add_symbols: " << nsyms << "\n";
assert(phase == 1);
plugin_symbols = {psyms, psyms + nsyms};
return LDPS_OK;
}
template <typename E>
static PluginStatus add_input_file(const char *path) {
LOG << "add_input_file: " << path << "\n";
Context<E> &ctx = *gctx<E>;
static i64 file_priority = 100;
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::must_open(ctx, path);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, "", false);
ctx.obj_pool.emplace_back(file);
lto_objects<E>.push_back(file);
file->priority = file_priority++;
file->is_alive = true;
file->parse(ctx);
file->resolve_symbols(ctx);
return LDPS_OK;
}
static PluginStatus
get_input_file(const void *handle, struct PluginInputFile *file) {
LOG << "get_input_file\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus release_input_file(const void *handle) {
LOG << "release_input_file\n";
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
if (file.mf->fd != -1) {
close(file.mf->fd);
file.mf->fd = -1;
}
return LDPS_OK;
}
static PluginStatus add_input_library(const char *path) {
LOG << "add_input_library\n";
return LDPS_OK;
}
static PluginStatus set_extra_library_path(const char *path) {
LOG << "set_extra_library_path\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus get_view(const void *handle, const void **view) {
LOG << "get_view\n";
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
*view = (void *)file.mf->data;
return LDPS_OK;
}
static PluginStatus
get_input_section_count(const void *handle, int *count) {
LOG << "get_input_section_count\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_type(const PluginSection section, int *type) {
LOG << "get_input_section_type\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_name(const PluginSection section,
char **section_name) {
LOG << "get_input_section_name\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_contents(const PluginSection section,
const char **section_contents,
size_t *len) {
LOG << "get_input_section_contents\n";
return LDPS_OK;
}
static PluginStatus
update_section_order(const PluginSection *section_list,
int num_sections) {
LOG << "update_section_order\n";
return LDPS_OK;
}
static PluginStatus allow_section_ordering() {
LOG << "allow_section_ordering\n";
return LDPS_OK;
}
static PluginStatus
get_symbols_v1(const void *handle, int nsyms, PluginSymbol *psyms) {
unreachable();
}
// get_symbols teaches the LTO plugin as to how we resolved symbols.
// The plugin uses the symbol resolution info to optimizes the program.
//
// For example, if a definition in an IR file is not referenced by
// non-IR objects at all, the plugin may choose to completely inline
// that definition within the IR objects and remove the symbol from the
// LTO result. On the other hand, if a definition is referenced by a
// non-IR object, it has to keep the symbol in the LTO result.
template <typename E>
static PluginStatus
get_symbols(const void *handle, int nsyms, PluginSymbol *psyms, bool is_v2) {
ObjectFile<E> &file = *(ObjectFile<E> *)handle;
assert(file.is_lto_obj);
// If file is an archive member which was not chose to be included in
// to the final result, we need to make the plugin to ignore all
// symbols.
if (!file.is_alive) {
assert(!is_v2);
for (int i = 0; i < nsyms; i++)
psyms[i].resolution = LDPR_PREEMPTED_REG;
return LDPS_NO_SYMS;
}
auto get_resolution = [&](ElfSym<E> &esym, Symbol<E> &sym) {
if (!sym.file)
return LDPR_UNDEF;
if (sym.file == &file) {
if (sym.referenced_by_regular_obj)
return LDPR_PREVAILING_DEF;
if (sym.is_exported)
return is_v2 ? LDPR_PREVAILING_DEF : LDPR_PREVAILING_DEF_IRONLY_EXP;
return LDPR_PREVAILING_DEF_IRONLY;
}
if (sym.file->is_dso)
return LDPR_RESOLVED_DYN;
if (((ObjectFile<E> *)sym.file)->is_lto_obj)
return esym.is_undef() ? LDPR_RESOLVED_IR : LDPR_PREEMPTED_IR;
return esym.is_undef() ? LDPR_RESOLVED_EXEC : LDPR_PREEMPTED_REG;
};
// Set the symbol resolution results to psyms.
for (i64 i = 0; i < nsyms; i++) {
ElfSym<E> &esym = file.elf_syms[i + 1];
Symbol<E> &sym = *file.symbols[i + 1];
psyms[i].resolution = get_resolution(esym, sym);
}
return LDPS_OK;
}
template <typename E>
static PluginStatus
get_symbols_v2(const void *handle, int nsyms, PluginSymbol *psyms) {
LOG << "get_symbols_v2\n";
return get_symbols<E>(handle, nsyms, psyms, true);
}
template <typename E>
static PluginStatus
get_symbols_v3(const void *handle, int nsyms, PluginSymbol *psyms) {
LOG << "get_symbols_v3\n";
return get_symbols<E>(handle, nsyms, psyms, false);
}
static PluginStatus allow_unique_segment_for_sections() {
LOG << "allow_unique_segment_for_sections\n";
return LDPS_OK;
}
static PluginStatus
unique_segment_for_sections(const char *segment_name,
uint64_t flags,
uint64_t align,
const PluginSection *section_list,
int num_sections) {
LOG << "unique_segment_for_sections\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_alignment(const PluginSection section,
int *addralign) {
LOG << "get_input_section_alignment\n";
return LDPS_OK;
}
static PluginStatus
get_input_section_size(const PluginSection section, uint64_t *size) {
LOG << "get_input_section_size\n";
return LDPS_OK;
}
template <typename E>
static PluginStatus
register_new_input_hook(NewInputHandler fn) {
LOG << "register_new_input_hook\n";
return LDPS_OK;
}
static PluginStatus
get_wrap_symbols(uint64_t *num_symbols, const char ***wrap_symbols) {
LOG << "get_wrap_symbols\n";
return LDPS_OK;
}
template <typename E>
static void load_plugin(Context<E> &ctx) {
assert(phase == 0);
phase = 1;
gctx<E> = &ctx;
void *handle = dlopen(ctx.arg.plugin.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle)
Fatal(ctx) << "could not open plugin file: " << dlerror();
OnloadFn *onload = (OnloadFn *)dlsym(handle, "onload");
if (!onload)
Fatal(ctx) << "failed to load plugin " << ctx.arg.plugin << ": "
<< dlerror();
auto save = [&](std::string_view str) {
return save_string(ctx, std::string(str).c_str()).data();
};
std::vector<PluginTagValue> tv;
tv.emplace_back(LDPT_MESSAGE, message);
if (ctx.arg.shared)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_DYN);
else if (ctx.arg.pie)
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_PIE);
else
tv.emplace_back(LDPT_LINKER_OUTPUT, LDPO_EXEC);
for (std::string_view opt : ctx.arg.plugin_opt)
tv.emplace_back(LDPT_OPTION, save(opt));
tv.emplace_back(LDPT_REGISTER_CLAIM_FILE_HOOK, register_claim_file_hook<E>);
tv.emplace_back(LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
register_all_symbols_read_hook<E>);
tv.emplace_back(LDPT_REGISTER_CLEANUP_HOOK, register_cleanup_hook<E>);
tv.emplace_back(LDPT_ADD_SYMBOLS, add_symbols);
tv.emplace_back(LDPT_GET_SYMBOLS, get_symbols_v1);
tv.emplace_back(LDPT_ADD_INPUT_FILE, add_input_file<E>);
tv.emplace_back(LDPT_GET_INPUT_FILE, get_input_file);
tv.emplace_back(LDPT_RELEASE_INPUT_FILE, release_input_file<E>);
tv.emplace_back(LDPT_ADD_INPUT_LIBRARY, add_input_library);
tv.emplace_back(LDPT_OUTPUT_NAME, save(ctx.arg.output));
tv.emplace_back(LDPT_SET_EXTRA_LIBRARY_PATH, set_extra_library_path);
tv.emplace_back(LDPT_GET_VIEW, get_view<E>);
tv.emplace_back(LDPT_GET_INPUT_SECTION_COUNT, get_input_section_count);
tv.emplace_back(LDPT_GET_INPUT_SECTION_TYPE, get_input_section_type);
tv.emplace_back(LDPT_GET_INPUT_SECTION_NAME, get_input_section_name);
tv.emplace_back(LDPT_GET_INPUT_SECTION_CONTENTS, get_input_section_contents);
tv.emplace_back(LDPT_UPDATE_SECTION_ORDER, update_section_order);
tv.emplace_back(LDPT_ALLOW_SECTION_ORDERING, allow_section_ordering);
tv.emplace_back(LDPT_GET_SYMBOLS_V2, get_symbols_v2<E>);
tv.emplace_back(LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
allow_unique_segment_for_sections);
tv.emplace_back(LDPT_UNIQUE_SEGMENT_FOR_SECTIONS, unique_segment_for_sections);
tv.emplace_back(LDPT_GET_SYMBOLS_V3, get_symbols_v3<E>);
tv.emplace_back(LDPT_GET_INPUT_SECTION_ALIGNMENT, get_input_section_alignment);
tv.emplace_back(LDPT_GET_INPUT_SECTION_SIZE, get_input_section_size);
tv.emplace_back(LDPT_REGISTER_NEW_INPUT_HOOK, register_new_input_hook<E>);
tv.emplace_back(LDPT_GET_WRAP_SYMBOLS, get_wrap_symbols);
tv.emplace_back(LDPT_NULL, 0);
onload(tv.data());
}
template <typename E>
static ElfSym<E> to_elf_sym(PluginSymbol &psym) {
ElfSym<E> esym = {};
switch (psym.def) {
case LDPK_DEF:
esym.st_shndx = SHN_ABS;
break;
case LDPK_WEAKDEF:
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_WEAK;
break;
case LDPK_UNDEF:
esym.st_shndx = SHN_UNDEF;
break;
case LDPK_WEAKUNDEF:
esym.st_shndx = SHN_UNDEF;
esym.st_bind = STB_WEAK;
break;
case LDPK_COMMON:
esym.st_shndx = SHN_COMMON;
break;
}
switch (psym.symbol_type) {
case LDST_UNKNOWN:
break;
case LDST_FUNCTION:
esym.st_type = STT_FUNC;
break;
case LDST_VARIABLE:
esym.st_type = STT_OBJECT;
break;
};
switch (psym.visibility) {
case LDPV_DEFAULT:
break;
case LDPV_PROTECTED:
esym.st_visibility = STV_PROTECTED;
break;
case LDPV_INTERNAL:
esym.st_visibility = STV_INTERNAL;
break;
case LDPV_HIDDEN:
esym.st_visibility = STV_HIDDEN;
break;
}
esym.st_size = psym.size;
return esym;
}
// Returns true if a given linker plugin looks like LLVM's one.
// Returns false if it's GCC.
template <typename E>
static bool is_llvm(Context<E> &ctx) {
return ctx.arg.plugin.ends_with("LLVMgold.so");
}
// Returns true if a given linker plugin supports the get_symbols_v3 API.
// Currently, we simply assume that LLVM supports it and GCC does not.
template <typename E>
static bool suppots_v3_api(Context<E> &ctx) {
return is_llvm(ctx);
}
template <typename E>
ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
LOG << "read_lto_object: " << mf->name << "\n";
if (ctx.arg.plugin.empty())
Fatal(ctx) << mf->name << ": don't know how to handle this LTO object file "
<< "because no -plugin option was given. Please make sure you "
<< "added -flto not only for creating object files but also for "
<< "creating the final executable.";
// dlopen the linker plugin file
static std::once_flag flag;
std::call_once(flag, [&] { load_plugin(ctx); });
// Create mold's object instance
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.emplace_back(obj);
obj->filename = mf->name;
obj->symbols.push_back(new Symbol<E>);
obj->first_global = 1;
obj->is_lto_obj = true;
obj->mf = mf;
// Create plugin's object instance
PluginInputFile file = {};
MappedFile<Context<E>> *mf2 = mf->parent ? mf->parent : mf;
file.name = save_string(ctx, mf2->name).data();
if (mf2->fd == -1)
mf2->fd = open(file.name, O_RDONLY);
file.fd = mf2->fd;
if (file.fd == -1)
Fatal(ctx) << "cannot open " << file.name << ": " << errno_string();
if (mf->parent)
obj->archive_name = mf->parent->name;
file.offset = mf->get_offset();
file.filesize = mf->size;
file.handle = (void *)obj;
LOG << "read_lto_symbols: "<< mf->name << "\n";
// claim_file_hook() calls add_symbols() which initializes `plugin_symbols`
int claimed = false;
claim_file_hook(&file, &claimed);
if (!claimed)
Fatal(ctx) << mf->name << ": not claimed by the LTO plugin";
// It looks like GCC doesn't need fd after claim_file_hook() while
// LLVM needs it and takes the ownership of fd. To prevent "too many
// open files" issue, we close fd only for GCC. This is ugly, though.
if (!is_llvm(ctx)) {
close(mf2->fd);
mf2->fd = -1;
}
// Initialize object symbols
std::vector<ElfSym<E>> *esyms = new std::vector<ElfSym<E>>(1);
for (PluginSymbol &psym : plugin_symbols) {
esyms->push_back(to_elf_sym<E>(psym));
obj->symbols.push_back(get_symbol(ctx, save_string(ctx, psym.name)));
}
obj->elf_syms = *esyms;
obj->sym_fragments.resize(esyms->size());
obj->symvers.resize(esyms->size());
plugin_symbols.clear();
return obj;
}
// This function restarts mold itself with `--:lto-pass2` and
// `--:ignore-ir-file` flags. We do this as a workaround for the old
// linker plugins that do not support the get_symbols_v3 API.
//
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
// object file we previously passed to the linker plugin. So we can't
// "unload" object files in archives that we ended up not choosing to
// include into the final output.
//
// As a workaround, we restart the linker with a list of object files
// the linker has to ignore, so that it won't read the object files
// from archives next time.
//
// This is an ugly hack and should be removed once GCC adopts the v3 API.
template <typename E>
static void restart_process(Context<E> &ctx) {
std::vector<const char *> args;
for (std::string_view arg : ctx.cmdline_args)
args.push_back(strdup(std::string(arg).c_str()));
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
if (file->is_lto_obj && !file->is_alive)
args.push_back(strdup(("--:ignore-ir-file=" +
file->mf->get_identifier()).c_str()));
args.push_back("--:lto-pass2");
args.push_back(nullptr);
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::cout << std::flush;
std::cerr << std::flush;
execv(self.c_str(), (char * const *)args.data());
std::cerr << "execv failed: " << errno_string() << "\n";
_exit(1);
}
// Entry point
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
Timer t(ctx, "do_lto");
if (!ctx.arg.lto_pass2 && !suppots_v3_api(ctx))
restart_process(ctx);
assert(phase == 1);
phase = 2;
// Set `referenced_by_regular_obj` bit.
for (ObjectFile<E> *file : ctx.objs) {
if (file->is_lto_obj)
continue;
for (i64 i = file->first_global; i < file->symbols.size(); i++) {
ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && sym.file && sym.file != file &&
!sym.file->is_dso && ((ObjectFile<E> *)sym.file)->is_lto_obj) {
std::scoped_lock lock(sym.mu);
sym.referenced_by_regular_obj = true;
}
}
}
// all_symbols_read_hook() calls add_input_file() and add_input_library()
LOG << "all symbols read\n";
if (PluginStatus st = all_symbols_read_hook(); st != LDPS_OK)
Fatal(ctx) << "LTO: all_symbols_read_hook returns " << st;
return lto_objects<E>;
}
template <typename E>
void lto_cleanup(Context<E> &ctx) {
Timer t(ctx, "lto_cleanup");
if (cleanup_hook)
cleanup_hook();
}
#define INSTANTIATE(E) \
template ObjectFile<E> * \
read_lto_object(Context<E> &, MappedFile<Context<E>> *); \
template std::vector<ObjectFile<E> *> do_lto(Context<E> &); \
template void lto_cleanup(Context<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+772
View File
@@ -0,0 +1,772 @@
#include "mold.h"
#include "../archive-file.h"
#include "../cmdline.h"
#include <cstring>
#include <functional>
#include <iomanip>
#include <map>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tbb/global_control.h>
#include <tbb/parallel_for_each.h>
#include <unistd.h>
#include <unordered_set>
namespace mold::elf {
template <typename E>
static ObjectFile<E> *new_object_file(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
if (i64 type = ((ElfEhdr<E> *)mf->data)->e_machine; type != E::e_machine)
Fatal(ctx) << mf->name << ": incompatible file type: " << type;
static Counter count("parsed_objs");
count++;
bool in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mf, archive_name, in_lib);
file->priority = ctx.file_priority++;
ctx.tg.run([file, &ctx] { file->parse(ctx); });
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
}
template <typename E>
static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
static Counter count("parsed_lto_objs");
count++;
if (ctx.arg.ignore_ir_file.count(mf->get_identifier()))
return new ObjectFile<E>;
ObjectFile<E> *file = read_lto_object(ctx, mf);
file->priority = ctx.file_priority++;
file->is_in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
file->is_alive = !file->is_in_lib;
ctx.has_lto_object = true;
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
}
template <typename E>
static SharedFile<E> *
new_shared_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
if (i64 type = ((ElfEhdr<E> *)mf->data)->e_machine; type != E::e_machine)
Fatal(ctx) << mf->name << ": incompatible file type: " << type;
SharedFile<E> *file = SharedFile<E>::create(ctx, mf);
file->priority = ctx.file_priority++;
ctx.tg.run([file, &ctx] { file->parse(ctx); });
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
}
template <typename E>
void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
if (ctx.visited.contains(mf->name))
return;
FileType type = get_file_type(mf);
switch (type) {
case FileType::ELF_OBJ:
ctx.objs.push_back(new_object_file(ctx, mf, ""));
return;
case FileType::ELF_DSO:
ctx.dsos.push_back(new_shared_file(ctx, mf));
ctx.visited.insert(mf->name);
return;
case FileType::AR:
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf)) {
switch (get_file_type(child)) {
case FileType::ELF_OBJ:
ctx.objs.push_back(new_object_file(ctx, child, mf->name));
break;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, child, mf->name));
break;
default:
break;
}
}
ctx.visited.insert(mf->name);
return;
case FileType::TEXT:
parse_linker_script(ctx, mf);
return;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, mf, ""));
return;
default:
Fatal(ctx) << mf->name << ": unknown file type: " << type;
}
}
// Read the beginning of a given file and returns its machine type
// (e.g. EM_X86_64 or EM_386). Return -1 if unknown.
template <typename E>
static i64 get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
switch (get_file_type(mf)) {
case FileType::ELF_OBJ:
case FileType::ELF_DSO:
case FileType::GCC_LTO_OBJ:
return ((ElfEhdr<E> *)mf->data)->e_machine;
case FileType::AR:
for (MappedFile<Context<E>> *child : read_fat_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
return ((ElfEhdr<E> *)child->data)->e_machine;
return -1;
case FileType::THIN_AR:
for (MappedFile<Context<E>> *child : read_thin_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
return ((ElfEhdr<E> *)child->data)->e_machine;
return -1;
case FileType::TEXT:
return get_script_output_type(ctx, mf);
default:
return -1;
}
}
template <typename E>
static i64
deduce_machine_type(Context<E> &ctx, std::span<std::string_view> args) {
for (std::string_view arg : args)
if (!arg.starts_with('-'))
if (auto *mf = MappedFile<Context<E>>::open(ctx, std::string(arg)))
if (i64 type = get_machine_type(ctx, mf); type != -1)
return type;
Fatal(ctx) << "-m option is missing";
}
template <typename E>
MappedFile<Context<E>> *open_library(Context<E> &ctx, std::string path) {
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path);
if (!mf)
return nullptr;
i64 type = get_machine_type(ctx, mf);
if (type == -1 || type == E::e_machine)
return mf;
Warn(ctx) << path << ": skipping incompatible file " << (int)type
<< " " << (int)E::e_machine;
return nullptr;
}
template <typename E>
MappedFile<Context<E>> *find_library(Context<E> &ctx, std::string name) {
if (name.starts_with(':')) {
for (std::string_view dir : ctx.arg.library_paths) {
std::string path = std::string(dir) + "/" + name.substr(1);
if (MappedFile<Context<E>> *mf = open_library(ctx, path))
return mf;
}
Fatal(ctx) << "library not found: " << name;
}
for (std::string_view dir : ctx.arg.library_paths) {
std::string stem = std::string(dir) + "/lib" + name;
if (!ctx.is_static)
if (MappedFile<Context<E>> *mf = open_library(ctx, stem + ".so"))
return mf;
if (MappedFile<Context<E>> *mf = open_library(ctx, stem + ".a"))
return mf;
}
Fatal(ctx) << "library not found: " << name;
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string_view> args) {
Timer t(ctx, "read_input_files");
std::vector<std::tuple<bool, bool, bool, bool>> state;
ctx.is_static = ctx.arg.is_static;
while (!args.empty()) {
std::string_view arg;
if (read_flag(args, "as-needed")) {
ctx.as_needed = true;
} else if (read_flag(args, "no-as-needed")) {
ctx.as_needed = false;
} else if (read_flag(args, "whole-archive")) {
ctx.whole_archive = true;
} else if (read_flag(args, "no-whole-archive")) {
ctx.whole_archive = false;
} else if (read_flag(args, "Bstatic")) {
ctx.is_static = true;
} else if (read_flag(args, "Bdynamic")) {
ctx.is_static = false;
} else if (read_flag(args, "start-lib")) {
ctx.in_lib = true;
} else if (read_flag(args, "end-lib")) {
ctx.in_lib = false;
} else if (read_arg(ctx, args, arg, "version-script")) {
parse_version_script(ctx, std::string(arg));
} else if (read_arg(ctx, args, arg, "dynamic-list")) {
parse_dynamic_list(ctx, std::string(arg));
} else if (read_flag(args, "push-state")) {
state.push_back({ctx.as_needed, ctx.whole_archive, ctx.is_static,
ctx.in_lib});
} else if (read_flag(args, "pop-state")) {
if (state.empty())
Fatal(ctx) << "no state pushed before popping";
std::tie(ctx.as_needed, ctx.whole_archive, ctx.is_static, ctx.in_lib) =
state.back();
state.pop_back();
} else if (read_arg(ctx, args, arg, "l")) {
MappedFile<Context<E>> *mf = find_library(ctx, std::string(arg));
mf->given_fullpath = false;
read_file(ctx, mf);
} else {
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, std::string(args[0])));
args = args.subspan(1);
}
}
if (ctx.objs.empty())
Fatal(ctx) << "no input files";
ctx.tg.wait();
}
template <typename E>
static i64 get_mtime(Context<E> &ctx, std::string path) {
struct stat st;
if (stat(path.c_str(), &st) < 0)
Fatal(ctx) << path << ": stat failed: " << errno_string();
return st.st_mtime;
}
template <typename E>
static bool reload_input_files(Context<E> &ctx) {
Timer t(ctx, "reload_input_files");
std::vector<ObjectFile<E> *> objs;
std::vector<SharedFile<E> *> dsos;
// Reload updated .o files
for (ObjectFile<E> *file : ctx.objs) {
if (file->mf->parent) {
if (get_mtime(ctx, file->mf->parent->name) != file->mf->parent->mtime)
return false;
objs.push_back(file);
continue;
}
if (get_mtime(ctx, file->mf->name) == file->mf->mtime) {
objs.push_back(file);
continue;
}
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, file->mf->name);
objs.push_back(new_object_file(ctx, mf, file->mf->name));
}
// Reload updated .so files
for (SharedFile<E> *file : ctx.dsos) {
if (get_mtime(ctx, file->mf->name) == file->mf->mtime) {
dsos.push_back(file);
} else {
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, file->mf->name);
dsos.push_back(new_shared_file(ctx, mf));
}
}
ctx.objs = objs;
ctx.dsos = dsos;
return true;
}
template <typename E>
static void show_stats(Context<E> &ctx) {
for (ObjectFile<E> *obj : ctx.objs) {
static Counter defined("defined_syms");
defined += obj->first_global - 1;
static Counter undefined("undefined_syms");
undefined += obj->symbols.size() - obj->first_global;
for (std::unique_ptr<InputSection<E>> &sec : obj->sections) {
if (!sec || !sec->is_alive)
continue;
static Counter alloc("reloc_alloc");
static Counter nonalloc("reloc_nonalloc");
if (sec->shdr().sh_flags & SHF_ALLOC)
alloc += sec->get_rels(ctx).size();
else
nonalloc += sec->get_rels(ctx).size();
}
static Counter comdats("comdats");
comdats += obj->comdat_groups.size();
static Counter removed_comdats("removed_comdat_mem");
for (auto &pair : obj->comdat_groups)
if (ComdatGroup *group = pair.first; group->owner != obj->priority)
removed_comdats += pair.second.size();
static Counter num_cies("num_cies");
num_cies += obj->cies.size();
static Counter num_unique_cies("num_unique_cies");
for (CieRecord<E> &cie : obj->cies)
if (cie.is_leader)
num_unique_cies++;
static Counter num_fdes("num_fdes");
num_fdes += obj->fdes.size();
}
static Counter num_bytes("total_input_bytes");
for (std::unique_ptr<MappedFile<Context<E>>> &mf : ctx.mf_pool)
num_bytes += mf->size;
static Counter num_input_sections("input_sections");
for (ObjectFile<E> *file : ctx.objs)
num_input_sections += file->sections.size();
static Counter num_output_chunks("output_chunks", ctx.chunks.size());
static Counter num_objs("num_objs", ctx.objs.size());
static Counter num_dsos("num_dsos", ctx.dsos.size());
if constexpr (std::is_same_v<E, ARM64>) {
static Counter num_thunks("num_thunks");
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
for (std::unique_ptr<RangeExtensionThunk<E>> &thunk : osec->thunks)
num_thunks += thunk->symbols.size();
}
Counter::print();
for (std::unique_ptr<MergedSection<E>> &sec : ctx.merged_sections)
sec->print_stats(ctx);
}
static i64 get_default_thread_count() {
// mold doesn't scale well above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
template <typename E>
static int elf_main(int argc, char **argv) {
Context<E> ctx;
// Process -run option first. process_run_subcommand() does not return.
if (argc >= 2)
if (argv[1] == "-run"sv || argv[1] == "--run"sv)
process_run_subcommand(ctx, argc, argv);
// Parse non-positional command line options
ctx.cmdline_args = expand_response_files(ctx, argv);
std::vector<std::string_view> file_args;
parse_nonpositional_args(ctx, file_args);
// If no -m option is given, deduce it from input files.
if (ctx.arg.emulation == -1)
ctx.arg.emulation = deduce_machine_type(ctx, file_args);
// Redo if -m is not x86-64.
if (ctx.arg.emulation != E::e_machine) {
switch (ctx.arg.emulation) {
case EM_386:
return elf_main<I386>(argc, argv);
case EM_AARCH64:
return elf_main<ARM64>(argc, argv);
case EM_ARM:
return elf_main<ARM32>(argc, argv);
case EM_RISCV:
return elf_main<RISCV64>(argc, argv);
}
unreachable();
}
Timer t_all(ctx, "all");
if (ctx.arg.relocatable) {
combine_objects(ctx, file_args);
return 0;
}
if (!ctx.arg.preload)
try_resume_daemon(ctx);
i64 thread_count = ctx.arg.thread_count;
if (thread_count == 0)
thread_count = get_default_thread_count();
tbb::global_control tbb_cont(tbb::global_control::max_allowed_parallelism,
thread_count);
install_signal_handler();
if (!ctx.arg.directory.empty())
if (chdir(ctx.arg.directory.c_str()) == -1)
Fatal(ctx) << "chdir failed: " << ctx.arg.directory
<< ": " << errno_string();
// Handle --wrap options if any.
for (std::string_view name : ctx.arg.wrap)
get_symbol(ctx, name)->wrap = true;
// Handle --retain-symbols-file options if any.
if (ctx.arg.retain_symbols_file)
for (std::string_view name : *ctx.arg.retain_symbols_file)
get_symbol(ctx, name)->write_to_symtab = true;
// Preload input files
std::function<void()> on_complete;
std::function<void()> wait_for_client;
if (ctx.arg.preload)
daemonize(ctx, &wait_for_client, &on_complete);
else if (ctx.arg.fork)
on_complete = fork_child();
for (std::string_view arg : ctx.arg.trace_symbol)
get_symbol(ctx, arg)->traced = true;
// Parse input files
read_input_files(ctx, file_args);
if (ctx.arg.preload) {
wait_for_client();
if (!reload_input_files(ctx)) {
std::vector<char *> args(argv, argv + argc);
args.push_back((char *)"--no-preload");
args.push_back(nullptr);
return elf_main<E>(argc + 1, args.data());
}
}
// Uniquify shared object files by soname
{
std::unordered_set<std::string_view> seen;
std::erase_if(ctx.dsos, [&](SharedFile<E> *file) {
return !seen.insert(file->soname).second;
});
}
Timer t_total(ctx, "total");
Timer t_before_copy(ctx, "before_copy");
// Apply -exclude-libs
apply_exclude_libs(ctx);
// Resolve symbols and fix the set of object files that are
// included to the final output.
resolve_symbols(ctx);
// Resolve mergeable section pieces to merge them.
register_section_pieces(ctx);
// Remove redundant comdat sections (e.g. duplicate inline functions).
eliminate_comdats(ctx);
// Create .bss sections for common symbols.
convert_common_symbols(ctx);
// Apply version scripts.
apply_version_script(ctx);
// Parse symbol version suffixes (e.g. "foo@ver1").
parse_symbol_version(ctx);
// Set is_import and is_export bits for each symbol.
compute_import_export(ctx);
// Garbage-collect unreachable sections.
if (ctx.arg.gc_sections)
gc_sections(ctx);
// Merge identical read-only sections.
if (ctx.arg.icf)
icf_sections(ctx);
// 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);
// Get a list of output sections.
append(ctx.chunks, collect_output_sections(ctx));
// Create a dummy file containing linker-synthesized symbols
// (e.g. `__bss_start`).
ctx.internal_obj = create_internal_file(ctx);
ctx.objs.push_back(ctx.internal_obj);
// Beyond this point, no new files will be added to ctx.objs
// or ctx.dsos.
// Handle `-z cet-report`.
if (ctx.arg.z_cet_report != CET_REPORT_NONE)
check_cet_errors(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.
//
// If we are linking an executable, weak undefs are converted to
// weakly imported symbol so that they'll have another chance to be
// resolved.
claim_unresolved_symbols(ctx);
// Beyond this point, no new symbols will be added to the result.
// Handle -repro
if (ctx.arg.repro)
write_repro_file(ctx);
// Make sure that all symbols have been resolved.
if (!ctx.arg.allow_multiple_definition)
check_duplicate_symbols(ctx);
// Handle --require-defined
for (std::string_view name : ctx.arg.require_defined)
if (!get_symbol(ctx, name)->file)
Error(ctx) << "--require-defined: undefined symbol: " << name;
// .init_array and .fini_array contents have to be sorted by
// a special rule. Sort them.
sort_init_fini(ctx);
// Likewise, .ctors and .dtors have to be sorted. They are rare
// because they are superceded by .init_array/.fini_array, though.
sort_ctor_dtor(ctx);
// Handle --shuffle-sections
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE)
shuffle_sections(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
// Sort sections by section attributes so that we'll have to
// create as few segments as possible.
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
return get_section_rank(ctx, a) < get_section_rank(ctx, b);
});
// Copy string referred by .dynamic to .dynstr.
for (SharedFile<E> *file : ctx.dsos)
ctx.dynstr->add_string(file->soname);
for (std::string_view str : ctx.arg.auxiliary)
ctx.dynstr->add_string(str);
for (std::string_view str : ctx.arg.filter)
ctx.dynstr->add_string(str);
if (!ctx.arg.rpaths.empty())
ctx.dynstr->add_string(ctx.arg.rpaths);
if (!ctx.arg.soname.empty())
ctx.dynstr->add_string(ctx.arg.soname);
// Scan relocations to find symbols that need entries in .got, .plt,
// .got.plt, .dynsym, .dynstr, etc.
scan_rels(ctx);
// If --packed_dyn_relocs=relr was given, base relocations are stored
// to a .relr.dyn section in a compressed form. Construct a compressed
// relocations now so that we can fix section sizes and file layout.
if (ctx.arg.pack_dyn_relocs_relr)
construct_relr(ctx);
// Reserve a space for dynamic symbol strings in .dynstr and sort
// .dynsym contents if necessary. Beyond this point, no symbol will
// be added to .dynsym.
ctx.dynsym->finalize(ctx);
// Fill .gnu.version_d section contents.
if (ctx.verdef)
ctx.verdef->construct(ctx);
// Fill .gnu.version_r section contents.
ctx.verneed->construct(ctx);
// Compute .symtab and .strtab sizes for each file.
create_output_symtab(ctx);
// .eh_frame is a special section from the linker's point of view,
// as its contents are parsed and reconstructed by the linker,
// unlike other sections that are regarded as opaque bytes.
// Here, we construct output .eh_frame contents.
ctx.eh_frame->construct(ctx);
// Handle --gdb-index.
if (ctx.arg.gdb_index)
ctx.gdb_index->construct(ctx);
// If --emit-relocs is given, we'll copy relocation sections from input
// files to an output file.
if (ctx.arg.emit_relocs)
create_reloc_sections(ctx);
// Update sh_size for each chunk and remove empty ones.
for (Chunk<E> *chunk : ctx.chunks)
chunk->update_shdr(ctx);
std::erase_if(ctx.chunks, [](Chunk<E> *chunk) {
return !chunk->is_output_section() && chunk->shdr.sh_size == 0;
});
// Set section indices.
for (i64 i = 0, shndx = 1; i < ctx.chunks.size(); i++)
if (!ctx.chunks[i]->is_header())
ctx.chunks[i]->shndx = shndx++;
// Some types of section header refer other section by index.
// Recompute the section header to fill such fields with correct values.
for (Chunk<E> *chunk : ctx.chunks)
chunk->update_shdr(ctx);
// Assign offsets to output sections
i64 filesize = set_osec_offsets(ctx);
// On ARM64, we may need to create so-called "range extension thunks"
// to extend branch instructions reach, as they can jump only to
// ±128 MiB.
if constexpr (std::is_same_v<E, ARM64>)
filesize = create_range_extension_thunks(ctx);
// On RISC-V, branches are encode using multiple instructions so
// that they can jump to anywhere in ±2 GiB by default. They may
// be replaced with shorter instruction sequences if destinations
// are close enough. Do this optimization.
if constexpr (std::is_same_v<E, RISCV64>)
filesize = riscv_resize_sections(ctx);
// Fix linker-synthesized symbol addresses.
fix_synthetic_symbols(ctx);
// Beyond this, you can assume that symbol addresses including their
// GOT or PLT addresses have a correct final value.
// 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();
// Create an output file
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0777);
ctx.buf = ctx.output_file->buf;
Timer t_copy(ctx, "copy");
// Copy input sections to the output file
{
Timer t(ctx, "copy_buf");
tbb::parallel_for_each(ctx.chunks, [&](Chunk<E> *chunk) {
std::string name =
chunk->name.empty() ? "(header)" : std::string(chunk->name);
Timer t2(ctx, name, &t);
chunk->copy_buf(ctx);
});
ctx.checkpoint();
}
if constexpr (std::is_same_v<E, ARM64>)
write_thunks(ctx);
if constexpr (std::is_same_v<E, ARM32>)
sort_arm_exidx(ctx);
// Some part of .gdb_index couldn't be computed until other debug
// sections are complete. We have complete debug sections now, so
// write the rest of .gdb_index.
if (ctx.gdb_index)
ctx.gdb_index->write_address_areas(ctx);
// Dynamic linker works better with sorted .rela.dyn section,
// so we sort them.
ctx.reldyn->sort(ctx);
// Zero-clear paddings between sections
clear_padding(ctx);
if (ctx.buildid) {
Timer t(ctx, "build_id");
ctx.buildid->write_buildid(ctx);
}
t_copy.stop();
// Close the output file. This is the end of the linker's main job.
ctx.output_file->close(ctx);
// Handle --dependency-file
if (!ctx.arg.dependency_file.empty())
write_dependency_file(ctx);
// Handle --print-dependencies
if (ctx.arg.print_dependencies == 1)
print_dependencies(ctx);
else if (ctx.arg.print_dependencies == 2)
print_dependencies_full(ctx);
if (ctx.has_lto_object)
lto_cleanup(ctx);
t_total.stop();
t_all.stop();
if (ctx.arg.print_map)
print_map(ctx);
// Show stats numbers
if (ctx.arg.stats)
show_stats(ctx);
if (ctx.arg.perf)
print_timer_records(ctx.timer_records);
std::cout << std::flush;
std::cerr << std::flush;
if (on_complete)
on_complete();
if (ctx.arg.quick_exit)
_exit(0);
for (std::function<void()> &fn : ctx.on_exit)
fn();
return 0;
}
int main(int argc, char **argv) {
return elf_main<X86_64>(argc, argv);
}
#define INSTANTIATE(E) \
template void read_file(Context<E> &, MappedFile<Context<E>> *);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+31 -17
View File
@@ -7,6 +7,8 @@
#include <tbb/parallel_for_each.h>
#include <unordered_map>
namespace mold::elf {
template <typename E>
using Map =
tbb::concurrent_hash_map<InputSection<E> *, std::vector<Symbol<E> *>>;
@@ -16,7 +18,7 @@ static std::unique_ptr<std::ofstream> open_output_file(Context<E> &ctx) {
std::unique_ptr<std::ofstream> file(new std::ofstream);
file->open(ctx.arg.Map.c_str());
if (!file->is_open())
Fatal(ctx) << "cannot open " << ctx.arg.Map << ": " << strerror(errno);
Fatal(ctx) << "cannot open " << ctx.arg.Map << ": " << errno_string();
return file;
}
@@ -26,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);
}
}
@@ -48,7 +51,6 @@ static Map<E> get_map(Context<E> &ctx) {
template <typename E>
void print_map(Context<E> &ctx) {
std::ostream *out = &std::cout;
std::unique_ptr<std::ofstream> file;
@@ -61,15 +63,16 @@ void print_map(Context<E> &ctx) {
Map<E> map = get_map(ctx);
// Print a mapfile.
*out << " VMA Size Align Out In Symbol\n";
*out << " VMA Size Align Out In Symbol\n";
for (OutputChunk<E> *osec : ctx.chunks) {
*out << std::setw(16) << (u64)osec->shdr.sh_addr
for (Chunk<E> *osec : ctx.chunks) {
*out << std::showbase
<< std::setw(18) << std::hex << (u64)osec->shdr.sh_addr << std::dec
<< std::setw(11) << (u64)osec->shdr.sh_size
<< std::setw(6) << (u64)osec->shdr.sh_addralign
<< " " << osec->name << "\n";
if (osec->kind != OutputChunk<E>::REGULAR)
if (!osec->is_output_section())
continue;
std::span<InputSection<E> *> members = ((OutputSection<E> *)osec)->members;
@@ -79,20 +82,23 @@ void print_map(Context<E> &ctx) {
InputSection<E> *mem = members[i];
std::ostringstream ss;
opt_demangle = ctx.arg.demangle;
u64 addr = osec->shdr.sh_addr + mem->offset;
ss << std::setw(16) << (osec->shdr.sh_addr + mem->offset)
<< std::setw(11) << (u64)mem->shdr.sh_size
<< std::setw(6) << (u64)mem->shdr.sh_addralign
ss << std::showbase
<< std::setw(18) << std::hex << addr << std::dec
<< std::setw(11) << (u64)mem->sh_size
<< std::setw(6) << (1 << (u64)mem->p2align)
<< " " << *mem << "\n";
typename Map<E>::const_accessor acc;
if (map.find(acc, mem))
for (Symbol<E> *sym : acc->second)
ss << std::setw(16) << sym->get_addr(ctx)
ss << std::showbase
<< std::setw(18) << std::hex << sym->get_addr(ctx) << std::dec
<< " 0 0 "
<< *sym << "\n";
bufs[i] = std::move(ss.str());
bufs[i] = ss.str();
});
for (std::string &str : bufs)
@@ -100,5 +106,13 @@ void print_map(Context<E> &ctx) {
}
}
template void print_map(Context<X86_64> &ctx);
template void print_map(Context<I386> &ctx);
#define INSTANTIATE(E) \
template void print_map(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+29 -20
View File
@@ -31,9 +31,9 @@ static void debug_print(char *fmt, ...) {
va_end(ap);
}
static void get_args(va_list ap, int argc, char **argv) {
static void get_args(va_list *ap, int argc, char **argv) {
for (int i = 1; i < argc - 1; i++) {
char *arg = va_arg(ap, char *);
char *arg = va_arg(*ap, char *);
if (!arg)
break;
argv[i] = arg;
@@ -41,9 +41,27 @@ static void get_args(va_list ap, int argc, char **argv) {
}
static bool is_ld(const char *path) {
return !strcmp(path, "/usr/bin/ld") ||
!strcmp(path, "/usr/bin/ld.lld") ||
!strcmp(path, "/usr/bin/ld.gold");
const char *ptr = path + strlen(path);
while (path < ptr && ptr[-1] != '/')
ptr--;
return !strcmp(ptr, "ld") || !strcmp(ptr, "ld.lld") ||
!strcmp(ptr, "ld.gold") || !strcmp(ptr, "ld.bfd");
}
int execvpe(const char *file, char *const *argv, char *const *envp) {
debug_print("execvpe %s\n", file);
if (!strcmp(file, "ld") || is_ld(file)) {
file = get_mold_path();
((const char **)argv)[0] = file;
}
for (int i = 0; envp[i]; i++)
putenv(envp[i]);
typeof(execvpe) *real = dlsym(RTLD_NEXT, "execvp");
return real(file, argv, environ);
}
int execve(const char *path, char *const *argv, char *const *envp) {
@@ -62,7 +80,8 @@ int execl(const char *path, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(ap, 4096, argv);
get_args(&ap, 4096, argv);
va_end(ap);
return execve(path, argv, environ);
}
@@ -70,7 +89,8 @@ int execlp(const char *file, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(ap, 4096, argv);
get_args(&ap, 4096, argv);
va_end(ap);
return execvpe(file, argv, environ);
}
@@ -78,8 +98,9 @@ int execle(const char *path, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(ap, 4096, argv);
get_args(&ap, 4096, argv);
char **env = va_arg(ap, char **);
va_end(ap);
return execve(path, argv, env);
}
@@ -91,18 +112,6 @@ int execvp(const char *file, char *const *argv) {
return execvpe(file, argv, environ);
}
int execvpe(const char *file, char *const *argv, char *const *envp) {
debug_print("execvpe %s\n", file);
if (!strcmp(file, "ld") || is_ld(file)) {
file = get_mold_path();
((const char **)argv)[0] = file;
}
typeof(execvpe) *real = dlsym(RTLD_NEXT, "execvpe");
return real(file, argv, environ);
}
int posix_spawn(pid_t *pid, const char *path,
const posix_spawn_file_actions_t *file_actions,
const posix_spawnattr_t *attrp,
+2652
View File
File diff suppressed because it is too large. Load diff
+2788
View File
File diff suppressed because it is too large. Load diff
+53 -40
View File
@@ -5,59 +5,69 @@
#include <sys/stat.h>
#include <sys/types.h>
static u32 get_umask() {
namespace mold::elf {
inline u32 get_umask() {
u32 orig_umask = umask(0);
umask(orig_umask);
return orig_umask;
}
template <typename C>
static std::pair<i64, char *>
open_or_create_file(C &ctx, std::string path, i64 filesize, i64 perm) {
std::string tmpl = filepath(path).parent_path() / ".mold-XXXXXX";
char *path2 = (char *)save_string(ctx, tmpl).data();
i64 fd = mkstemp(path2);
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
// Reuse an existing file if exists and writable.
if (rename(path.c_str(), path2) == 0) {
::close(fd);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd != -1 && !ftruncate(fd, filesize) && !fchmod(fd, perm & ~get_umask()))
return {fd, path2};
unlink(path2);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
}
if (ftruncate(fd, filesize))
Fatal(ctx) << "ftruncate failed: " << errno_string();
if (fchmod(fd, (perm & ~get_umask())) == -1)
Fatal(ctx) << "fchmod failed: " << errno_string();
return {fd, path2};
}
template <typename E>
class MemoryMappedOutputFile : public OutputFile<E> {
public:
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize,
i64 perm)
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, true) {
std::string dir(path_dirname(path));
this->tmpfile = (char *)save_string(ctx, dir + "/.mold-XXXXXX").data();
i64 fd = mkstemp(this->tmpfile);
if (fd == -1)
Fatal(ctx) << "cannot open " << this->tmpfile << ": " << strerror(errno);
if (rename(path.c_str(), this->tmpfile) == 0) {
::close(fd);
fd = ::open(this->tmpfile, O_RDWR | O_CREAT, perm);
if (fd == -1) {
if (errno != ETXTBSY)
Fatal(ctx) << "cannot open " << path << ": " << strerror(errno);
unlink(this->tmpfile);
fd = ::open(this->tmpfile, O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << path << ": " << strerror(errno);
}
}
if (ftruncate(fd, filesize))
Fatal(ctx) << "ftruncate failed";
if (fchmod(fd, (perm & ~get_umask())) == -1)
Fatal(ctx) << "fchmod failed";
i64 fd;
std::tie(fd, output_tmpfile) = open_or_create_file(ctx, path, filesize, perm);
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << strerror(errno);
Fatal(ctx) << path << ": mmap failed: " << errno_string();
::close(fd);
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
if (!ctx.buildid)
if (!this->is_unmapped)
munmap(this->buf, this->filesize);
if (rename(this->tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << strerror(errno);
this->tmpfile = nullptr;
if (rename(output_tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << errno_string();
output_tmpfile = nullptr;
}
};
@@ -69,7 +79,7 @@ public:
this->buf = (u8 *)mmap(NULL, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << "mmap failed: " << strerror(errno);
Fatal(ctx) << "mmap failed: " << errno_string();
}
void close(Context<E> &ctx) override {
@@ -83,7 +93,7 @@ public:
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << this->path << ": " << strerror(errno);
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
FILE *fp = fdopen(fd, "w");
fwrite(this->buf, this->filesize, 1, fp);
@@ -122,10 +132,13 @@ OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
return file;
}
template
std::unique_ptr<OutputFile<X86_64>>
OutputFile<X86_64>::open(Context<X86_64> &, std::string, i64, i64);
#define INSTANTIATE(E) \
template class OutputFile<E>;
template
std::unique_ptr<OutputFile<I386>>
OutputFile<I386>::open(Context<I386> &, std::string, i64, i64);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+1681
View File
File diff suppressed because it is too large. Load diff
+51 -42
View File
@@ -27,20 +27,23 @@
// symbol indices.
#include "mold.h"
#include "../archive-file.h"
#include <unordered_map>
#include <unordered_set>
namespace mold::elf {
template <typename E> class RObjectFile;
template <typename E>
class ROutputChunk {
class RChunk {
public:
ROutputChunk() {
RChunk() {
out_shdr.sh_addralign = 1;
}
virtual ~ROutputChunk() = default;
virtual ~RChunk() = default;
virtual void update_shdr(Context<E> &ctx) {}
virtual void write_to(Context<E> &ctx) = 0;
@@ -51,7 +54,7 @@ public:
};
template <typename E>
class RInputSection : public ROutputChunk<E> {
class RInputSection : public RChunk<E> {
public:
RInputSection(Context<E> &ctx, RObjectFile<E> &file, const ElfShdr<E> &shdr);
void update_shdr(Context<E> &ctx) override;
@@ -61,13 +64,13 @@ public:
};
template <typename E>
class RSymtabSection : public ROutputChunk<E> {
class RSymtabSection : public RChunk<E> {
public:
RSymtabSection() {
this->name = ".symtab";
this->out_shdr.sh_type = SHT_SYMTAB;
this->out_shdr.sh_entsize = sizeof(ElfSym<E>);
this->out_shdr.sh_addralign = E::wordsize;
this->out_shdr.sh_addralign = E::word_size;
}
void add_local_symbol(Context<E> &ctx, RObjectFile<E> &file, i64 idx);
@@ -80,7 +83,7 @@ public:
};
template <typename E>
class RStrtabSection : public ROutputChunk<E> {
class RStrtabSection : public RChunk<E> {
public:
RStrtabSection(std::string_view name) {
this->name = name;
@@ -95,7 +98,7 @@ public:
};
template <typename E>
class ROutputEhdr : public ROutputChunk<E> {
class ROutputEhdr : public RChunk<E> {
public:
ROutputEhdr() {
this->out_shdr.sh_size = sizeof(ElfEhdr<E>);
@@ -105,7 +108,7 @@ public:
};
template <typename E>
class ROutputShdr : public ROutputChunk<E> {
class ROutputShdr : public RChunk<E> {
public:
ROutputShdr() {
this->out_shdr.sh_size = sizeof(ElfShdr<E>);
@@ -118,7 +121,7 @@ public:
template <typename E>
class RObjectFile {
public:
RObjectFile(Context<E> &ctx, MemoryMappedFile<E> &mb, bool is_alive);
RObjectFile(Context<E> &ctx, MappedFile<Context<E>> &mf, bool is_alive);
void remove_comdats(Context<E> &ctx,
std::unordered_set<std::string_view> &groups);
@@ -126,7 +129,7 @@ public:
template <typename T>
std::span<T> get_data(Context<E> &ctx, const ElfShdr<E> &shdr);
MemoryMappedFile<E> &mb;
MappedFile<Context<E>> &mf;
std::span<ElfShdr<E>> elf_sections;
std::vector<std::unique_ptr<RInputSection<E>>> sections;
std::span<const ElfSym<E>> syms;
@@ -294,7 +297,7 @@ template <typename E>
void ROutputEhdr<E>::write_to(Context<E> &ctx) {
ElfEhdr<E> &hdr = *(ElfEhdr<E> *)(ctx.buf + this->out_shdr.sh_offset);
memcpy(&hdr.e_ident, "\177ELF", 4);
hdr.e_ident[EI_CLASS] = (E::wordsize == 8) ? ELFCLASS64 : ELFCLASS32;
hdr.e_ident[EI_CLASS] = (E::word_size == 8) ? ELFCLASS64 : ELFCLASS32;
hdr.e_ident[EI_DATA] = E::is_le ? ELFDATA2LSB : ELFDATA2MSB;
hdr.e_ident[EI_VERSION] = EV_CURRENT;
hdr.e_type = ET_REL;
@@ -310,7 +313,7 @@ void ROutputEhdr<E>::write_to(Context<E> &ctx) {
template <typename E>
void ROutputShdr<E>::update_shdr(Context<E> &ctx) {
for (ROutputChunk<E> *chunk : ctx.r_chunks)
for (RChunk<E> *chunk : ctx.r_chunks)
if (chunk->shndx)
this->out_shdr.sh_size += sizeof(ElfShdr<E>);
}
@@ -318,18 +321,18 @@ void ROutputShdr<E>::update_shdr(Context<E> &ctx) {
template <typename E>
void ROutputShdr<E>::write_to(Context<E> &ctx) {
ElfShdr<E> *hdr = (ElfShdr<E> *)(ctx.buf + this->out_shdr.sh_offset);
for (ROutputChunk<E> *chunk : ctx.r_chunks)
for (RChunk<E> *chunk : ctx.r_chunks)
if (chunk->shndx)
hdr[chunk->shndx] = chunk->out_shdr;
}
template <typename E>
RObjectFile<E>::RObjectFile(Context<E> &ctx, MemoryMappedFile<E> &mb,
RObjectFile<E>::RObjectFile(Context<E> &ctx, MappedFile<Context<E>> &mf,
bool is_alive)
: mb(mb), is_alive(is_alive) {
: mf(mf), is_alive(is_alive) {
// Read ELF header and section header
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)mb.data(ctx);
ElfShdr<E> *sh_begin = (ElfShdr<E> *)(mb.data(ctx) + ehdr.e_shoff);
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)mf.data;
ElfShdr<E> *sh_begin = (ElfShdr<E> *)(mf.data + ehdr.e_shoff);
i64 num_sections = (ehdr.e_shnum == 0) ? sh_begin->sh_size : ehdr.e_shnum;
elf_sections = {sh_begin, sh_begin + num_sections};
sections.resize(num_sections);
@@ -337,14 +340,14 @@ RObjectFile<E>::RObjectFile(Context<E> &ctx, MemoryMappedFile<E> &mb,
// Read .shstrtab
i64 shstrtab_idx = (ehdr.e_shstrndx == SHN_XINDEX)
? sh_begin->sh_link : ehdr.e_shstrndx;
shstrtab = (char *)(mb.data(ctx) + elf_sections[shstrtab_idx].sh_offset);
shstrtab = (char *)(mf.data + elf_sections[shstrtab_idx].sh_offset);
// Read .symtab
for (i64 i = 1; i < elf_sections.size(); i++) {
ElfShdr<E> &shdr = elf_sections[i];
if (shdr.sh_type == SHT_SYMTAB) {
syms = get_data<const ElfSym<E>>(ctx, shdr);
strtab = (char *)(mb.data(ctx) + elf_sections[shdr.sh_link].sh_offset);
strtab = (char *)(mf.data + elf_sections[shdr.sh_link].sh_offset);
symtab_shndx = i;
first_global = shdr.sh_info;
break;
@@ -403,8 +406,8 @@ void RObjectFile<E>::remove_comdats(Context<E> &ctx,
template <typename E>
template <typename T>
std::span<T> RObjectFile<E>::get_data(Context<E> &ctx, const ElfShdr<E> &shdr) {
T *begin = (T *)(mb.data(ctx) + shdr.sh_offset);
T *end = (T *)(mb.data(ctx) + shdr.sh_offset + shdr.sh_size);
T *begin = (T *)(mf.data + shdr.sh_offset);
T *end = (T *)(mf.data + shdr.sh_offset + shdr.sh_size);
return {begin, end};
}
@@ -430,26 +433,26 @@ open_files(Context<E> &ctx, std::span<std::string_view> args) {
read_arg(ctx, args, arg, "dynamic-list"))
continue;
MemoryMappedFile<E> *mb = nullptr;
MappedFile<Context<E>> *mf = nullptr;
if (read_arg(ctx, args, arg, "l")) {
mb = find_library(ctx, std::string(arg));
mf = find_library(ctx, std::string(arg));
} else {
if (arg.starts_with('-'))
continue;
arg = args[0];
args = args.subspan(1);
mb = MemoryMappedFile<E>::must_open(ctx, std::string(arg));
mf = MappedFile<Context<E>>::must_open(ctx, std::string(arg));
}
switch (get_file_type(ctx, mb)) {
case FileType::OBJ:
files.emplace_back(new RObjectFile<E>(ctx, *mb, true));
switch (get_file_type(mf)) {
case FileType::ELF_OBJ:
files.emplace_back(new RObjectFile<E>(ctx, *mf, true));
break;
case FileType::AR:
case FileType::THIN_AR:
for (MemoryMappedFile<E> *child : read_archive_members(ctx, mb))
if (get_file_type(ctx, child) == FileType::OBJ)
for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf))
if (get_file_type(child) == FileType::ELF_OBJ)
files.emplace_back(new RObjectFile<E>(ctx, *child, whole_archive));
break;
default:
@@ -462,7 +465,7 @@ open_files(Context<E> &ctx, std::span<std::string_view> args) {
template <typename E>
static i64 assign_offsets(Context<E> &ctx) {
i64 offset = 0;
for (ROutputChunk<E> *chunk : ctx.r_chunks) {
for (RChunk<E> *chunk : ctx.r_chunks) {
offset = align_to(offset, chunk->out_shdr.sh_addralign);
chunk->out_shdr.sh_offset = offset;
offset += chunk->out_shdr.sh_size;
@@ -508,11 +511,9 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
break;
}
files.erase(std::remove_if(files.begin(), files.end(),
[](std::unique_ptr<RObjectFile<E>> &file) {
return !file->is_alive;
}),
files.end());
std::erase_if(files, [](std::unique_ptr<RObjectFile<E>> &file) {
return !file->is_alive;
});
// Remove duplicate comdat groups
std::unordered_set<std::string_view> comdat_groups;
@@ -547,12 +548,12 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
// Assign output section indices
i64 shndx = 1;
for (ROutputChunk<E> *chunk : ctx.r_chunks)
for (RChunk<E> *chunk : ctx.r_chunks)
if (chunk != &ehdr && chunk != &shdr)
chunk->shndx = shndx++;
// Add section names to .shstrtab
for (ROutputChunk<E> *chunk : ctx.r_chunks)
for (RChunk<E> *chunk : ctx.r_chunks)
if (chunk->shndx)
chunk->out_shdr.sh_name = shstrtab.add_string(chunk->name);
@@ -568,7 +569,7 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
symtab.add_global_symbol(ctx, *file, i);
// Finalize section header
for (ROutputChunk<E> *chunk : ctx.r_chunks)
for (RChunk<E> *chunk : ctx.r_chunks)
chunk->update_shdr(ctx);
// Open an output file
@@ -579,10 +580,18 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
ctx.buf = out->buf;
// Write to the output file
for (ROutputChunk<E> *chunk : ctx.r_chunks)
for (RChunk<E> *chunk : ctx.r_chunks)
chunk->write_to(ctx);
out->close(ctx);
}
template void combine_objects(Context<X86_64> &, std::span<std::string_view>);
template void combine_objects(Context<I386> &, std::span<std::string_view>);
#define INSTANTIATE(E) \
template void combine_objects(Context<E> &, std::span<std::string_view>);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
+62 -42
View File
@@ -1,9 +1,12 @@
#include "mold.h"
#include "../sha.h"
#include <openssl/sha.h>
#include <sys/signal.h>
#include <filesystem>
#include <signal.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
@@ -11,6 +14,8 @@
#define DAEMON_TIMEOUT 30
namespace mold::elf {
// Exiting from a program with large memory usage is slow --
// it may take a few hundred milliseconds. To hide the latency,
// we fork a child and let it do the actual linking work.
@@ -48,7 +53,7 @@ std::function<void()> fork_child() {
// Child
close(pipefd[0]);
return [=]() {
return [=] {
char buf[] = {1};
int n = write(pipefd[1], buf, 1);
assert(n == 1);
@@ -118,7 +123,7 @@ static void send_fd(Context<E> &ctx, i64 conn, i64 fd) {
*(int *)CMSG_DATA(cmsg) = fd;
if (sendmsg(conn, &msg, 0) == -1)
Fatal(ctx) << "sendmsg failed: " << strerror(errno);
Fatal(ctx) << "sendmsg failed: " << errno_string();
}
template <typename E>
@@ -138,7 +143,7 @@ static i64 recv_fd(Context<E> &ctx, i64 conn) {
i64 len = recvmsg(conn, &msg, 0);
if (len <= 0)
Fatal(ctx) << "recvmsg failed: " << strerror(errno);
Fatal(ctx) << "recvmsg failed: " << errno_string();
struct cmsghdr *cmsg;
cmsg = CMSG_FIRSTHDR(&msg);
@@ -149,7 +154,7 @@ template <typename E>
void try_resume_daemon(Context<E> &ctx) {
i64 conn = socket(AF_UNIX, SOCK_STREAM, 0);
if (conn == -1)
Fatal(ctx) << "socket failed: " << strerror(errno);
Fatal(ctx) << "socket failed: " << errno_string();
std::string path = "/tmp/mold-" + compute_sha256(ctx.cmdline_args);
@@ -176,11 +181,11 @@ template <typename E>
void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
std::function<void()> *on_complete) {
if (daemon(1, 0) == -1)
Fatal(ctx) << "daemon failed: " << strerror(errno);
Fatal(ctx) << "daemon failed: " << errno_string();
i64 sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1)
Fatal(ctx) << "socket failed: " << strerror(errno);
Fatal(ctx) << "socket failed: " << errno_string();
socket_tmpfile =
strdup(("/tmp/mold-" + compute_sha256(ctx.cmdline_args)).c_str());
@@ -193,21 +198,21 @@ void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1) {
if (errno != EADDRINUSE)
Fatal(ctx) << "bind failed: " << strerror(errno);
Fatal(ctx) << "bind failed: " << errno_string();
unlink(socket_tmpfile);
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1)
Fatal(ctx) << "bind failed: " << strerror(errno);
Fatal(ctx) << "bind failed: " << errno_string();
}
umask(orig_mask);
if (listen(sock, 0) == -1)
Fatal(ctx) << "listen failed: " << strerror(errno);
Fatal(ctx) << "listen failed: " << errno_string();
static i64 conn = -1;
*wait_for_client = [=, &ctx]() {
*wait_for_client = [=, &ctx] {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
@@ -218,7 +223,7 @@ void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
i64 res = select(sock + 1, &rfds, NULL, NULL, &tv);
if (res == -1)
Fatal(ctx) << "select failed: " << strerror(errno);
Fatal(ctx) << "select failed: " << errno_string();
if (res == 0) {
std::cout << "timeout\n";
@@ -227,29 +232,46 @@ void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
conn = accept(sock, NULL, NULL);
if (conn == -1)
Fatal(ctx) << "accept failed: " << strerror(errno);
Fatal(ctx) << "accept failed: " << errno_string();
unlink(socket_tmpfile);
dup2(recv_fd(ctx, conn), STDOUT_FILENO);
dup2(recv_fd(ctx, conn), STDERR_FILENO);
};
*on_complete = [=]() {
*on_complete = [=] {
char buf[] = {1};
int n = write(conn, buf, 1);
assert(n == 1);
};
}
static std::string get_self_path() {
return std::filesystem::read_symlink("/proc/self/exe");
}
template <typename E>
static std::string get_self_path(Context<E> &ctx) {
char buf[4096];
size_t n = readlink("/proc/self/exe", buf, sizeof(buf));
if (n == -1)
Fatal(ctx) << "readlink(\"/proc/self/exe\") failed: " << strerror(errno);
if (n == sizeof(buf))
Fatal(ctx) << "readlink: path too long";
return {buf, n};
std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
// Look for mold-wrapper.so from the same directory as the executable is.
std::filesystem::path path = self.parent_path() / "mold-wrapper.so";
std::error_code ec;
if (std::filesystem::is_regular_file(path, ec) && !ec)
return path;
#ifdef LIBDIR
// If not found, search $(LIBDIR)/mold, which is /usr/local/lib/mold
// by default.
path = LIBDIR "/mold/mold-wrapper.so";
if (std::filesystem::is_regular_file(path, ec) && !ec)
return path;
#endif
// Look for ../lib/mold/mold-wrapper.so
path = self.parent_path() / "../lib/mold/mold-wrapper.so";
if (std::filesystem::is_regular_file(path, ec) && !ec)
return path;
Fatal(ctx) << "mold-wrapper.so is missing";
}
template <typename E>
@@ -261,34 +283,27 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
Fatal(ctx) << "-run: argument missing";
// Get the mold-wrapper.so path
std::string self = get_self_path(ctx);
std::string dso_path;
if (self == "/usr/bin/mold")
dso_path = "/usr/lib/mold/mold-wrapper.so";
else if (self == "/usr/local/bin/mold")
dso_path = "/usr/local/lib/mold/mold-wrapper.so";
else
dso_path = std::string(path_dirname(self)) + "/mold-wrapper.so";
struct stat st;
if (stat(dso_path.c_str(), &st) || (st.st_mode & S_IFMT) != S_IFREG)
Fatal(ctx) << dso_path << " is missing";
std::string self = get_self_path();
std::string dso_path = find_dso(ctx, self);
// Set environment variables
putenv(strdup(("LD_PRELOAD=" + dso_path).c_str()));
putenv(strdup(("MOLD_PATH=" + self).c_str()));
// If /usr/bin/ld{,lld,gold} is specified, run mold itself
if (std::string_view cmd = argv[2]; cmd == "ld" ||
cmd == "/usr/bin/ld" || cmd == "/usr/bin/ld.lld" ||
cmd == "/usr/bin/ld.gold") {
execv(self.c_str(), argv + 2);
Fatal(ctx) << "mold -run failed: " << self << ": " << strerror(errno);
// If ld, ld.lld or ld.gold is specified, run mold itself
if (std::string cmd = filepath(argv[2]).filename();
cmd == "ld" || cmd == "ld.lld" || cmd == "ld.gold") {
std::vector<char *> args;
args.push_back(argv[0]);
args.insert(args.end(), argv + 3, argv + argc);
args.push_back(nullptr);
execv(self.c_str(), args.data());
Fatal(ctx) << "mold -run failed: " << self << ": " << errno_string();
}
// Execute a given command
execvp(argv[2], argv + 2);
Fatal(ctx) << "mold -run failed: " << argv[2] << ": " << strerror(errno);
Fatal(ctx) << "mold -run failed: " << argv[2] << ": " << errno_string();
}
#define INSTANTIATE(E) \
@@ -299,3 +314,8 @@ 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
+166
View File
@@ -0,0 +1,166 @@
// This file implements the Aho-Corasick algorithm to match version
// script patterns to symbol strings as quickly as possible.
//
// Here are some examples of version script patterns:
//
// qt_private_api_tag*
// *16QAccessibleCache*
// *32QAbstractFileIconProviderPrivate*
// *17QPixmapIconEngine*
//
// The pattern is a glob pattern, so `*` is a wildcard that matches
// any substring. We sometimes have hundreds of version script
// patterns and have to match them against millions of symbol strings.
//
// Aho-Corasick cannot handle complex patterns such as `*foo*bar*`.
// We handle such patterns with GlobPattern. GlobPattern is relatively
// slow, but complex patterns are rare in practice, so it should be OK.
#include "mold.h"
#include <queue>
#include <regex>
namespace mold::elf {
std::optional<u16> VersionMatcher::find(std::string_view str) {
u32 idx = UINT32_MAX;
if (root) {
std::call_once(once_flag, [&] { compile(); });
// Match against simple glob patterns
TrieNode *node = root.get();
auto walk = [&](u8 c) {
for (;;) {
if (node->children[c]) {
node = node->children[c].get();
idx = std::min(idx, node->value);
return;
}
if (!node->suffix_link)
return;
node = node->suffix_link;
}
};
walk('\0');
for (u8 c : str)
walk(c);
walk('\0');
}
// Match against complex glob patterns
for (std::pair<GlobPattern, u32> &glob : globs)
if (glob.first.match(str))
idx = std::min(idx, glob.second);
if (idx == UINT32_MAX)
return {};
return versions[idx];
}
static bool is_simple_pattern(std::string_view pat) {
static std::regex re(R"(\*?[^*[?]+\*?)", std::regex_constants::optimize);
return std::regex_match(pat.begin(), pat.end(), re);
}
static std::string handle_stars(std::string_view pat) {
std::string str(pat);
// Convert "foo" -> "\0foo\0", "*foo" -> "foo\0", "foo*" -> "\0foo"
// and "*foo*" -> "foo". Aho-Corasick can do only substring matching,
// so we use \0 as beginning/end-of-string markers.
if (str.starts_with('*') && str.ends_with('*'))
return str.substr(1, str.size() - 2);
if (str.starts_with('*'))
return str.substr(1) + "\0"s;
if (str.ends_with('*'))
return "\0"s + str.substr(0, str.size() - 1);
return "\0"s + str + "\0"s;
}
bool VersionMatcher::add(std::string_view pat, u16 ver) {
assert(!compiled);
assert(!pat.empty());
u32 idx = strings.size();
strings.push_back(std::string(pat));
versions.push_back(ver);
// Complex glob pattern
if (!is_simple_pattern(pat)) {
if (std::optional<GlobPattern> glob = GlobPattern::compile(pat)) {
globs.push_back({std::move(*glob), idx});
return true;
}
return false;
}
// Simple glob pattern
if (!root)
root.reset(new TrieNode);
TrieNode *node = root.get();
for (u8 c : handle_stars(pat)) {
if (!node->children[c])
node->children[c].reset(new TrieNode);
node = node->children[c].get();
}
node->value = std::min(node->value, idx);
return true;
}
void VersionMatcher::compile() {
fix_suffix_links(*root);
fix_values();
compiled = true;
}
void VersionMatcher::fix_suffix_links(TrieNode &node) {
for (i64 i = 0; i < 256; i++) {
if (!node.children[i])
continue;
TrieNode &child = *node.children[i];
TrieNode *cur = node.suffix_link;
for (;;) {
if (!cur) {
child.suffix_link = root.get();
break;
}
if (cur->children[i]) {
child.suffix_link = cur->children[i].get();
break;
}
cur = cur->suffix_link;
}
fix_suffix_links(child);
}
}
void VersionMatcher::fix_values() {
std::queue<TrieNode *> queue;
queue.push(root.get());
do {
TrieNode *node = queue.front();
queue.pop();
for (std::unique_ptr<TrieNode> &child : node->children) {
if (!child)
continue;
child->value = std::min(child->value, child->suffix_link->value);
queue.push(child.get());
}
} while (!queue.empty());
}
} // namespace mold::elf
+16 -94
View File
@@ -1,105 +1,27 @@
#include "mold.h"
std::string get_current_dir() {
std::string buf(8192, '\0');
getcwd(buf.data(), buf.size());
buf.resize(buf.find_first_of('\0'));
return buf;
}
#include <filesystem>
#include <sys/stat.h>
// Returns the directory part of a given path.
// Returns '.' if path doesn't contain '/'.
std::string_view path_dirname(std::string_view path) {
i64 pos = path.find_last_of('/');
if (pos == path.npos)
return ".";
return path.substr(0, pos);
}
namespace mold {
// Returns the filename part of a given path.
// Returns '/' if path reperesents the root directory.
// Returns '.' if path is empty.
std::string_view path_filename(std::string_view path) {
if (path.empty())
return ".";
while (path.ends_with('/'))
path = path.substr(0, path.size() - 2);
if (path.empty())
return "/";
i64 pos = path.find_last_of('/');
if (pos == path.npos)
return path;
return path.substr(pos + 1);
}
// Returns the filename part of a given path without the file
// extension.
std::string_view path_basename(std::string_view path) {
std::string_view name = path_filename(path);
i64 pos = name.find_last_of('.');
if (pos == name.npos)
return name;
return name.substr(0, pos);
}
std::string path_to_absolute(std::string_view path) {
if (path.starts_with('/'))
return std::string(path);
return get_current_dir() + "/" + std::string(path);
std::string get_realpath(std::string_view path) {
std::error_code ec;
std::string ret = std::filesystem::canonical(path, ec);
return ec ? std::string(path) : ret;
}
// Removes redundant '/..' or '/.' from a given path.
// The transformation is done purely by lexical processing.
// This function does not access file system.
std::string path_clean(std::string_view path) {
std::vector<std::string_view> components;
bool is_rooted = path.starts_with('/');
while (!path.empty()) {
size_t pos = path.find('/');
std::string_view elem;
if (pos == path.npos) {
elem = path;
path = "";
} else {
elem = path.substr(0, pos);
path = path.substr(pos + 1);
}
if (elem.empty() || elem == ".")
continue;
if (elem == "..") {
if (components.empty()) {
if (is_rooted)
continue;
components.push_back("..");
continue;
}
if (components.back() == "..")
components.push_back("..");
else
components.pop_back();
continue;
}
components.push_back(elem);
}
std::string ret;
if (is_rooted)
ret = "/";
for (i64 i = 0, end = components.size(); i < end; i++) {
ret += components[i];
if (i != end - 1)
ret += "/";
}
return ret;
return filepath(path).lexically_normal();
}
std::filesystem::path to_abs_path(std::filesystem::path path) {
if (path.is_absolute())
return path;
return (std::filesystem::current_path() / path).lexically_normal();
}
} // namespace mold
+148
View File
@@ -0,0 +1,148 @@
#pragma once
#include "mold.h"
#include "elf/elf.h"
namespace mold {
enum class FileType {
UNKNOWN,
EMPTY,
ELF_OBJ,
ELF_DSO,
MACH_OBJ,
MACH_DYLIB,
MACH_UNIVERSAL,
AR,
THIN_AR,
TAPI,
TEXT,
GCC_LTO_OBJ,
LLVM_BITCODE,
};
template <typename C>
bool is_text_file(MappedFile<C> *mf) {
u8 *data = mf->data;
return mf->size >= 4 && isprint(data[0]) && isprint(data[1]) &&
isprint(data[2]) && isprint(data[3]);
}
template <typename E, typename C>
inline bool is_gcc_lto_obj(MappedFile<C> *mf) {
using namespace mold::elf;
const char *data = mf->get_contents().data();
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)data;
std::span<ElfShdr<E>> shdrs{(ElfShdr<E> *)(data + ehdr.e_shoff), ehdr.e_shnum};
for (ElfShdr<E> &sec : shdrs) {
if (sec.sh_type != SHT_SYMTAB)
continue;
std::span<ElfSym<E>> elf_syms{(ElfSym<E> *)(data + sec.sh_offset),
sec.sh_size / sizeof(ElfSym<E>)};
auto skip = [](u8 type) {
return type == STT_NOTYPE || type == STT_FILE || type == STT_SECTION;
};
// GCC LTO object contains only sections symbols followed by a common
// symbol whose name is `__gnu_lto_v1` or `__gnu_lto_slim`.
i64 i = 1;
while (i < elf_syms.size() && skip(elf_syms[i].st_type))
i++;
if (i < elf_syms.size() && elf_syms[i].st_shndx == SHN_COMMON) {
std::string_view name =
data + shdrs[sec.sh_link].sh_offset + elf_syms[i].st_name;
if (name.starts_with("__gnu_lto_"))
return true;
}
break;
}
return false;
}
template <typename C>
FileType get_file_type(MappedFile<C> *mf) {
std::string_view data = mf->get_contents();
if (data.empty())
return FileType::EMPTY;
if (data.starts_with("\177ELF")) {
switch (*(u16 *)(data.data() + 16)) {
case 1: {
// ET_REL
elf::Elf32Ehdr &ehdr = *(elf::Elf32Ehdr *)data.data();
if (ehdr.e_ident[elf::EI_CLASS] == elf::ELFCLASS32) {
if (is_gcc_lto_obj<elf::I386>(mf))
return FileType::GCC_LTO_OBJ;
} else {
if (is_gcc_lto_obj<elf::X86_64>(mf))
return FileType::GCC_LTO_OBJ;
}
return FileType::ELF_OBJ;
}
case 3: // ET_DYN
return FileType::ELF_DSO;
}
return FileType::UNKNOWN;
}
if (data.starts_with("\xcf\xfa\xed\xfe")) {
switch (*(u32 *)(data.data() + 12)) {
case 1: // MH_OBJECT
return FileType::MACH_OBJ;
case 6: // MH_DYLIB
return FileType::MACH_DYLIB;
}
return FileType::UNKNOWN;
}
if (data.starts_with("!<arch>\n"))
return FileType::AR;
if (data.starts_with("!<thin>\n"))
return FileType::THIN_AR;
if (data.starts_with("--- !tapi-tbd"))
return FileType::TAPI;
if (data.starts_with("\xca\xfe\xba\xbe"))
return FileType::MACH_UNIVERSAL;
if (is_text_file(mf))
return FileType::TEXT;
if (data.starts_with("\xde\xc0\x17\x0b"))
return FileType::LLVM_BITCODE;
if (data.starts_with("BC\xc0\xde"))
return FileType::LLVM_BITCODE;
return FileType::UNKNOWN;
}
inline std::string filetype_to_string(FileType type) {
switch (type) {
case FileType::UNKNOWN: return "UNKNOWN";
case FileType::EMPTY: return "EMPTY";
case FileType::ELF_OBJ: return "ELF_OBJ";
case FileType::ELF_DSO: return "ELF_DSO";
case FileType::MACH_OBJ: return "MACH_OBJ";
case FileType::MACH_DYLIB: return "MACH_DYLIB";
case FileType::MACH_UNIVERSAL: return "MACH_UNIVERSAL";
case FileType::AR: return "AR";
case FileType::THIN_AR: return "THIN_AR";
case FileType::TAPI: return "TAPI";
case FileType::TEXT: return "TEXT";
case FileType::GCC_LTO_OBJ: return "GCC_LTO_OBJ";
case FileType::LLVM_BITCODE: return "LLVM_BITCODE";
}
return "UNKNOWN";
}
inline std::ostream &operator<<(std::ostream &out, FileType type) {
out << filetype_to_string(type);
return out;
}
} // namespace mold
+20
View File
@@ -0,0 +1,20 @@
// This file implements HyperLogLog algorithm, which estimates
// the number of unique items in a given multiset.
//
// For more info, read
// https://engineering.fb.com/2018/12/13/data-infrastructure/hyperloglog
#include "mold.h"
#include <cmath>
namespace mold {
i64 HyperLogLog::get_cardinality() const {
double z = 0;
for (i64 val : buckets)
z += pow(2, -val);
return ALPHA * NBUCKETS * NBUCKETS / z;
}
} // namespace mold
-154
View File
@@ -1,154 +0,0 @@
#include "mold.h"
#include <limits>
template <typename E>
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
const ElfShdr<E> &shdr, std::string_view name,
std::string_view contents, i64 section_idx)
: file(file), shdr(shdr), nameptr(name.data()), namelen(name.size()),
contents(contents), section_idx(section_idx) {
// As a special case, we want to map .ctors and .dtors to
// .init_array and .fini_array, respectively. However, old CRT
// object files are not compatible with this translation, so we need
// to keep them as-is if a section came from crtbegin.o or crtend.o.
//
// Yeah, this is an ugly hack, but the fundamental problem is that
// we have two different mechanism, ctors/dtors and init_array/fini_array
// for the same purpose. The latter was introduced to replace the
// former, but as it is often the case, the former still lingers
// around, so we need to keep this code to conver the old mechanism
// to the new one.
std::string_view stem = path_filename(file.filename);
if (stem != "crtbegin.o" && stem != "crtend.o" &&
stem != "crtbeginS.o" && stem != "crtendS.o") {
if (name == ".ctors" || name.starts_with(".ctors."))
name = ".init_array";
else if (name == ".dtors" || name.starts_with(".dtors."))
name = ".fini_array";
}
output_section =
OutputSection<E>::get_instance(ctx, name, shdr.sh_type, shdr.sh_flags);
}
template <typename E>
void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
if (shdr.sh_type == SHT_NOBITS || shdr.sh_size == 0)
return;
// Copy data
memcpy(buf, contents.data(), contents.size());
// Apply relocations
if (shdr.sh_flags & SHF_ALLOC)
apply_reloc_alloc(ctx, buf);
else
apply_reloc_nonalloc(ctx, buf);
// As a special case, .ctors and .dtors section contents are
// reversed. These sections are now obsolete and mapped to
// .init_array and .fini_array, but they have to be reversed to
// maintain the original semantics.
bool init_fini = output_section->name == ".init_array" ||
output_section->name == ".fini_array";
bool ctors_dtors = name().starts_with(".ctors") ||
name().starts_with(".dtors");
if (init_fini && ctors_dtors)
std::reverse((typename E::WordTy *)buf,
(typename E::WordTy *)(buf + shdr.sh_size));
}
template <typename E>
static i64 get_output_type(Context<E> &ctx) {
if (ctx.arg.shared)
return 0;
if (ctx.arg.pie)
return 1;
return 2;
}
template <typename E>
static i64 get_sym_type(Context<E> &ctx, Symbol<E> &sym) {
if (sym.is_absolute(ctx))
return 0;
if (!sym.is_imported)
return 1;
if (sym.get_type() != STT_FUNC)
return 2;
return 3;
}
template <typename E>
void InputSection<E>::dispatch(Context<E> &ctx, Action table[3][4],
u16 rel_type, i64 i) {
std::span<ElfRel<E>> rels = get_rels(ctx);
const ElfRel<E> &rel = rels[i];
Symbol<E> &sym = *file.symbols[rel.r_sym];
bool is_writable = (shdr.sh_flags & SHF_WRITE);
Action action = table[get_output_type(ctx)][get_sym_type(ctx, sym)];
switch (action) {
case NONE:
rel_types[i] = rel_type;
return;
case ERROR:
break;
case COPYREL:
if (!ctx.arg.z_copyreloc && !sym.esym().is_undef_weak())
break;
if (sym.esym().st_visibility == STV_PROTECTED)
Error(ctx) << *this << ": cannot make copy relocation for "
<< " protected symbol '" << sym << "', defined in "
<< *sym.file;
sym.flags |= NEEDS_COPYREL;
rel_types[i] = rel_type;
return;
case PLT:
sym.flags |= NEEDS_PLT;
rel_types[i] = rel_type;
return;
case DYNREL:
if (!is_writable) {
if (ctx.arg.z_text)
break;
ctx.has_textrel = true;
}
sym.flags |= NEEDS_DYNSYM;
rel_types[i] = R_DYN;
file.num_dynrel++;
return;
case BASEREL:
if (!is_writable) {
if (ctx.arg.z_text)
break;
ctx.has_textrel = true;
}
rel_types[i] = R_BASEREL;
file.num_dynrel++;
return;
default:
unreachable(ctx);
}
Error(ctx) << *this << ": " << rel_to_string<E>(rel.r_type)
<< " relocation against symbol `" << sym
<< "' can not be used; recompile with -fPIE";
}
template <typename E>
void InputSection<E>::report_undef(Context<E> &ctx, Symbol<E> &sym) {
switch (ctx.arg.unresolved_symbols) {
case UnresolvedKind::ERROR:
Error(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UnresolvedKind::WARN:
Warn(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UnresolvedKind::IGNORE:
break;
}
}
template class InputSection<X86_64>;
template class InputSection<I386>;
+150
View File
@@ -0,0 +1,150 @@
#pragma once
namespace mold {
enum PluginStatus {
LDPS_OK,
LDPS_NO_SYMS,
LDPS_BAD_HANDLE,
LDPS_ERR,
};
enum PluginTag {
LDPT_NULL,
LDPT_API_VERSION,
LDPT_GOLD_VERSION,
LDPT_LINKER_OUTPUT,
LDPT_OPTION,
LDPT_REGISTER_CLAIM_FILE_HOOK,
LDPT_REGISTER_ALL_SYMBOLS_READ_HOOK,
LDPT_REGISTER_CLEANUP_HOOK,
LDPT_ADD_SYMBOLS,
LDPT_GET_SYMBOLS,
LDPT_ADD_INPUT_FILE,
LDPT_MESSAGE,
LDPT_GET_INPUT_FILE,
LDPT_RELEASE_INPUT_FILE,
LDPT_ADD_INPUT_LIBRARY,
LDPT_OUTPUT_NAME,
LDPT_SET_EXTRA_LIBRARY_PATH,
LDPT_GNU_LD_VERSION,
LDPT_GET_VIEW,
LDPT_GET_INPUT_SECTION_COUNT,
LDPT_GET_INPUT_SECTION_TYPE,
LDPT_GET_INPUT_SECTION_NAME,
LDPT_GET_INPUT_SECTION_CONTENTS,
LDPT_UPDATE_SECTION_ORDER,
LDPT_ALLOW_SECTION_ORDERING,
LDPT_GET_SYMBOLS_V2,
LDPT_ALLOW_UNIQUE_SEGMENT_FOR_SECTIONS,
LDPT_UNIQUE_SEGMENT_FOR_SECTIONS,
LDPT_GET_SYMBOLS_V3,
LDPT_GET_INPUT_SECTION_ALIGNMENT,
LDPT_GET_INPUT_SECTION_SIZE,
LDPT_REGISTER_NEW_INPUT_HOOK,
LDPT_GET_WRAP_SYMBOLS,
LDPT_ADD_SYMBOLS_V2,
};
enum PluginApiVersion {
LD_PLUGIN_API_VERSION = 1,
};
struct PluginTagValue {
PluginTagValue(PluginTag tag, int val) : tag(tag), val(val) {}
template <typename T>
PluginTagValue(PluginTag tag, T *ptr) : tag(tag), ptr((void *)ptr) {}
PluginTag tag;
union {
int val;
void *ptr;
};
};
enum PluginOutputFileType {
LDPO_REL,
LDPO_EXEC,
LDPO_DYN,
LDPO_PIE,
};
struct PluginInputFile {
const char *name;
int fd;
off_t offset;
off_t filesize;
void *handle;
};
struct PluginSection {
const void *handle;
int shndx;
};
struct PluginSymbol {
char *name;
char *version;
char def;
char symbol_type;
char section_kind;
int visibility;
uint64_t size;
char *comdat_key;
int resolution;
};
enum PluginSymbolKind {
LDPK_DEF,
LDPK_WEAKDEF,
LDPK_UNDEF,
LDPK_WEAKUNDEF,
LDPK_COMMON,
};
enum PluginSymbolVisibility {
LDPV_DEFAULT,
LDPV_PROTECTED,
LDPV_INTERNAL,
LDPV_HIDDEN,
};
enum PluginSymbolType {
LDST_UNKNOWN,
LDST_FUNCTION,
LDST_VARIABLE,
};
enum PluginSymbolSectionKind {
LDSSK_DEFAULT,
LDSSK_BSS,
};
enum PluginSymbolResolution {
LDPR_UNKNOWN,
LDPR_UNDEF,
LDPR_PREVAILING_DEF,
LDPR_PREVAILING_DEF_IRONLY,
LDPR_PREEMPTED_REG,
LDPR_PREEMPTED_IR,
LDPR_RESOLVED_IR,
LDPR_RESOLVED_EXEC,
LDPR_RESOLVED_DYN,
LDPR_PREVAILING_DEF_IRONLY_EXP,
};
enum PluginLevel {
LDPL_INFO,
LDPL_WARNING,
LDPL_ERROR,
LDPL_FATAL,
};
typedef PluginStatus OnloadFn(PluginTagValue *tv);
typedef PluginStatus ClaimFileHandler(const PluginInputFile *, int *);
typedef PluginStatus AllSymbolsReadHandler();
typedef PluginStatus CleanupHandler();
typedef PluginStatus NewInputHandler(const PluginInputFile *);
} // namespace mold
+258
View File
@@ -0,0 +1,258 @@
#include "mold.h"
namespace mold::macho {
// Returns [hi:lo] bits of val.
static u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
}
static u64 page(u64 val) {
return val & 0xffff'ffff'ffff'f000;
}
static u64 encode_page(u64 val) {
return (bits(val, 13, 12) << 29) | (bits(val, 32, 14) << 5);
}
template <>
void StubsSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->hdr.offset);
for (i64 i = 0; i < syms.size(); i++) {
static const u32 insn[] = {
0x90000010, // adrp x16, $ptr@PAGE
0xf9400210, // ldr x16, [x16, $ptr@PAGEOFF]
0xd61f0200, // br x16
};
static_assert(sizeof(insn) == ARM64::stub_size);
u64 la_addr = ctx.lazy_symbol_ptr.hdr.addr + ARM64::word_size * i;
u64 this_addr = this->hdr.addr + ARM64::stub_size * i;
memcpy(buf, insn, sizeof(insn));
buf[0] |= encode_page(page(la_addr) - page(this_addr));
buf[1] |= bits(la_addr, 11, 3) << 10;
buf += 3;
}
}
template <>
void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *start = (u32 *)(ctx.buf + this->hdr.offset);
u32 *buf = start;
static const u32 insn0[] = {
0x90000011, // adrp x17, $__dyld_private@PAGE
0x91000231, // add x17, x17, $__dyld_private@PAGEOFF
0xa9bf47f0, // stp x16, x17, [sp, #-16]!
0x90000010, // adrp x16, $dyld_stub_binder@PAGE
0xf9400210, // ldr x16, [x16, $dyld_stub_binder@PAGEOFF]
0xd61f0200, // br x16
};
static_assert(sizeof(insn0) == ARM64::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
u64 dyld_private = get_symbol(ctx, "__dyld_private")->get_addr(ctx);
buf[0] |= encode_page(page(dyld_private) - page(this->hdr.addr));
buf[1] |= bits(dyld_private, 11, 0) << 10;
u64 stub_binder = get_symbol(ctx, "dyld_stub_binder")->get_addr(ctx);
buf[3] |= encode_page(page(stub_binder) - page(this->hdr.addr - 12));
buf[4] |= bits(stub_binder, 11, 0) << 10;
buf += 6;
for (i64 i = 0; i < ctx.stubs.syms.size(); i++) {
static const u32 insn[] = {
0x18000050, // ldr w16, addr
0x14000000, // b stubHelperHeader
0x00000000, // addr: .long <idx>
};
static_assert(sizeof(insn) == ARM64::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
buf[1] |= bits((start - buf - 1) * 4, 27, 2);
buf[2] = ctx.stubs.bind_offsets[i];
buf += 3;
}
}
static i64 read_addend(u8 *buf, const MachRel &r) {
if (r.p2size == 2)
return *(i32 *)(buf + r.offset);
if (r.p2size == 3)
return *(i64 *)(buf + r.offset);
unreachable();
}
static Relocation<ARM64>
read_reloc(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
const MachSection &hdr, MachRel *rels, i64 &idx) {
i64 addend = 0;
switch (rels[idx].type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
addend = read_addend((u8 *)file.mf->data + hdr.offset, rels[idx]);
break;
case ARM64_RELOC_ADDEND:
addend = rels[idx++].offset;
break;
}
MachRel &r = rels[idx];
Relocation<ARM64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
return rel;
}
u32 addr;
if (r.is_pcrel)
addr = hdr.addr + r.offset + addend;
else
addr = addend;
Subsection<ARM64> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
return rel;
}
template <>
std::vector<Relocation<ARM64>>
read_relocations(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
const MachSection &hdr) {
std::vector<Relocation<ARM64>> vec;
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++)
vec.push_back(read_reloc(ctx, file, hdr, rels, i));
return vec;
}
template <>
void Subsection<ARM64>::scan_relocations(Context<ARM64> &ctx) {
for (Relocation<ARM64> &r : get_rels()) {
Symbol<ARM64> *sym = r.sym;
if (!sym)
continue;
switch (r.type) {
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
sym->flags |= NEEDS_GOT;
break;
case ARM64_RELOC_TLVP_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
sym->flags |= NEEDS_THREAD_PTR;
break;
}
if (sym->file && sym->file->is_dylib()) {
sym->flags |= NEEDS_STUB;
((DylibFile<ARM64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<ARM64>::apply_reloc(Context<ARM64> &ctx, u8 *buf) {
std::span<Relocation<ARM64>> rels = get_rels();
for (i64 i = 0; i < rels.size(); i++) {
Relocation<ARM64> &r = rels[i];
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_BRANCH26:
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_PAGEOFF12:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case ARM64_RELOC_SUBTRACTOR: {
Relocation<ARM64> s = rels[++i];
assert(s.type == ARM64_RELOC_UNSIGNED);
u64 val1 = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
u64 val2 = s.sym ? s.sym->get_addr(ctx) : s.subsec->get_addr(ctx);
val = val2 - val1;
break;
}
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
val = r.sym->get_got_addr(ctx);
break;
case ARM64_RELOC_TLVP_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
val = r.sym->get_tlv_addr(ctx);
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
case ARM64_RELOC_POINTER_TO_GOT:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
if (r.p2size == 2)
*(i32 *)(buf + r.offset) = val;
else if (r.p2size == 3)
*(i64 *)(buf + r.offset) = val;
else
unreachable();
break;
case ARM64_RELOC_BRANCH26:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
*(u32 *)(buf + r.offset) |= bits(val, 27, 2);
break;
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGE21:
assert(r.is_pcrel);
*(u32 *)(buf + r.offset) |=
encode_page(page(val) - page(get_addr(ctx) + r.offset));
break;
case ARM64_RELOC_PAGEOFF12:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: {
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
u32 insn = *(u32 *)(buf + r.offset);
i64 scale = 0;
if ((insn & 0x3b000000) == 0x39000000)
scale = insn >> 30;
*(u32 *)(buf + r.offset) |= bits(val, 11, scale) << 10;
break;
}
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
}
}
} // namespace mold::macho
+222
View File
@@ -0,0 +1,222 @@
#include "mold.h"
namespace mold::macho {
template <>
void StubsSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
u8 *buf = ctx.buf + this->hdr.offset;
for (i64 i = 0; i < syms.size(); i++) {
// `ff 25 xx xx xx xx` is a RIP-relative indirect jump instruction,
// i.e., `jmp *IMM(%rip)`. It loads an address from la_symbol_ptr
// and jump there.
static_assert(X86_64::stub_size == 6);
buf[i * 6] = 0xff;
buf[i * 6 + 1] = 0x25;
*(u32 *)(buf + i * 6 + 2) =
ctx.lazy_symbol_ptr.hdr.addr + i * X86_64::word_size -
(this->hdr.addr + i * 6 + 6);
}
}
template <>
void StubHelperSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
u8 *start = ctx.buf + this->hdr.offset;
u8 *buf = start;
static const u8 insn0[] = {
0x4c, 0x8d, 0x1d, 0, 0, 0, 0, // lea $__dyld_private(%rip), %r11
0x41, 0x53, // push %r11
0xff, 0x25, 0, 0, 0, 0, // jmp *$dyld_stub_binder@GOT(%rip)
0x90, // nop
};
static_assert(sizeof(insn0) == X86_64::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
*(u32 *)(buf + 3) =
get_symbol(ctx, "__dyld_private")->get_addr(ctx) - this->hdr.addr - 7;
*(u32 *)(buf + 11) =
get_symbol(ctx, "dyld_stub_binder")->get_got_addr(ctx) - this->hdr.addr - 15;
buf += 16;
for (i64 i = 0; i < ctx.stubs.syms.size(); i++) {
u8 insn[] = {
0x68, 0, 0, 0, 0, // push $bind_offset
0xe9, 0, 0, 0, 0, // jmp $__stub_helper
};
static_assert(sizeof(insn) == X86_64::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
*(u32 *)(buf + 1) = ctx.stubs.bind_offsets[i];
*(u32 *)(buf + 6) = start - buf - 10;
buf += 10;
}
}
static i64 get_reloc_addend(u32 type) {
switch (type) {
case X86_64_RELOC_SIGNED_1:
return 1;
case X86_64_RELOC_SIGNED_2:
return 2;
case X86_64_RELOC_SIGNED_4:
return 4;
default:
return 0;
}
}
static i64 read_addend(u8 *buf, const MachRel &r) {
if (r.p2size == 2)
return *(i32 *)(buf + r.offset) + get_reloc_addend(r.type);
if (r.p2size == 3)
return *(i64 *)(buf + r.offset) + get_reloc_addend(r.type);
unreachable();
}
static Relocation<X86_64>
read_reloc(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
const MachSection &hdr, MachRel &r) {
if (r.p2size != 2 && r.p2size != 3)
Fatal(ctx) << file << ": invalid r.p2size: " << (u32)r.p2size;
if (r.is_pcrel) {
if (r.p2size != 2)
Fatal(ctx) << file << ": invalid PC-relative reloc: " << r.offset;
} else {
if (r.p2size != 3)
Fatal(ctx) << file << ": invalid non-PC-relative reloc: " << r.offset;
}
u8 *buf = (u8 *)file.mf->data + hdr.offset;
Relocation<X86_64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
i64 addend = read_addend(buf, r);
if (r.is_extern) {
rel.sym = file.syms[r.idx];
rel.addend = addend;
return rel;
}
u32 addr;
if (r.is_pcrel)
addr = hdr.addr + r.offset + 4 + addend;
else
addr = addend;
Subsection<X86_64> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
rel.subsec = target;
rel.addend = addr - target->input_addr;
return rel;
}
template <>
std::vector<Relocation<X86_64>>
read_relocations(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
const MachSection &hdr) {
std::vector<Relocation<X86_64>> vec;
vec.reserve(hdr.nreloc);
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++)
vec.push_back(read_reloc(ctx, file, hdr, rels[i]));
return vec;
}
template <>
void Subsection<X86_64>::scan_relocations(Context<X86_64> &ctx) {
for (Relocation<X86_64> &r : get_rels()) {
Symbol<X86_64> *sym = r.sym;
if (!sym)
continue;
switch (r.type) {
case X86_64_RELOC_GOT_LOAD:
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_GOT;
break;
case X86_64_RELOC_GOT:
sym->flags |= NEEDS_GOT;
break;
case X86_64_RELOC_TLV:
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_THREAD_PTR;
break;
}
if (sym->file && sym->file->is_dylib()) {
sym->flags |= NEEDS_STUB;
((DylibFile<X86_64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<X86_64>::apply_reloc(Context<X86_64> &ctx, u8 *buf) {
for (const Relocation<X86_64> &r : get_rels()) {
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
switch (r.type) {
case X86_64_RELOC_UNSIGNED:
case X86_64_RELOC_SIGNED:
case X86_64_RELOC_BRANCH:
case X86_64_RELOC_SIGNED_1:
case X86_64_RELOC_SIGNED_2:
case X86_64_RELOC_SIGNED_4:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case X86_64_RELOC_GOT_LOAD:
if (r.sym->got_idx != -1) {
val = r.sym->get_got_addr(ctx);
} else {
// Relax MOVQ into LEAQ
if (buf[r.offset - 2] != 0x8b)
Error(ctx) << isec << ": invalid GOT_LOAD relocation";
buf[r.offset - 2] = 0x8d;
val = r.sym->get_addr(ctx);
}
break;
case X86_64_RELOC_GOT:
val = r.sym->get_got_addr(ctx);
break;
case X86_64_RELOC_TLV:
if (r.sym->tlv_idx != -1) {
val = r.sym->get_tlv_addr(ctx);
} else {
// Relax MOVQ into LEAQ
if (buf[r.offset - 2] != 0x8b)
Error(ctx) << isec << ": invalid TLV relocation";
buf[r.offset - 2] = 0x8d;
val = r.sym->get_addr(ctx);
}
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset + 4 + get_reloc_addend(r.type);
if (r.p2size == 2)
*(u32 *)(buf + r.offset) = val;
else if (r.p2size == 3)
*(u64 *)(buf + r.offset) = val;
else
unreachable();
}
}
} // namespace mold::macho
+322
View File
@@ -0,0 +1,322 @@
#include "mold.h"
#include "../cmdline.h"
#include <optional>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <regex>
#include <unistd.h>
#include <unordered_set>
namespace mold::macho {
static const char helpmsg[] = R"(
Options:
-F<PATH> Add DIR to framework search path
-L<PATH> Add DIR to library search path
-Z Do not search the standard directories when
searching for libraries and frameworks
-ObjC Load all static archive members that implement
an Objective-C class or category
-adhoc_codesign Add ad-hoc code signature to the output file
-no_adhoc_codesign
-arch <ARCH_NAME> Specify target architecture
-bundle Produce a mach-o bundle
-dead_strip Remove unreachable functions and data
-dead_strip_dylibs Remove unreachable dylibs from dependencies
-demangle Demangle C++ symbols in log messages (default)
-dylib Produce a dynamic library
-dynamic Link against dylibs (default)
-e <SYMBOL> Specify the entry point of a main executable
-execute Produce an executable (default)
-filelist <FILE>[,<DIR>] Specify the list of input file names
-framework <NAME>,[,<SUFFIX>]
Search for a given framework
-headerpad <SIZE> Allocate the size of padding after load commands
-headerpad_max_install_names
Allocate MAXPATHLEN byte padding after load commands
-help Report usage information
-l<LIB> Search for a given library
-lto_library <FILE> Ignored
-map <FILE> Write map file to a given file
-needed-l<LIB> Search for a given library
-needed-framework <NAME>[,<SUFFIX>]
Search for a given framework
-no_deduplicate Ignored
-o <FILE> Set output filename
-pagezero_size <SIZE> Specify the size of the __PAGEZERO segment
-platform_version <PLATFORM> <MIN_VERSION> <SDK_VERSION>
Set platform, platform version and SDK version
-rpath <PATH> Add PATH to the runpath search path list
-syslibroot <DIR> Prepend DIR to library search paths
-t Print out each file the linker loads
-v Report version information)";
template <typename E>
static i64 parse_platform(Context<E> &ctx, std::string_view arg) {
static std::regex re(R"(\d+)", std::regex_constants::ECMAScript);
if (std::regex_match(arg.begin(), arg.end(), re))
return stoi(std::string(arg));
if (arg == "macos")
return PLATFORM_MACOS;
if (arg == "ios")
return PLATFORM_IOS;
if (arg == "tvos")
return PLATFORM_TVOS;
if (arg == "watchos")
return PLATFORM_WATCHOS;
if (arg == "bridgeos")
return PLATFORM_BRIDGEOS;
if (arg == "mac-catalyst")
return PLATFORM_MACCATALYST;
if (arg == "ios-simulator")
return PLATFORM_IOSSIMULATOR;
if (arg == "tvos-simulator")
return PLATFORM_TVOSSIMULATOR;
if (arg == "watchos-simulator")
return PLATFORM_WATCHOSSIMULATOR;
if (arg == "driverkit")
return PLATFORM_DRIVERKIT;
Fatal(ctx) << "unknown -platform_version name: " << arg;
}
template <typename E>
static i64 parse_version(Context<E> &ctx, std::string_view arg) {
static std::regex re(R"((\d+)(?:\.(\d+))?(?:\.(\d+))?)",
std::regex_constants::ECMAScript);
std::cmatch m;
if (!std::regex_match(arg.begin(), arg.end(), m, re))
Fatal(ctx) << "malformed version number: " << arg;
i64 major = (m[1].length() == 0) ? 0 : stoi(m[1]);
i64 minor = (m[2].length() == 0) ? 0 : stoi(m[2]);
i64 patch = (m[3].length() == 0) ? 0 : stoi(m[3]);
return (major << 16) | (minor << 8) | patch;
}
static bool is_directory(std::filesystem::path path) {
std::error_code ec;
return std::filesystem::is_directory(path, ec) && !ec;
}
template <typename E>
void parse_nonpositional_args(Context<E> &ctx,
std::vector<std::string> &remaining) {
std::vector<std::string_view> &args = ctx.cmdline_args;
i64 i = 1;
std::vector<std::string> framework_paths;
std::vector<std::string> library_paths;
bool nostdlib = false;
std::optional<i64> pagezero_size;
while (i < args.size()) {
std::string_view arg;
std::string_view arg2;
std::string_view arg3;
auto read_arg = [&](std::string name) {
if (args[i] == name) {
if (args.size() <= i + 1)
Fatal(ctx) << "option -" << name << ": argument missing";
arg = args[i + 1];
i += 2;
return true;
}
return false;
};
auto read_arg3 = [&](std::string name) {
if (args[i] == name) {
if (args.size() <= i + 3)
Fatal(ctx) << "option -" << name << ": argument missing";
arg = args[i + 1];
arg2 = args[i + 2];
arg3 = args[i + 3];
i += 4;
return true;
}
return false;
};
auto read_joined = [&](std::string name) {
if (read_arg(name))
return true;
if (args[i].starts_with(name)) {
arg = args[i].substr(name.size());
i++;
return true;
}
return false;
};
auto read_flag = [&](std::string name) {
if (args[i] == name) {
i++;
return true;
}
return false;
};
if (args[i].starts_with('@')) {
std::vector<std::string_view> vec =
read_response_file(ctx, args[i].substr(1));
args.erase(args.begin() + i);
args.insert(args.begin() + i, vec.begin(), vec.end());
continue;
}
if (read_flag("-help") || read_flag("--help")) {
SyncOut(ctx) << "Usage: " << ctx.cmdline_args[i]
<< " [options] file...\n" << helpmsg;
exit(0);
}
if (read_joined("-F")) {
framework_paths.push_back(std::string(arg));
} else if (read_joined("-L")) {
library_paths.push_back(std::string(arg));
} else if (read_flag("-Z")) {
nostdlib = true;
} else if (read_flag("-ObjC")) {
ctx.arg.ObjC = true;
} else if (read_flag("-adhoc_codesign")) {
ctx.arg.adhoc_codesign = true;
} else if (read_flag("-no_adhoc_codesign")) {
ctx.arg.adhoc_codesign = false;
} else if (read_arg("-arch")) {
if (arg == "x86_64")
ctx.arg.arch = CPU_TYPE_X86_64;
else if (arg == "arm64")
ctx.arg.arch = CPU_TYPE_ARM64;
else
Fatal(ctx) << "unknown -arch: " << arg;
} else if (read_flag("-bundle")) {
ctx.output_type = MH_BUNDLE;
} else if (read_flag("-color-diagnostics") ||
read_flag("--color-diagnostics")) {
} else if (read_flag("-dead_strip")) {
ctx.arg.dead_strip = true;
} else if (read_flag("-dead_strip_dylibs")) {
ctx.arg.dead_strip_dylibs = true;
} else if (read_flag("-demangle")) {
ctx.arg.demangle = true;
} else if (read_flag("-dylib")) {
ctx.output_type = MH_DYLIB;
} else if (read_arg("-headerpad")) {
size_t pos;
ctx.arg.headerpad = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed -headerpad: " << arg;
} else if (read_flag("-headerpad_max_install_names")) {
ctx.arg.headerpad = 1024;
} else if (read_flag("-dynamic")) {
ctx.arg.dynamic = true;
} else if (read_arg("-e")) {
ctx.arg.entry = arg;
} else if (read_flag("-execute")) {
ctx.output_type = MH_EXECUTE;
} else if (read_arg("-fatal_warnings")) {
} else if (read_arg("-filelist")) {
remaining.push_back("-filelist");
remaining.push_back(std::string(arg));
} else if (read_arg("-framework")) {
remaining.push_back("-framework");
remaining.push_back(std::string(arg));
} else if (read_arg("-lto_library")) {
} else if (read_joined("-l")) {
remaining.push_back("-l");
remaining.push_back(std::string(arg));
} else if (read_arg("-map")) {
ctx.arg.map = arg;
} else if (read_joined("-needed-l")) {
remaining.push_back("-needed-l");
remaining.push_back(std::string(arg));
} else if (read_arg("-needed_framework")) {
remaining.push_back("-needed_framework");
remaining.push_back(std::string(arg));
} else if (read_flag("-no_deduplicate")) {
} else if (read_arg("-o")) {
ctx.arg.output = arg;
} else if (read_arg("-pagezero_size")) {
size_t pos;
pagezero_size = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed -pagezero_size: " << arg;
} else if (read_arg3("-platform_version")) {
ctx.arg.platform = parse_platform(ctx, arg);
ctx.arg.platform_min_version = parse_version(ctx, arg2);
ctx.arg.platform_sdk_version = parse_version(ctx, arg3);
} else if (read_arg("-rpath")) {
ctx.arg.rpath.push_back(std::string(arg));
} else if (read_flag("-search_dylibs_first")) {
Fatal(ctx) << "-search_dylibs_first is not supported";
} else if (read_flag("-search_paths_first")) {
} else if (read_arg("-syslibroot")) {
ctx.arg.syslibroot.push_back(std::string(arg));
} else if (read_flag("-t")) {
ctx.arg.trace = true;
} else if (read_flag("-v")) {
SyncOut(ctx) << mold_version;
} else {
if (args[i][0] == '-')
Fatal(ctx) << "unknown command line option: " << args[i];
remaining.push_back(std::string(args[i]));
i++;
}
}
auto add_search_path = [&](std::vector<std::string> &vec, std::string path) {
if (!path.starts_with('/') || ctx.arg.syslibroot.empty()) {
if (is_directory(path))
vec.push_back(path);
return;
}
bool found = false;
for (std::string &dir : ctx.arg.syslibroot) {
std::string str = path_clean(dir + "/" + path);
if (is_directory(str)) {
vec.push_back(str);
found = true;
}
}
if (!found && is_directory(path))
vec.push_back(path);
};
for (std::string &path : library_paths)
add_search_path(ctx.arg.library_paths, path);
if (!nostdlib) {
add_search_path(ctx.arg.library_paths, "/usr/lib");
add_search_path(ctx.arg.library_paths, "/usr/local/lib");
}
for (std::string &path : framework_paths)
add_search_path(ctx.arg.framework_paths, path);
if (!nostdlib) {
add_search_path(ctx.arg.framework_paths, "/Library/Frameworks");
add_search_path(ctx.arg.framework_paths, "/System/Library/Frameworks");
}
if (pagezero_size.has_value()) {
if (ctx.output_type != MH_EXECUTE)
Error(ctx) << "-pagezero_size option can only be used when"
<< " linking a main executable";
ctx.arg.pagezero_size = *pagezero_size;
} else {
ctx.arg.pagezero_size = (ctx.output_type == MH_EXECUTE) ? 0x100000000 : 0;
}
}
#define INSTANTIATE(E) \
template void parse_nonpositional_args(Context<E> &, std::vector<std::string> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+112
View File
@@ -0,0 +1,112 @@
#include "mold.h"
namespace mold::macho {
template <typename E>
static std::vector<Subsection<E> *> collect_root_set(Context<E> &ctx) {
std::vector<Subsection<E> *> rootset;
auto mark = [&](Symbol<E> *sym) {
if (sym && sym->subsec)
rootset.push_back(sym->subsec);
};
mark(get_symbol(ctx, ctx.arg.entry));
if (ctx.output_type == MH_DYLIB || ctx.output_type == MH_BUNDLE)
for (ObjectFile<E> *file : ctx.objs)
for (Symbol<E> *sym : file->syms)
if (sym->file == file && sym->is_extern)
mark(sym);
return rootset;
}
template <typename E>
static void visit(Context<E> &ctx, Subsection<E> &subsec) {
if (subsec.is_alive.exchange(true))
return;
for (Relocation<E> &rel : subsec.get_rels()) {
if (rel.sym) {
if (rel.sym->subsec)
visit(ctx, *rel.sym->subsec);
} else {
visit(ctx, *rel.subsec);
}
}
for (UnwindRecord<E> &rec : subsec.get_unwind_records()) {
rec.is_alive = true;
visit(ctx, *rec.subsec);
if (rec.lsda)
visit(ctx, *rec.lsda);
if (Symbol<E> *sym = rec.personality; sym && sym->subsec)
visit(ctx, *sym->subsec);
}
}
template <typename E>
static bool refers_live_subsection(Subsection<E> &subsec) {
for (Relocation<E> &rel : subsec.get_rels()) {
if (rel.sym) {
if (!rel.sym->subsec || rel.sym->subsec->is_alive)
return true;
} else {
if (rel.subsec->is_alive)
return true;
}
}
return false;
}
template <typename E>
static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
for (Subsection<E> *subsec : rootset)
visit(ctx, *subsec);
bool repeat;
do {
repeat = false;
for (ObjectFile<E> *file : ctx.objs) {
for (std::unique_ptr<Subsection<E>> &subsec : file->subsections) {
if ((subsec->isec.hdr.attr & S_ATTR_LIVE_SUPPORT) &&
!subsec->is_alive &&
refers_live_subsection(*subsec)) {
visit(ctx, *subsec);
repeat = true;
}
}
}
} while (repeat);
}
template <typename E>
static void sweep(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs) {
std::erase_if(file->subsections,
[](const std::unique_ptr<Subsection<E>> &subsec) {
return !subsec->is_alive;
});
}
for (ObjectFile<E> *file : ctx.objs)
for (Symbol<E> *&sym : file->syms)
if (sym->file == file && sym->subsec && !sym->subsec->is_alive)
sym = nullptr;
}
template <typename E>
void dead_strip(Context<E> &ctx) {
std::vector<Subsection<E> *> rootset = collect_root_set(ctx);
mark(ctx, rootset);
sweep(ctx);
}
#define INSTANTIATE(E) \
template void dead_strip(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+511
View File
@@ -0,0 +1,511 @@
#include "mold.h"
#include <cstdlib>
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
static u8 *open_file(std::string path) {
i64 fd = ::open(path.c_str(), O_RDONLY);
if (fd == -1)
return nullptr;
struct stat st;
if (fstat(fd, &st) == -1) {
std::cerr << path << ": fstat failed: " << errno_string();
exit(1);
}
void *ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
std::cerr << path << ": mmap failed: " << errno_string();
exit(1);
}
close(fd);
return (u8 *)ptr;
}
static void print_bytes(u8 *buf, i64 size) {
if (size == 0) {
std::cout << "[]\n";
return;
}
std::cout << "[" << std::setw(2) << std::setfill('0') << (u32)buf[0];
for (i64 i = 1; i < size; i++)
std::cout << " " << std::setw(2) << std::setfill('0') << (u32)buf[i];
std::cout << "]\n";
}
struct ExportEntry {
std::string name;
u32 flags;
u64 addr;
};
static void read_trie(std::vector<ExportEntry> &vec, u8 *start, i64 offset = 0,
const std::string &prefix = "") {
u8 *buf = start + offset;
if (read_uleb(buf)) {
ExportEntry ent;
ent.name = prefix;
ent.flags = read_uleb(buf);
ent.addr = read_uleb(buf);
vec.push_back(std::move(ent));
}
for (i64 i = 0, n = read_uleb(buf); i < n; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(vec, start, off, prefix + suffix);
}
}
void dump_unwind_info(u8 *buf, MachSection &sec) {
UnwindSectionHeader &hdr = *(UnwindSectionHeader *)(buf + sec.offset);
std::cout << std::hex << " Unwind info:"
<< "\n version: 0x" << hdr.version
<< "\n encoding_offset: 0x" << hdr.encoding_offset
<< "\n encoding_count: 0x" << hdr.encoding_count
<< "\n personality_offset: 0x" << hdr.personality_offset
<< "\n personality_count: 0x" << hdr.personality_count
<< "\n page_offset: 0x" << hdr.page_offset
<< "\n page_count: 0x" << hdr.page_count;
std::cout << "\n encoding:";
u32 *enc = (u32 *)(buf + sec.offset + hdr.encoding_offset);
for (i64 i = 0; i < hdr.encoding_count; i++)
std::cout << std::hex << "\n 0x" << enc[i];
std::cout << "\n personality:";
u32 *pers = (u32 *)(buf + sec.offset + hdr.personality_offset);
for (i64 i = 0; i < hdr.personality_count; i++)
std::cout << std::hex << "\n 0x" << pers[i];
UnwindFirstLevelPage *ent =
(UnwindFirstLevelPage *)(buf + sec.offset + hdr.page_offset);
for (i64 i = 0; i < hdr.page_count; i++) {
std::cout << std::hex << "\n function:"
<< "\n func_addr: 0x" << ent[i].func_addr
<< "\n page_offset: 0x" << ent[i].page_offset
<< "\n lsda_offset: 0x" << ent[i].lsda_offset;
if (i != hdr.page_count - 1) {
UnwindLsdaEntry *lsda =
(UnwindLsdaEntry *)(buf + sec.offset + ent[i].lsda_offset);
i64 lsda_size = ent[i + 1].lsda_offset - ent[i].lsda_offset;
for (i64 j = 0; j < lsda_size / sizeof(UnwindLsdaEntry); j++)
std::cout << std::hex
<< "\n lsda:"
<< "\n func_addr: 0x" << lsda[j].func_addr
<< "\n lsda_addr: 0x" << lsda[j].lsda_addr;
}
if (ent[i].page_offset == 0)
break;
u8 *addr = buf + sec.offset + ent[i].page_offset;
switch (u32 kind = *addr; kind) {
case UNWIND_SECOND_LEVEL_REGULAR: {
std::cout << "\n UNWIND_SECOND_LEVEL_REGULAR:" ;
break;
}
case UNWIND_SECOND_LEVEL_COMPRESSED: {
std::cout << "\n UNWIND_SECOND_LEVEL_COMPRESSED" ;
UnwindSecondLevelPage &hdr2 = *(UnwindSecondLevelPage *)addr;
std::cout << std::hex
<< "\n page_offset: 0x" << hdr2.page_offset
<< "\n page_count: 0x" << hdr2.page_count
<< "\n encoding_offset: 0x" << hdr2.encoding_offset
<< "\n encoding_count: 0x" << hdr2.encoding_count;
UnwindPageEntry *ent2 = (UnwindPageEntry *)(addr + hdr2.page_offset);
for (i64 j = 0; j < hdr2.page_count; j++)
std::cout << std::hex << "\n ent 0x"
<< (ent[i].func_addr + ent2[j].func_addr)
<< " 0x" << ent2[j].encoding;
u32 *enc = (u32 *)(addr + hdr2.encoding_offset);
for (i64 j = 0; j < hdr2.encoding_count; j++)
std::cout << std::hex << "\n 0x" << enc[j];
break;
}
default:
std::cout << "\n bad 2nd-level unwind info header: " << kind;
}
}
std::cout << "\n";
}
void dump_compact_unwind(u8 *buf, MachSection &sec) {
CompactUnwindEntry *ent = (CompactUnwindEntry *)(buf + sec.offset);
i64 nentry = sec.size / sizeof(CompactUnwindEntry);
std::cout << " Compact unwind:"
<< "\n num_entry: " << nentry;
for (i64 i = 0; i < nentry; i++) {
std::cout << std::hex
<< "\n entry: 0x" << (i * sizeof(CompactUnwindEntry))
<< "\n code_start: 0x" << ent[i].code_start
<< "\n code_len: 0x" << ent[i].code_len
<< "\n encoding: 0x" << ent[i].encoding
<< "\n personality: 0x" << ent[i].personality
<< "\n lsda: 0x" << ent[i].lsda;
}
std::cout << "\n";
}
void dump_file(std::string path) {
u8 *buf = open_file(path);
if (!buf) {
std::cerr << "cannot open " << path << "\n";
exit(1);
}
MachHeader &hdr = *(MachHeader *)buf;
std::cout << "magic: 0x" << std::hex << hdr.magic
<< "\ncputype: 0x" << hdr.cputype
<< "\ncpusubtype: 0x" << hdr.cpusubtype
<< "\nfiletype: 0x" << hdr.filetype
<< "\nncmds: 0x" << hdr.ncmds
<< "\nsizeofcmds: 0x" << hdr.sizeofcmds
<< "\nflags: 0x" << hdr.flags
<< "\n\n";
u8 *p = buf + sizeof(MachHeader);
for (i64 i = 0; i < hdr.ncmds; i++) {
std::cout << "fileoff: 0x" << std::hex << (p - buf) << "\n";
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
auto print_dylib = [&] {
DylibCommand &cmd = *(DylibCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
<< "\n timestamp: 0x" << cmd.timestamp
<< "\n current_version: 0x" << cmd.current_version
<< "\n compatibility_version: 0x" << cmd.compatibility_version
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
<< "\n";
};
switch (lc.cmd) {
case LC_SYMTAB: {
std::cout << "LC_SYMTAB\n";
SymtabCommand &cmd = *(SymtabCommand *)&lc;
std::cout << " cmdsize: " << cmd.cmdsize
<< "\n symoff: 0x" << std::hex << cmd.symoff
<< "\n nsyms: " << std::dec << cmd.nsyms
<< "\n stroff: 0x" << std::hex << cmd.stroff
<< "\n strsize: 0x" << cmd.strsize
<< "\n symdata: ";
print_bytes(buf + cmd.symoff, cmd.nsyms * sizeof(MachSym));
std::cout << " strdata: ";
print_bytes(buf + cmd.stroff, cmd.strsize);
MachSym *syms = (MachSym *)(buf + cmd.symoff);
for (i64 j = 0; j < cmd.nsyms; j++) {
std::cout << " symbol:"
<< "\n name: " << (char *)(buf + cmd.stroff + syms[j].stroff)
<< "\n stub: " << (u32)syms[j].stub
<< "\n pext: " << (u32)syms[j].pext
<< "\n type: " << (u32)syms[j].type
<< "\n ext: " << (u32)syms[j].ext
<< "\n sect: 0x" << (u32)syms[j].sect
<< "\n desc: 0x" << (u32)syms[j].desc
<< "\n value: 0x" << syms[j].value
<< "\n";
}
break;
}
case LC_DYSYMTAB: {
std::cout << "LC_DYSYMTAB\n";
DysymtabCommand &cmd = *(DysymtabCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n ilocalsym: 0x" << cmd.ilocalsym
<< "\n nlocalsym: 0x" << cmd.nlocalsym
<< "\n iextdefsym: 0x" << cmd.iextdefsym
<< "\n nextdefsym: 0x" << cmd.nextdefsym
<< "\n iundefsym: 0x" << cmd.iundefsym
<< "\n nundefsym: 0x" << cmd.nundefsym
<< "\n tocoff: 0x" << cmd.tocoff
<< "\n ntoc: 0x" << cmd.ntoc
<< "\n modtaboff: 0x" << cmd.modtaboff
<< "\n nmodtab: 0x" << cmd.nmodtab
<< "\n extrefsymoff: 0x" << cmd.extrefsymoff
<< "\n nextrefsyms: 0x" << cmd.nextrefsyms
<< "\n indirectsymoff: 0x" << cmd.indirectsymoff
<< "\n nindirectsyms: 0x" << cmd.nindirectsyms
<< "\n extreloff: 0x" << cmd.extreloff
<< "\n nextrel: 0x" << cmd.nextrel
<< "\n locreloff: 0x" << cmd.locreloff
<< "\n nlocrel: 0x" << cmd.nlocrel
<< "\n";
if (cmd.indirectsymoff) {
std::cout << " indirectsymdata: ";
print_bytes(buf + cmd.indirectsymoff, 4 * cmd.nindirectsyms);
}
break;
}
case LC_LOAD_DYLIB:
std::cout << "LC_LOAD_DYLIB\n";
print_dylib();
break;
case LC_LOAD_WEAK_DYLIB:
std::cout << "LC_LOAD_WEAK_DYLIB\n";
print_dylib();
break;
case LC_ID_DYLIB:
std::cout << "LC_ID_DYLIB\n";
print_dylib();
break;
case LC_LOAD_DYLINKER: {
std::cout << "LC_LOAD_DYLINKER\n";
DylinkerCommand &cmd = *(DylinkerCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
<< "\n";
break;
}
case LC_SEGMENT_64: {
std::cout << "LC_SEGMENT_64\n";
SegmentCommand &cmd = *(SegmentCommand *)&lc;
std::cout << " cmdsize: " << cmd.cmdsize
<< "\n segname: " << cmd.get_segname()
<< "\n vmaddr: 0x" << std::hex << cmd.vmaddr
<< "\n vmsize: 0x" << cmd.vmsize
<< "\n fileoff: 0x" << cmd.fileoff
<< "\n filesize: 0x" << cmd.filesize
<< "\n maxprot: " << std::dec << cmd.maxprot
<< "\n initprot: " << cmd.initprot
<< "\n nsects: " << cmd.nsects
<< "\n flags: 0x" << std::hex << cmd.flags
<< "\n";
MachSection *sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
for (i64 j = 0; j < cmd.nsects; j++) {
std::cout << " section:\n sectname: " << sec[j].get_sectname()
<< "\n segname: " << sec[j].get_segname()
<< "\n addr: 0x" << std::hex << sec[j].addr
<< "\n size: 0x" << sec[j].size
<< "\n offset: 0x" << sec[j].offset
<< "\n p2align: " << std::dec << sec[j].p2align
<< "\n reloff: " << std::hex << sec[j].reloff
<< "\n nreloc: " << std::dec << sec[j].nreloc
<< "\n type: 0x" << std::hex << sec[j].type
<< "\n attr: 0x" << std::hex << sec[j].attr
<< "\n";
if (sec[j].type != S_ZEROFILL) {
std::cout << " contents: ";
print_bytes(buf + sec[j].offset, sec[j].size);
}
if (sec[j].reloff) {
MachRel *rel = (MachRel *)(buf + sec[j].reloff);
for (i64 k = 0; k < sec[j].nreloc; k++) {
std::cout << " reloc: "
<< "\n offset: 0x" << rel[k].offset
<< "\n idx: 0x" << rel[k].idx
<< "\n is_pcrel: " << rel[k].is_pcrel
<< "\n p2size: 0x" << rel[k].p2size
<< "\n is_extern: " << rel[k].is_extern
<< "\n type: " << rel[k].type
<< "\n";
}
}
if (sec[j].match("__TEXT", "__unwind_info"))
dump_unwind_info(buf, sec[j]);
if (sec[j].match("__LD", "__compact_unwind"))
dump_compact_unwind(buf, sec[j]);
}
break;
}
case LC_UUID: {
std::cout << "LC_UUID\n";
UUIDCommand &cmd = *(UUIDCommand *)&lc;
std::cout << " data: ";
print_bytes(cmd.uuid, sizeof(cmd.uuid));
break;
}
case LC_DYLD_INFO_ONLY: {
std::cout << "LC_DYLD_INFO_ONLY\n";
DyldInfoCommand &cmd = *(DyldInfoCommand *)&lc;
if (cmd.rebase_off) {
std::cout << " rebase: ";
print_bytes(buf + cmd.rebase_off, cmd.rebase_size);
std::cout << " rebase_off: 0x" << std::hex << cmd.rebase_off
<< "\n rebase_size: 0x" << cmd.rebase_size
<< "\n";
}
if (cmd.bind_off) {
std::cout << " bind: ";
print_bytes(buf + cmd.bind_off, cmd.bind_size);
std::cout << " bind_off: 0x" << std::hex << cmd.bind_off
<< "\n bind_size: 0x" << cmd.bind_size
<< "\n";
}
if (cmd.weak_bind_off) {
std::cout << " weak_bind: ";
print_bytes(buf + cmd.weak_bind_off, cmd.weak_bind_size);
std::cout << " weak_bind_off: 0x" << std::hex << cmd.weak_bind_off
<< "\n weak_bind_size: 0x" << cmd.weak_bind_size
<< "\n";
}
if (cmd.lazy_bind_off) {
std::cout << " lazy_bind: ";
print_bytes(buf + cmd.lazy_bind_off, cmd.lazy_bind_size);
std::cout << " lazy_bind_off: 0x" << std::hex << cmd.lazy_bind_off
<< "\n lazy_bind_size: 0x" << cmd.lazy_bind_size
<< "\n";
}
if (cmd.export_off) {
std::cout << " export: ";
print_bytes(buf + cmd.export_off, cmd.export_size);
std::cout << " export_off: 0x" << std::hex << cmd.export_off
<< "\n export_size: 0x" << cmd.export_size
<< "\n";
std::vector<ExportEntry> vec;
read_trie(vec, buf + cmd.export_off);
for (ExportEntry &ent : vec)
std::cout << " export_sym: " << ent.name << " 0x" << ent.addr << "\n";
}
break;
}
case LC_FUNCTION_STARTS: {
std::cout << "LC_FUNCTION_STARTS\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
std::cout << " dataoff: 0x" << cmd.dataoff
<< "\n datasize: 0x" << cmd.datasize
<< "\n data:";
u8 *p = buf + cmd.dataoff;
u64 addr = 0;
for (;;) {
u64 delta = read_uleb(p);
if (!delta)
break;
addr += delta;
std::cout << std::hex << " 0x" << addr;
}
std::cout << "\n";
break;
}
case LC_MAIN: {
std::cout << "LC_MAIN\n";
EntryPointCommand &cmd = *(EntryPointCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n entryoff: 0x" << cmd.entryoff
<< "\n stacksize: 0x" << cmd.stacksize
<< "\n";
break;
}
case LC_DATA_IN_CODE: {
std::cout << "LC_DATA_IN_CODE\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
std::cout << " dataoff: 0x" << cmd.dataoff
<< "\n datasize: 0x" << cmd.datasize
<< "\n";
break;
}
case LC_SOURCE_VERSION: {
std::cout << "LC_SOURCE_VERSION\n";
SourceVersionCommand &cmd = *(SourceVersionCommand *)&lc;
std::cout << " version: 0x" << cmd.version
<< "\n";
break;
}
break;
case LC_BUILD_VERSION: {
std::cout << "LC_BUILD_VERSION\n";
BuildVersionCommand &cmd = *(BuildVersionCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n platform: 0x" << cmd.platform
<< "\n minos: 0x" << cmd.minos
<< "\n sdk: 0x" << cmd.sdk
<< "\n ntools: 0x" << cmd.ntools
<< "\n";
BuildToolVersion *tools =
(BuildToolVersion *)((u8 *)&lc + sizeof(BuildVersionCommand));
for (i64 i = 0; i < cmd.ntools; i++)
std::cout << " tool: 0x" << tools[i].tool
<< "\n version: 0x" << tools[i].version
<< "\n";
break;
}
case LC_VERSION_MIN_MACOSX: {
std::cout << "LC_VERSION_MIN_MACOSX\n";
VersionMinCommand &cmd = *(VersionMinCommand *)&lc;
std::cout << " version: " << (int)cmd.version
<< "\n sdk: " << (int)cmd.sdk
<< "\n";
break;
}
case LC_CODE_SIGNATURE: {
std::cout << "LC_CODE_SIGNATURE\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
CodeSignatureHeader &sig = *(CodeSignatureHeader *)(buf + cmd.dataoff);
std::cout << " magic: " << sig.magic
<< "\n length: " << sig.length
<< "\n count: " << sig.count
<< "\n";
for (i64 i = 0; i < sig.count; i++) {
CodeSignatureBlobIndex &idx = ((CodeSignatureBlobIndex *)(&sig + 1))[i];
std::cout << " idx type: " << idx.type
<< "\n idx offset: " << idx.offset
<< "\n";
CodeSignatureDirectory &dir =
*(CodeSignatureDirectory *)((u8 *)&sig + idx.offset);
std::cout << std::hex
<< " magic: 0x" << dir.magic
<< "\n version: 0x" << dir.version
<< "\n flags: 0x" << dir.flags
<< "\n hash_offset: 0x" << dir.hash_offset
<< "\n n_code_slots: 0x" << dir.n_code_slots
<< "\n hash_size: 0x" << (u32)dir.hash_size
<< "\n hash_type: 0x" << (u32)dir.hash_type
<< "\n page_size: 0x" << (1 << dir.page_size)
<< "\n";
}
break;
}
default:
std::cout << "UNKNOWN (0x" << std::hex << lc.cmd << ")\n";
break;
}
}
}
} // namespace mold::macho
+60
View File
@@ -0,0 +1,60 @@
#include "mold.h"
namespace mold::macho {
template <typename E>
InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
const MachSection &hdr)
: file(file), hdr(hdr),
osec(*OutputSection<E>::get_instance(ctx, hdr.get_segname(),
hdr.get_sectname())) {
if (hdr.type != S_ZEROFILL)
contents = file.mf->get_contents().substr(hdr.offset, hdr.size);
}
template <typename E>
void InputSection<E>::parse_relocations(Context<E> &ctx) {
rels.reserve(hdr.nreloc);
// Parse mach-o relocations to fill `rels` vector
rels = read_relocations(ctx, file, hdr);
// Sort `rels` vector
sort(rels, [](const Relocation<E> &a, const Relocation<E> &b) {
return a.offset < b.offset;
});
// Find subsections for this section
auto begin = std::lower_bound(
file.subsections.begin(), file.subsections.end(), hdr.addr,
[](std::unique_ptr<Subsection<E>> &subsec, u32 addr) {
return subsec->input_addr < addr;
});
auto end = std::lower_bound(
begin, file.subsections.end(), hdr.addr + hdr.size,
[](std::unique_ptr<Subsection<E>> &subsec, u32 addr) {
return subsec->input_addr < addr;
});
// Assign each subsection a group of relocations
i64 i = 0;
for (auto it = begin; it < end; it++) {
Subsection<E> &subsec = **it;
subsec.rel_offset = i;
while (i < rels.size() &&
rels[i].offset < subsec.input_offset + subsec.input_size) {
rels[i].offset -= subsec.input_offset;
i++;
}
subsec.nrels = i - subsec.rel_offset;
}
}
#define INSTANTIATE(E) \
template class InputSection<E>
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+693
View File
@@ -0,0 +1,693 @@
#pragma once
#include "../big-endian.h"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <string_view>
namespace mold::macho {
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef int8_t i8;
typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
static constexpr u32 FAT_MAGIC = 0xcafebabe;
static constexpr u32 MH_OBJECT = 0x1;
static constexpr u32 MH_EXECUTE = 0x2;
static constexpr u32 MH_FVMLIB = 0x3;
static constexpr u32 MH_CORE = 0x4;
static constexpr u32 MH_PRELOAD = 0x5;
static constexpr u32 MH_DYLIB = 0x6;
static constexpr u32 MH_DYLINKER = 0x7;
static constexpr u32 MH_BUNDLE = 0x8;
static constexpr u32 MH_DYLIB_STUB = 0x9;
static constexpr u32 MH_DSYM = 0xa;
static constexpr u32 MH_KEXT_BUNDLE = 0xb;
static constexpr u32 MH_NOUNDEFS = 0x1;
static constexpr u32 MH_INCRLINK = 0x2;
static constexpr u32 MH_DYLDLINK = 0x4;
static constexpr u32 MH_BINDATLOAD = 0x8;
static constexpr u32 MH_PREBOUND = 0x10;
static constexpr u32 MH_SPLIT_SEGS = 0x20;
static constexpr u32 MH_LAZY_INIT = 0x40;
static constexpr u32 MH_TWOLEVEL = 0x80;
static constexpr u32 MH_FORCE_FLAT = 0x100;
static constexpr u32 MH_NOMULTIDEFS = 0x200;
static constexpr u32 MH_NOFIXPREBINDING = 0x400;
static constexpr u32 MH_PREBINDABLE = 0x800;
static constexpr u32 MH_ALLMODSBOUND = 0x1000;
static constexpr u32 MH_SUBSECTIONS_VIA_SYMBOLS = 0x2000;
static constexpr u32 MH_CANONICAL = 0x4000;
static constexpr u32 MH_WEAK_DEFINES = 0x8000;
static constexpr u32 MH_BINDS_TO_WEAK = 0x10000;
static constexpr u32 MH_ALLOW_STACK_EXECUTION = 0x20000;
static constexpr u32 MH_ROOT_SAFE = 0x40000;
static constexpr u32 MH_SETUID_SAFE = 0x80000;
static constexpr u32 MH_NO_REEXPORTED_DYLIBS = 0x100000;
static constexpr u32 MH_PIE = 0x200000;
static constexpr u32 MH_DEAD_STRIPPABLE_DYLIB = 0x400000;
static constexpr u32 MH_HAS_TLV_DESCRIPTORS = 0x800000;
static constexpr u32 MH_NO_HEAP_EXECUTION = 0x1000000;
static constexpr u32 MH_APP_EXTENSION_SAFE = 0x02000000;
static constexpr u32 MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000;
static constexpr u32 MH_SIM_SUPPORT = 0x08000000;
static constexpr u32 VM_PROT_READ = 0x1;
static constexpr u32 VM_PROT_WRITE = 0x2;
static constexpr u32 VM_PROT_EXECUTE = 0x4;
static constexpr u32 VM_PROT_NO_CHANGE = 0x8;
static constexpr u32 VM_PROT_COPY = 0x10;
static constexpr u32 VM_PROT_WANTS_COPY = 0x10;
static constexpr u32 LC_REQ_DYLD = 0x80000000;
static constexpr u32 LC_SEGMENT = 0x1;
static constexpr u32 LC_SYMTAB = 0x2;
static constexpr u32 LC_SYMSEG = 0x3;
static constexpr u32 LC_THREAD = 0x4;
static constexpr u32 LC_UNIXTHREAD = 0x5;
static constexpr u32 LC_LOADFVMLIB = 0x6;
static constexpr u32 LC_IDFVMLIB = 0x7;
static constexpr u32 LC_IDENT = 0x8;
static constexpr u32 LC_FVMFILE = 0x9;
static constexpr u32 LC_PREPAGE = 0xa;
static constexpr u32 LC_DYSYMTAB = 0xb;
static constexpr u32 LC_LOAD_DYLIB = 0xc;
static constexpr u32 LC_ID_DYLIB = 0xd;
static constexpr u32 LC_LOAD_DYLINKER = 0xe;
static constexpr u32 LC_ID_DYLINKER = 0xf;
static constexpr u32 LC_PREBOUND_DYLIB = 0x10;
static constexpr u32 LC_ROUTINES = 0x11;
static constexpr u32 LC_SUB_FRAMEWORK = 0x12;
static constexpr u32 LC_SUB_UMBRELLA = 0x13;
static constexpr u32 LC_SUB_CLIENT = 0x14;
static constexpr u32 LC_SUB_LIBRARY = 0x15;
static constexpr u32 LC_TWOLEVEL_HINTS = 0x16;
static constexpr u32 LC_PREBIND_CKSUM = 0x17;
static constexpr u32 LC_LOAD_WEAK_DYLIB = (0x18 | LC_REQ_DYLD);
static constexpr u32 LC_SEGMENT_64 = 0x19;
static constexpr u32 LC_ROUTINES_64 = 0x1a;
static constexpr u32 LC_UUID = 0x1b;
static constexpr u32 LC_RPATH = (0x1c | LC_REQ_DYLD);
static constexpr u32 LC_CODE_SIGNATURE = 0x1d;
static constexpr u32 LC_SEGMENT_SPLIT_INFO = 0x1e;
static constexpr u32 LC_REEXPORT_DYLIB = (0x1f | LC_REQ_DYLD);
static constexpr u32 LC_LAZY_LOAD_DYLIB = 0x20;
static constexpr u32 LC_ENCRYPTION_INFO = 0x21;
static constexpr u32 LC_DYLD_INFO = 0x22;
static constexpr u32 LC_DYLD_INFO_ONLY = (0x22 | LC_REQ_DYLD);
static constexpr u32 LC_LOAD_UPWARD_DYLIB = (0x23 | LC_REQ_DYLD);
static constexpr u32 LC_VERSION_MIN_MACOSX = 0x24;
static constexpr u32 LC_VERSION_MIN_IPHONEOS = 0x25;
static constexpr u32 LC_FUNCTION_STARTS = 0x26;
static constexpr u32 LC_DYLD_ENVIRONMENT = 0x27;
static constexpr u32 LC_MAIN = (0x28 | LC_REQ_DYLD);
static constexpr u32 LC_DATA_IN_CODE = 0x29;
static constexpr u32 LC_SOURCE_VERSION = 0x2A;
static constexpr u32 LC_DYLIB_CODE_SIGN_DRS = 0x2B;
static constexpr u32 LC_ENCRYPTION_INFO_64 = 0x2C;
static constexpr u32 LC_LINKER_OPTION = 0x2D;
static constexpr u32 LC_LINKER_OPTIMIZATION_HINT = 0x2E;
static constexpr u32 LC_VERSION_MIN_TVOS = 0x2F;
static constexpr u32 LC_VERSION_MIN_WATCHOS = 0x30;
static constexpr u32 LC_NOTE = 0x31;
static constexpr u32 LC_BUILD_VERSION = 0x32;
static constexpr u32 LC_DYLD_EXPORTS_TRIE = (0x33 | LC_REQ_DYLD);
static constexpr u32 LC_DYLD_CHAINED_FIXUPS = (0x34 | LC_REQ_DYLD);
static constexpr u32 SG_HIGHVM = 0x1;
static constexpr u32 SG_FVMLIB = 0x2;
static constexpr u32 SG_NORELOC = 0x4;
static constexpr u32 SG_PROTECTED_VERSION_1 = 0x8;
static constexpr u32 SG_READ_ONLY = 0x10;
static constexpr u32 S_REGULAR = 0x0;
static constexpr u32 S_ZEROFILL = 0x1;
static constexpr u32 S_CSTRING_LITERALS = 0x2;
static constexpr u32 S_4BYTE_LITERALS = 0x3;
static constexpr u32 S_8BYTE_LITERALS = 0x4;
static constexpr u32 S_LITERAL_POINTERS = 0x5;
static constexpr u32 S_NON_LAZY_SYMBOL_POINTERS = 0x6;
static constexpr u32 S_LAZY_SYMBOL_POINTERS = 0x7;
static constexpr u32 S_SYMBOL_STUBS = 0x8;
static constexpr u32 S_MOD_INIT_FUNC_POINTERS = 0x9;
static constexpr u32 S_MOD_TERM_FUNC_POINTERS = 0xa;
static constexpr u32 S_COALESCED = 0xb;
static constexpr u32 S_GB_ZEROFILL = 0xc;
static constexpr u32 S_INTERPOSING = 0xd;
static constexpr u32 S_16BYTE_LITERALS = 0xe;
static constexpr u32 S_DTRACE_DOF = 0xf;
static constexpr u32 S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10;
static constexpr u32 S_THREAD_LOCAL_REGULAR = 0x11;
static constexpr u32 S_THREAD_LOCAL_ZEROFILL = 0x12;
static constexpr u32 S_THREAD_LOCAL_VARIABLES = 0x13;
static constexpr u32 S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14;
static constexpr u32 S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15;
static constexpr u32 S_INIT_FUNC_OFFSETS = 0x16;
static constexpr u32 S_ATTR_LOC_RELOC = 0x000001;
static constexpr u32 S_ATTR_EXT_RELOC = 0x000002;
static constexpr u32 S_ATTR_SOME_INSTRUCTIONS = 0x000004;
static constexpr u32 S_ATTR_DEBUG = 0x020000;
static constexpr u32 S_ATTR_SELF_MODIFYING_CODE = 0x040000;
static constexpr u32 S_ATTR_LIVE_SUPPORT = 0x080000;
static constexpr u32 S_ATTR_NO_DEAD_STRIP = 0x100000;
static constexpr u32 S_ATTR_STRIP_STATIC_SYMS = 0x200000;
static constexpr u32 S_ATTR_NO_TOC = 0x400000;
static constexpr u32 S_ATTR_PURE_INSTRUCTIONS = 0x800000;
static constexpr u32 CPU_TYPE_X86_64 = 0x1000007;
static constexpr u32 CPU_TYPE_ARM64 = 0x100000c;
static constexpr u32 CPU_SUBTYPE_X86_64_ALL = 3;
static constexpr u32 CPU_SUBTYPE_ARM64_ALL = 0;
static constexpr u32 REBASE_TYPE_POINTER = 1;
static constexpr u32 REBASE_TYPE_TEXT_ABSOLUTE32 = 2;
static constexpr u32 REBASE_TYPE_TEXT_PCREL32 = 3;
static constexpr u32 REBASE_OPCODE_MASK = 0xf0;
static constexpr u32 REBASE_IMMEDIATE_MASK = 0x0f;
static constexpr u32 REBASE_OPCODE_DONE = 0x00;
static constexpr u32 REBASE_OPCODE_SET_TYPE_IMM = 0x10;
static constexpr u32 REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20;
static constexpr u32 REBASE_OPCODE_ADD_ADDR_ULEB = 0x30;
static constexpr u32 REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40;
static constexpr u32 REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50;
static constexpr u32 REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60;
static constexpr u32 REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70;
static constexpr u32 REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80;
static constexpr u32 BIND_TYPE_POINTER = 1;
static constexpr u32 BIND_TYPE_TEXT_ABSOLUTE32 = 2;
static constexpr u32 BIND_TYPE_TEXT_PCREL32 = 3;
static constexpr u32 BIND_SPECIAL_DYLIB_SELF = 0;
static constexpr u32 BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1;
static constexpr u32 BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2;
static constexpr u32 BIND_SPECIAL_DYLIB_WEAK_LOOKUP = -3;
static constexpr u32 BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1;
static constexpr u32 BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8;
static constexpr u32 BIND_OPCODE_MASK = 0xF0;
static constexpr u32 BIND_IMMEDIATE_MASK = 0x0F;
static constexpr u32 BIND_OPCODE_DONE = 0x00;
static constexpr u32 BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10;
static constexpr u32 BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20;
static constexpr u32 BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30;
static constexpr u32 BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40;
static constexpr u32 BIND_OPCODE_SET_TYPE_IMM = 0x50;
static constexpr u32 BIND_OPCODE_SET_ADDEND_SLEB = 0x60;
static constexpr u32 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70;
static constexpr u32 BIND_OPCODE_ADD_ADDR_ULEB = 0x80;
static constexpr u32 BIND_OPCODE_DO_BIND = 0x90;
static constexpr u32 BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0;
static constexpr u32 BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0;
static constexpr u32 BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0;
static constexpr u32 BIND_OPCODE_THREADED = 0xD0;
static constexpr u32 BIND_SUBOPCODE_THREADED_SET_BIND_ORDINAL_TABLE_SIZE_ULEB = 0x00;
static constexpr u32 BIND_SUBOPCODE_THREADED_APPLY = 0x01;
static constexpr u32 EXPORT_SYMBOL_FLAGS_KIND_MASK = 0x03;
static constexpr u32 EXPORT_SYMBOL_FLAGS_KIND_REGULAR = 0x00;
static constexpr u32 EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL = 0x01;
static constexpr u32 EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE = 0x02;
static constexpr u32 EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION = 0x04;
static constexpr u32 EXPORT_SYMBOL_FLAGS_REEXPORT = 0x08;
static constexpr u32 EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER = 0x10;
static constexpr u32 DICE_KIND_DATA = 1;
static constexpr u32 DICE_KIND_JUMP_TABLE8 = 2;
static constexpr u32 DICE_KIND_JUMP_TABLE16 = 3;
static constexpr u32 DICE_KIND_JUMP_TABLE32 = 4;
static constexpr u32 DICE_KIND_ABS_JUMP_TABLE32 = 5;
static constexpr u32 N_UNDF = 0;
static constexpr u32 N_ABS = 1;
static constexpr u32 N_INDR = 5;
static constexpr u32 N_PBUD = 6;
static constexpr u32 N_SECT = 7;
static constexpr u32 N_GSYM = 0x20;
static constexpr u32 N_FNAME = 0x22;
static constexpr u32 N_FUN = 0x24;
static constexpr u32 N_STSYM = 0x26;
static constexpr u32 N_LCSYM = 0x28;
static constexpr u32 N_BNSYM = 0x2e;
static constexpr u32 N_AST = 0x32;
static constexpr u32 N_OPT = 0x3c;
static constexpr u32 N_RSYM = 0x40;
static constexpr u32 N_SLINE = 0x44;
static constexpr u32 N_ENSYM = 0x4e;
static constexpr u32 N_SSYM = 0x60;
static constexpr u32 N_SO = 0x64;
static constexpr u32 N_OSO = 0x66;
static constexpr u32 N_LSYM = 0x80;
static constexpr u32 N_BINCL = 0x82;
static constexpr u32 N_SOL = 0x84;
static constexpr u32 N_PARAMS = 0x86;
static constexpr u32 N_VERSION = 0x88;
static constexpr u32 N_OLEVEL = 0x8A;
static constexpr u32 N_PSYM = 0xa0;
static constexpr u32 N_EINCL = 0xa2;
static constexpr u32 N_ENTRY = 0xa4;
static constexpr u32 N_LBRAC = 0xc0;
static constexpr u32 N_EXCL = 0xc2;
static constexpr u32 N_RBRAC = 0xe0;
static constexpr u32 N_BCOMM = 0xe2;
static constexpr u32 N_ECOMM = 0xe4;
static constexpr u32 N_ECOML = 0xe8;
static constexpr u32 N_LENG = 0xfe;
static constexpr u32 N_PC = 0x30;
static constexpr u32 REFERENCE_TYPE = 0xf;
static constexpr u32 REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0;
static constexpr u32 REFERENCE_FLAG_UNDEFINED_LAZY = 1;
static constexpr u32 REFERENCE_FLAG_DEFINED = 2;
static constexpr u32 REFERENCE_FLAG_PRIVATE_DEFINED = 3;
static constexpr u32 REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 4;
static constexpr u32 REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5;
static constexpr u32 REFERENCED_DYNAMICALLY = 0x0010;
static constexpr u32 N_NO_DEAD_STRIP = 0x0020;
static constexpr u32 N_DESC_DISCARDED = 0x0020;
static constexpr u32 N_WEAK_REF = 0x0040;
static constexpr u32 N_WEAK_DEF = 0x0080;
static constexpr u32 N_REF_TO_WEAK = 0x0080;
static constexpr u32 N_ARM_THUMB_DEF = 0x0008;
static constexpr u32 N_SYMBOL_RESOLVER = 0x0100;
static constexpr u32 N_ALT_ENTRY = 0x0200;
static constexpr u32 PLATFORM_MACOS = 1;
static constexpr u32 PLATFORM_IOS = 2;
static constexpr u32 PLATFORM_TVOS = 3;
static constexpr u32 PLATFORM_WATCHOS = 4;
static constexpr u32 PLATFORM_BRIDGEOS = 5;
static constexpr u32 PLATFORM_MACCATALYST = 6;
static constexpr u32 PLATFORM_IOSSIMULATOR = 7;
static constexpr u32 PLATFORM_TVOSSIMULATOR = 8;
static constexpr u32 PLATFORM_WATCHOSSIMULATOR = 9;
static constexpr u32 PLATFORM_DRIVERKIT = 10;
static constexpr u32 TOOL_CLANG = 1;
static constexpr u32 TOOL_SWIFT = 2;
static constexpr u32 TOOL_LD = 3;
static constexpr u32 ARM64_RELOC_UNSIGNED = 0;
static constexpr u32 ARM64_RELOC_SUBTRACTOR = 1;
static constexpr u32 ARM64_RELOC_BRANCH26 = 2;
static constexpr u32 ARM64_RELOC_PAGE21 = 3;
static constexpr u32 ARM64_RELOC_PAGEOFF12 = 4;
static constexpr u32 ARM64_RELOC_GOT_LOAD_PAGE21 = 5;
static constexpr u32 ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6;
static constexpr u32 ARM64_RELOC_POINTER_TO_GOT = 7;
static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGE21 = 8;
static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9;
static constexpr u32 ARM64_RELOC_ADDEND = 10;
static constexpr u32 X86_64_RELOC_UNSIGNED = 0;
static constexpr u32 X86_64_RELOC_SIGNED = 1;
static constexpr u32 X86_64_RELOC_BRANCH = 2;
static constexpr u32 X86_64_RELOC_GOT_LOAD = 3;
static constexpr u32 X86_64_RELOC_GOT = 4;
static constexpr u32 X86_64_RELOC_SUBTRACTOR = 5;
static constexpr u32 X86_64_RELOC_SIGNED_1 = 6;
static constexpr u32 X86_64_RELOC_SIGNED_2 = 7;
static constexpr u32 X86_64_RELOC_SIGNED_4 = 8;
static constexpr u32 X86_64_RELOC_TLV = 9;
struct FatHeader {
ubig32 magic;
ubig32 nfat_arch;
};
struct FatArch {
ubig32 cputype;
ubig32 cpusubtype;
ubig32 offset;
ubig32 size;
ubig32 align;
};
struct MachHeader {
u32 magic;
u32 cputype;
u32 cpusubtype;
u32 filetype;
u32 ncmds;
u32 sizeofcmds;
u32 flags;
u32 reserved;
};
struct LoadCommand {
u32 cmd;
u32 cmdsize;
};
struct SegmentCommand {
std::string_view get_segname() const {
return {segname, strnlen(segname, sizeof(segname))};
}
u32 cmd;
u32 cmdsize;
char segname[16];
u64 vmaddr;
u64 vmsize;
u64 fileoff;
u64 filesize;
u32 maxprot;
u32 initprot;
u32 nsects;
u32 flags;
};
struct MachSection {
void set_segname(std::string_view name) {
assert(name.size() <= sizeof(segname));
memcpy(segname, name.data(), name.size());
}
std::string_view get_segname() const {
return {segname, strnlen(segname, sizeof(segname))};
}
void set_sectname(std::string_view name) {
assert(name.size() <= sizeof(sectname));
memcpy(sectname, name.data(), name.size());
}
std::string_view get_sectname() const {
return {sectname, strnlen(sectname, sizeof(sectname))};
}
bool match(std::string_view segname, std::string_view sectname) const {
return get_segname() == segname && get_sectname() == sectname;
}
char sectname[16];
char segname[16];
u64 addr;
u64 size;
u32 offset;
u32 p2align;
u32 reloff;
u32 nreloc;
u32 type : 8;
u32 attr : 24;
u32 reserved1;
u32 reserved2;
u32 reserved3;
};
struct DylibCommand {
u32 cmd;
u32 cmdsize;
u32 nameoff;
u32 timestamp;
u32 current_version;
u32 compatibility_version;
};
struct DylinkerCommand {
u32 cmd;
u32 cmdsize;
u32 nameoff;
};
struct SymtabCommand {
u32 cmd;
u32 cmdsize;
u32 symoff;
u32 nsyms;
u32 stroff;
u32 strsize;
};
struct DysymtabCommand {
u32 cmd;
u32 cmdsize;
u32 ilocalsym;
u32 nlocalsym;
u32 iextdefsym;
u32 nextdefsym;
u32 iundefsym;
u32 nundefsym;
u32 tocoff;
u32 ntoc;
u32 modtaboff;
u32 nmodtab;
u32 extrefsymoff;
u32 nextrefsyms;
u32 indirectsymoff;
u32 nindirectsyms;
u32 extreloff;
u32 nextrel;
u32 locreloff;
u32 nlocrel;
};
struct VersionMinCommand {
u32 cmd;
u32 cmdsize;
u32 version;
u32 sdk;
};
struct DyldInfoCommand {
u32 cmd;
u32 cmdsize;
u32 rebase_off;
u32 rebase_size;
u32 bind_off;
u32 bind_size;
u32 weak_bind_off;
u32 weak_bind_size;
u32 lazy_bind_off;
u32 lazy_bind_size;
u32 export_off;
u32 export_size;
};
struct UUIDCommand {
u32 cmd;
u32 cmdsize;
u8 uuid[16];
};
struct RpathCommand {
u32 cmd;
u32 cmdsize;
u32 path_off;
};
struct LinkEditDataCommand {
u32 cmd;
u32 cmdsize;
u32 dataoff;
u32 datasize;
};
struct BuildToolVersion {
u32 tool;
u32 version;
};
struct BuildVersionCommand {
u32 cmd;
u32 cmdsize;
u32 platform;
u32 minos;
u32 sdk;
u32 ntools;
};
struct EntryPointCommand {
u32 cmd;
u32 cmdsize;
u64 entryoff;
u64 stacksize;
};
struct SourceVersionCommand {
u32 cmd;
u32 cmdsize;
u64 version;
};
struct DataInCodeEntry {
u32 offset;
u16 length;
u16 kind;
};
// This struct is named `n_list` on BSD and macOS.
struct MachSym {
bool is_undef() const {
return type == N_UNDF && !is_common();
}
bool is_common() const {
return type == N_UNDF && ext && value;
}
u32 stroff;
u8 ext : 1;
u8 type : 3;
u8 pext : 1;
u8 stub : 3;
u8 sect;
union {
u16 desc;
struct {
u16 : 8;
u16 p2align : 4;
u16 : 4;
};
};
u64 value;
};
// This struct is named `relocation_info` on BSD and macOS.
struct MachRel {
u32 offset;
u32 idx : 24;
u32 is_pcrel : 1;
u32 p2size : 2;
u32 is_extern : 1;
u32 type : 4;
};
// __TEXT,__unwind_info section contents
static constexpr u32 UNWIND_SECTION_VERSION = 1;
static constexpr u32 UNWIND_SECOND_LEVEL_REGULAR = 2;
static constexpr u32 UNWIND_SECOND_LEVEL_COMPRESSED = 3;
static constexpr u32 UNWIND_PERSONALITY_MASK = 0x30000000;
struct UnwindSectionHeader {
u32 version;
u32 encoding_offset;
u32 encoding_count;
u32 personality_offset;
u32 personality_count;
u32 page_offset;
u32 page_count;
};
struct UnwindFirstLevelPage {
u32 func_addr;
u32 page_offset;
u32 lsda_offset;
};
struct UnwindSecondLevelPage {
u32 kind;
u16 page_offset;
u16 page_count;
u16 encoding_offset;
u16 encoding_count;
};
struct UnwindLsdaEntry {
u32 func_addr;
u32 lsda_addr;
};
struct UnwindPageEntry {
u32 func_addr : 24;
u32 encoding : 8;
};
// __LD,__compact_unwind section contents
struct CompactUnwindEntry {
u64 code_start;
u32 code_len;
u32 encoding;
u64 personality;
u64 lsda;
};
// __LINKEDIT,__code_signature
static constexpr u32 CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0;
static constexpr u32 CS_SUPPORTSEXECSEG = 0x20400;
static constexpr u32 CSMAGIC_CODEDIRECTORY = 0xfade0c02;
static constexpr u32 CSSLOT_CODEDIRECTORY = 0;
static constexpr u32 CS_ADHOC = 0x00000002;
static constexpr u32 CS_LINKER_SIGNED = 0x00020000;
static constexpr u32 CS_EXECSEG_MAIN_BINARY = 1;
static constexpr u32 CS_HASHTYPE_SHA256 = 2;
struct CodeSignatureHeader {
ubig32 magic;
ubig32 length;
ubig32 count;
};
struct CodeSignatureBlobIndex {
ubig32 type;
ubig32 offset;
u32 padding;
};
struct CodeSignatureDirectory {
ubig32 magic;
ubig32 length;
ubig32 version;
ubig32 flags;
ubig32 hash_offset;
ubig32 ident_offset;
ubig32 n_special_slots;
ubig32 n_code_slots;
ubig32 code_limit;
u8 hash_size;
u8 hash_type;
u8 platform;
u8 page_size;
ubig32 spare2;
ubig32 scatter_offset;
ubig32 team_offset;
ubig32 spare3;
ubig64 code_limit64;
ubig64 exec_seg_base;
ubig64 exec_seg_limit;
ubig64 exec_seg_flags;
};
struct ARM64 {
static constexpr u32 cputype = CPU_TYPE_ARM64;
static constexpr u32 cpusubtype = CPU_SUBTYPE_ARM64_ALL;
static constexpr u32 abs_rel = ARM64_RELOC_UNSIGNED;
static constexpr u32 word_size = 8;
static constexpr u32 stub_size = 12;
static constexpr u32 stub_helper_hdr_size = 24;
static constexpr u32 stub_helper_size = 12;
};
struct X86_64 {
static constexpr u32 cputype = CPU_TYPE_X86_64;
static constexpr u32 cpusubtype = CPU_SUBTYPE_X86_64_ALL;
static constexpr u32 abs_rel = X86_64_RELOC_UNSIGNED;
static constexpr u32 word_size = 8;
static constexpr u32 stub_size = 6;
static constexpr u32 stub_helper_hdr_size = 16;
static constexpr u32 stub_helper_size = 10;
};
} // namespace mold::macho
+489
View File
@@ -0,0 +1,489 @@
#include "mold.h"
#include "../archive-file.h"
#include "../cmdline.h"
#include <cstdlib>
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
static std::pair<std::string_view, std::string_view>
split_string(std::string_view str, char sep) {
size_t pos = str.find(sep);
if (pos == str.npos)
return {str, ""};
return {str.substr(0, pos), str.substr(pos + 1)};
}
template <typename E>
static void create_internal_file(Context<E> &ctx) {
ObjectFile<E> *obj = new ObjectFile<E>;
ctx.obj_pool.emplace_back(obj);
ctx.objs.push_back(obj);
auto add = [&](std::string_view name) {
Symbol<E> *sym = get_symbol(ctx, name);
sym->file = obj;
obj->syms.push_back(sym);
return sym;
};
add("__dyld_private");
switch (ctx.output_type) {
case MH_EXECUTE: {
Symbol<E> *sym = add("__mh_execute_header");
sym->is_extern = true;
sym->referenced_dynamically = true;
sym->value = ctx.arg.pagezero_size;
break;
}
case MH_DYLIB:
add("__mh_dylib_header");
break;
case MH_BUNDLE:
add("__mh_bundle_header");
break;
default:
unreachable();
}
}
template <typename E>
static bool compare_segments(const std::unique_ptr<OutputSegment<E>> &a,
const std::unique_ptr<OutputSegment<E>> &b) {
// We want to sort output segments in the following order:
// __TEXT, __DATA_CONST, __DATA, <other segments>, __LINKEDIT
auto get_rank = [](std::string_view name) {
if (name == "__TEXT")
return 0;
if (name == "__DATA_CONST")
return 1;
if (name == "__DATA")
return 2;
return INT_MAX;
};
std::string_view na = a->cmd.get_segname();
std::string_view nb = b->cmd.get_segname();
i64 ra = get_rank(na);
i64 rb = get_rank(nb);
if (ra != INT_MAX || rb != INT_MAX)
return ra < rb;
if (na == "__LINKEDIT" || nb == "__LINKEDIT")
return na != "__LINKEDIT";
return na < nb;
}
template <typename E>
static bool compare_chunks(const Chunk<E> *a, const Chunk<E> *b) {
assert(a->hdr.get_segname() == b->hdr.get_segname());
if ((a->hdr.type == S_ZEROFILL) != (b->hdr.type == S_ZEROFILL))
return a->hdr.type != S_ZEROFILL;
static const std::string_view rank[] = {
// __TEXT
"__mach_header",
"__text",
"__stubs",
"__stub_helper",
"__gcc_except_tab",
"__cstring",
"__unwind_info",
// __DATA_CONST
"__got",
// __DATA
"__la_symbol_ptr",
"__data",
"__thread_ptr",
"__thread_data",
"__thread_vars",
"__thread_bss",
"__common",
"__bss",
// __LINKEDIT
"__rebase",
"__binding",
"__weak_binding",
"__lazy_binding",
"__export",
"__func_starts",
"__data_in_code",
"__symbol_table",
"__ind_sym_tab",
"__string_table",
"__code_signature",
};
auto get_rank = [](const Chunk<E> *chunk) -> i64 {
std::string_view name = chunk->hdr.get_sectname();
i64 i = 0;
for (; i < sizeof(rank) / sizeof(rank[0]); i++)
if (name == rank[i])
return i;
return INT_MAX;
};
i64 ra = get_rank(a);
i64 rb = get_rank(b);
if (ra == INT_MAX && rb == INT_MAX)
return a->hdr.get_sectname() < b->hdr.get_sectname();
return ra < rb;
}
template <typename E>
static void create_synthetic_chunks(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs)
for (std::unique_ptr<Subsection<E>> &subsec : file->subsections)
subsec->isec.osec.add_subsec(subsec.get());
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk != ctx.data && chunk->is_regular &&
((OutputSection<E> *)chunk)->members.empty())
continue;
OutputSegment<E> *seg =
OutputSegment<E>::get_instance(ctx, chunk->hdr.get_segname());
seg->chunks.push_back(chunk);
}
sort(ctx.segments, compare_segments<E>);
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
sort(seg->chunks, compare_chunks<E>);
}
template <typename E>
static void scan_unwind_info(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs)
for (UnwindRecord<E> &rec : file->unwind_records)
if (!ctx.arg.dead_strip || rec.is_alive)
if (rec.personality)
rec.personality->flags |= NEEDS_GOT;
}
template <typename E>
static void export_symbols(Context<E> &ctx) {
ctx.got.add(ctx, get_symbol(ctx, "dyld_stub_binder"));
for (ObjectFile<E> *file : ctx.objs) {
for (Symbol<E> *sym : file->syms) {
if (sym && sym->file == file) {
if (sym->flags & NEEDS_GOT)
ctx.got.add(ctx, sym);
if (sym->flags & NEEDS_THREAD_PTR)
ctx.thread_ptrs.add(ctx, sym);
}
}
}
for (DylibFile<E> *file : ctx.dylibs) {
for (Symbol<E> *sym : file->syms) {
if (sym && sym->file == file) {
if (sym->flags & NEEDS_STUB)
ctx.stubs.add(ctx, sym);
if (sym->flags & NEEDS_GOT)
ctx.got.add(ctx, sym);
if (sym->flags & NEEDS_THREAD_PTR)
ctx.thread_ptrs.add(ctx, sym);
}
}
}
}
template <typename E>
static i64 assign_offsets(Context<E> &ctx) {
i64 sect_idx = 1;
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
for (Chunk<E> *chunk : seg->chunks)
if (!chunk->is_hidden)
chunk->sect_idx = sect_idx++;
i64 fileoff = 0;
i64 vmaddr = ctx.arg.pagezero_size;
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments) {
seg->set_offset(ctx, fileoff, vmaddr);
fileoff += seg->cmd.filesize;
vmaddr += seg->cmd.vmsize;
}
return fileoff;
}
template <typename E>
static void fix_synthetic_symbol_values(Context<E> &ctx) {
get_symbol(ctx, "__dyld_private")->value = ctx.data->hdr.addr;
get_symbol(ctx, "__mh_dylib_header")->value = ctx.data->hdr.addr;
get_symbol(ctx, "__mh_bundle_header")->value = ctx.data->hdr.addr;
}
template <typename E>
MappedFile<Context<E>> *find_framework(Context<E> &ctx, std::string name) {
std::string suffix;
std::tie(name, suffix) = split_string(name, ',');
for (std::string path : ctx.arg.framework_paths) {
path = get_realpath(path + "/" + name + ".framework/" + name);
if (!suffix.empty())
if (auto *mf = MappedFile<Context<E>>::open(ctx, path + suffix))
return mf;
if (auto *mf = MappedFile<Context<E>>::open(ctx, path + ".tbd"))
return mf;
if (auto *mf = MappedFile<Context<E>>::open(ctx, path))
return mf;
}
Fatal(ctx) << "-framework not found: " << name;
}
template <typename E>
MappedFile<Context<E>> *find_library(Context<E> &ctx, std::string name) {
for (std::string dir : ctx.arg.library_paths) {
for (std::string ext : {".tbd", ".dylib", ".a"}) {
std::string path = dir + "/lib" + name + ext;
if (MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path))
return mf;
}
}
return nullptr;
}
template <typename E>
static MappedFile<Context<E>> *
strip_universal_header(Context<E> &ctx, MappedFile<Context<E>> *mf) {
FatHeader &hdr = *(FatHeader *)mf->data;
assert(hdr.magic == FAT_MAGIC);
FatArch *arch = (FatArch *)(mf->data + sizeof(hdr));
for (i64 i = 0; i < hdr.nfat_arch; i++)
if (arch[i].cputype == E::cputype)
return mf->slice(ctx, mf->name, arch[i].offset, arch[i].size);
Fatal(ctx) << mf->name << ": fat file contains no matching file";
}
template <typename E>
static void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf,
bool is_needed = false) {
if (get_file_type(mf) == FileType::MACH_UNIVERSAL)
mf = strip_universal_header(ctx, mf);
switch (get_file_type(mf)) {
case FileType::TAPI:
case FileType::MACH_DYLIB:
ctx.dylibs.push_back(DylibFile<E>::create(ctx, mf));
if (is_needed || !ctx.arg.dead_strip_dylibs)
ctx.dylibs.back()->is_needed = true;
break;
case FileType::MACH_OBJ:
ctx.objs.push_back(ObjectFile<E>::create(ctx, mf, ""));
break;
case FileType::AR:
for (MappedFile<Context<E>> *child : read_archive_members(ctx, mf))
if (get_file_type(child) == FileType::MACH_OBJ)
ctx.objs.push_back(ObjectFile<E>::create(ctx, child, mf->name));
break;
default:
break;
// Fatal(ctx) << mf->name << ": unknown file type";
}
}
template <typename E>
static std::vector<std::string>
read_filelist(Context<E> &ctx, std::string arg) {
std::string path;
std::string dir;
if (size_t pos = arg.find(','); pos != arg.npos) {
path = arg.substr(0, pos);
dir = arg.substr(pos + 1) + "/";
} else {
path = arg;
}
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path);
if (!mf)
Fatal(ctx) << "-filepath: cannot open " << path;
std::vector<std::string> vec;
for (std::string_view str = mf->get_contents(); !str.empty();) {
std::string_view path;
std::tie(path, str) = split_string(str, '\n');
vec.push_back(path_clean(dir + std::string(path)));
}
return vec;
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
while (!args.empty()) {
if (args[0].starts_with("-filelist")) {
for (std::string &path : read_filelist(ctx, args[1])) {
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path);
if (!mf)
Fatal(ctx) << "-filepath " << args[1] << ": cannot open file: " << path;
read_file(ctx, mf);
}
args = args.subspan(2);
} else if (args[0] == "-framework" || args[0] == "-needed_framework") {
bool needed = (args[0] == "-needed_framework");
read_file(ctx, find_framework(ctx, args[1]), needed);
args = args.subspan(2);
} else if (args[0] == "-l" || args[0] == "-needed-l") {
MappedFile<Context<E>> *mf = find_library(ctx, args[1]);
if (!mf)
Fatal(ctx) << "library not found: -l" << args[1];
bool needed = (args[0] == "-needed-l");
read_file(ctx, mf, needed);
args = args.subspan(2);
} else {
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, args[0]));
args = args.subspan(1);
}
}
if (ctx.objs.empty())
Fatal(ctx) << "no input files";
}
template <typename E>
static int do_main(int argc, char **argv) {
Context<E> ctx;
if (argc > 1 && std::string_view(argv[1]) == "-dump") {
if (argc != 3)
Fatal(ctx) << "usage: ld64.mold -dump <executable-name>\n";
dump_file(argv[2]);
exit(0);
}
for (i64 i = 0; i < argc; i++)
ctx.cmdline_args.push_back(argv[i]);
std::vector<std::string> file_args;
parse_nonpositional_args(ctx, file_args);
if (ctx.arg.arch != E::cputype) {
if (ctx.arg.arch == CPU_TYPE_X86_64)
return do_main<X86_64>(argc, argv);
}
read_input_files(ctx, file_args);
i64 priority = 1;
for (ObjectFile<E> *file : ctx.objs)
file->priority = priority++;
for (DylibFile<E> *dylib : ctx.dylibs)
dylib->priority = priority++;
for (i64 i = 0; i < ctx.dylibs.size(); i++)
ctx.dylibs[i]->dylib_idx = i + 1;
for (ObjectFile<E> *file : ctx.objs)
file->parse(ctx);
for (DylibFile<E> *dylib : ctx.dylibs)
dylib->parse(ctx);
if (ctx.arg.ObjC)
for (ObjectFile<E> *file : ctx.objs)
if (!file->archive_name.empty() && file->is_objc_object(ctx))
file->is_alive = true;
for (ObjectFile<E> *file : ctx.objs) {
if (file->is_alive)
file->resolve_regular_symbols(ctx);
else
file->resolve_lazy_symbols(ctx);
}
std::vector<ObjectFile<E> *> live_objs;
for (ObjectFile<E> *file : ctx.objs)
if (file->is_alive)
live_objs.push_back(file);
for (i64 i = 0; i < live_objs.size(); i++)
append(live_objs, live_objs[i]->mark_live_objects(ctx));
for (DylibFile<E> *dylib : ctx.dylibs)
dylib->resolve_symbols(ctx);
if (ctx.output_type == MH_EXECUTE && !get_symbol(ctx, ctx.arg.entry)->file)
Error(ctx) << "undefined entry point symbol: " << ctx.arg.entry;
create_internal_file(ctx);
std::erase_if(ctx.objs, [](ObjectFile<E> *file) { return !file->is_alive; });
std::erase_if(ctx.dylibs, [](DylibFile<E> *file) { return !file->is_alive; });
if (ctx.arg.trace) {
for (ObjectFile<E> *file : ctx.objs)
SyncOut(ctx) << *file;
for (DylibFile<E> *file : ctx.dylibs)
SyncOut(ctx) << *file;
}
for (ObjectFile<E> *file : ctx.objs)
file->convert_common_symbols(ctx);
if (ctx.arg.dead_strip)
dead_strip(ctx);
create_synthetic_chunks(ctx);
for (ObjectFile<E> *file : ctx.objs)
file->check_duplicate_symbols(ctx);
for (i64 i = 0; i < ctx.segments.size(); i++)
ctx.segments[i]->seg_idx = i + 1;
for (ObjectFile<E> *file : ctx.objs)
for (std::unique_ptr<Subsection<E> > &subsec : file->subsections)
subsec->scan_relocations(ctx);
scan_unwind_info(ctx);
if (ctx.arg.dead_strip_dylibs)
std::erase_if(ctx.dylibs, [](DylibFile<E> *file) { return !file->is_needed; });
export_symbols(ctx);
i64 output_size = assign_offsets(ctx);
fix_synthetic_symbol_values(ctx);
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, output_size, 0777);
ctx.buf = ctx.output_file->buf;
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
seg->copy_buf(ctx);
ctx.code_sig.write_signature(ctx);
ctx.output_file->close(ctx);
ctx.checkpoint();
if (!ctx.arg.map.empty())
print_map(ctx);
return 0;
}
int main(int argc, char **argv) {
if (!getenv("MOLD_SUPPRESS_MACHO_WARNING")) {
std::cerr <<
R"(********************************************************************************
mold for macOS is pre-alpha. Do not use unless you know what you are doing.
Do not report bugs because it's too early to manage missing features as bugs.
********************************************************************************
)";
}
return do_main<ARM64>(argc, argv);
}
}
+80
View File
@@ -0,0 +1,80 @@
#include "mold.h"
#include <iomanip>
#include <fstream>
namespace mold::macho {
struct Sym {
u64 addr;
u32 size;
u32 fileidx;
std::string_view name;
};
template <typename E>
void print_map(Context<E> &ctx) {
std::ofstream out(ctx.arg.map.c_str());
if (!out.is_open())
Fatal(ctx) << "cannot open " << ctx.arg.map << ": " << errno_string();
out << "# Path: " << ctx.arg.output << "\n"
<< "# Arch: x86-64\n"
<< "# Object files:\n";
std::vector<Sym> syms;
for (i64 i = 0; i < ctx.objs.size(); i++){
ObjectFile<E> &file = *ctx.objs[i];
if (file.is_alive) {
out << "[" << std::setw(3) << i << "] " << file << "\n";
for (Symbol<E> *sym : file.syms)
if (sym->file == &file)
syms.push_back({sym->get_addr(ctx), 0, (u32)i, sym->name});
}
}
sort(syms, [](const Sym &a, const Sym &b) {
return a.addr < b.addr;
});
out << "# Sections:\n"
<< "# Address Size Segment Section\n";
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments) {
for (Chunk<E> *chunk : seg->chunks) {
if (!chunk->is_hidden) {
out << "0x" << std::hex
<< std::setw(8) << std::setfill('0') << chunk->hdr.addr
<< " 0x"
<< std::setw(8) << std::setfill('0') << chunk->hdr.size
<< " " << std::setw(7) << std::left << std::setfill(' ')
<< chunk->hdr.get_segname()
<< " " << chunk->hdr.get_sectname() << "\n";
}
}
}
out << "# Symbols:\n"
<< "# Address Size File Name\n";
for (Sym &sym : syms) {
out << "0x" << std::hex
<< std::setw(8) << std::setfill('0') << sym.addr
<< " 0x"
<< std::setw(8) << std::setfill('0') << 0
<< " ["
<< std::setw(3) << std::right << std::setfill(' ') << std::dec
<< sym.fileidx
<< "] " << sym.name << "\n";
}
}
#define INSTANTIATE(E) \
template void print_map(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+920
View File
@@ -0,0 +1,920 @@
#pragma once
#include "macho.h"
#include "../mold.h"
#include <map>
#include <memory>
#include <span>
#include <tbb/concurrent_hash_map.h>
#include <tbb/spin_mutex.h>
#include <unordered_map>
#include <variant>
namespace mold::macho {
static constexpr i64 COMMON_PAGE_SIZE = 0x4000;
static constexpr i64 SHA256_SIZE = 32;
template <typename E> class Chunk;
template <typename E> class InputSection;
template <typename E> class OutputSection;
template <typename E> class Subsection;
template <typename E> struct Context;
template <typename E> struct Symbol;
//
// object-file.cc
//
template <typename E>
struct Relocation {
u32 offset = 0;
u8 type = -1;
u8 p2size = 0;
bool is_pcrel = false;
i64 addend = 0;
Symbol<E> *sym = nullptr;
Subsection<E> *subsec = nullptr;
};
template <typename E>
struct UnwindRecord {
UnwindRecord(u32 len, u32 enc) : code_len(len), encoding(enc) {}
inline u64 get_func_raddr(Context<E> &ctx) const;
Subsection<E> *subsec = nullptr;
u32 offset = 0;
u32 code_len;
u32 encoding;
Symbol<E> *personality = nullptr;
Subsection<E> *lsda = nullptr;
u32 lsda_offset = 0;
bool is_alive = false;
};
template <typename E>
class InputFile {
public:
virtual ~InputFile() = default;
MappedFile<Context<E>> *mf = nullptr;
std::vector<Symbol<E> *> syms;
i64 priority = 0;
std::atomic_bool is_alive = false;
std::string archive_name;
virtual bool is_dylib() const = 0;
protected:
InputFile() = default;
};
template <typename E>
class ObjectFile : public InputFile<E> {
public:
ObjectFile() = default;
static ObjectFile *create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name);
bool is_dylib() const override { return false; }
void parse(Context<E> &ctx);
Subsection<E> *find_subsection(Context<E> &ctx, u32 addr);
void parse_compact_unwind(Context<E> &ctx, MachSection &hdr);
void resolve_regular_symbols(Context<E> &ctx);
void resolve_lazy_symbols(Context<E> &ctx);
bool is_objc_object(Context<E> &ctx);
std::vector<ObjectFile *> mark_live_objects(Context<E> &ctx);
void convert_common_symbols(Context<E> &ctx);
void check_duplicate_symbols(Context<E> &ctx);
Relocation<E> read_reloc(Context<E> &ctx, const MachSection &hdr, MachRel r);
std::vector<std::unique_ptr<InputSection<E>>> sections;
std::vector<std::unique_ptr<Subsection<E>>> subsections;
std::vector<u32> sym_to_subsec;
std::span<MachSym> mach_syms;
std::vector<Symbol<E>> local_syms;
std::vector<UnwindRecord<E>> unwind_records;
std::span<DataInCodeEntry> data_in_code_entries;
private:
void parse_sections(Context<E> &ctx);
void parse_symtab(Context<E> &ctx);
void split_subsections(Context<E> &ctx);
void parse_data_in_code(Context<E> &ctx);
LoadCommand *find_load_command(Context<E> &ctx, u32 type);
i64 find_subsection_idx(Context<E> &ctx, u32 addr);
void override_symbol(Context<E> &ctx, i64 symidx);
InputSection<E> *get_common_sec(Context<E> &ctx);
MachSection *unwind_sec = nullptr;
std::unique_ptr<MachSection> common_hdr;
InputSection<E> *common_sec = nullptr;
};
template <typename E>
class DylibFile : public InputFile<E> {
public:
static DylibFile *create(Context<E> &ctx, MappedFile<Context<E>> *mf);
void parse(Context<E> &ctx);
void resolve_symbols(Context<E> &ctx);
std::string_view install_name;
i64 dylib_idx = 0;
std::atomic_bool is_needed = false;
bool is_dylib() const override { return true; }
private:
void parse_dylib(Context<E> &ctx);
void read_trie(Context<E> &ctx, u8 *start, i64 offset = 0,
const std::string &prefix = "");
DylibFile() {
this->is_alive = true;
}
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputFile<E> &file);
//
// input-sections.cc
//
template <typename E>
class InputSection {
public:
InputSection(Context<E> &ctx, ObjectFile<E> &file, const MachSection &hdr);
void parse_relocations(Context<E> &ctx);
ObjectFile<E> &file;
const MachSection &hdr;
OutputSection<E> &osec;
std::string_view contents;
std::vector<Symbol<E> *> syms;
std::vector<Relocation<E>> rels;
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputSection<E> &sec);
template <typename E>
class Subsection {
public:
inline u64 get_addr(Context<E> &ctx) const;
std::string_view get_contents() {
assert(isec.hdr.type != S_ZEROFILL);
return isec.contents.substr(input_offset, input_size);
}
std::span<UnwindRecord<E>> get_unwind_records() {
return std::span<UnwindRecord<E>>(isec.file.unwind_records)
.subspan(unwind_offset, nunwind);
}
std::span<Relocation<E>> get_rels() const {
return std::span<Relocation<E>>(isec.rels).subspan(rel_offset, nrels);
}
void scan_relocations(Context<E> &ctx);
void apply_reloc(Context<E> &ctx, u8 *buf);
InputSection<E> &isec;
u32 input_offset = 0;
u32 input_size = 0;
u32 input_addr = 0;
u32 rel_offset = 0;
u32 nrels = 0;
u32 unwind_offset = 0;
u32 nunwind = 0;
u32 raddr = -1;
u16 p2align = 0;
std::atomic_bool is_alive = false;
};
template <typename E>
std::vector<Relocation<E>>
read_relocations(Context<E> &ctx, ObjectFile<E> &file, const MachSection &hdr);
//
// Symbol
//
enum {
NEEDS_GOT = 1 << 0,
NEEDS_STUB = 1 << 1,
NEEDS_THREAD_PTR = 1 << 2,
};
template <typename E>
struct Symbol {
Symbol() = default;
Symbol(std::string_view name) : name(name) {}
Symbol(const Symbol<E> &other) : name(other.name) {}
std::string_view name;
InputFile<E> *file = nullptr;
Subsection<E> *subsec = nullptr;
u64 value = 0;
i32 stub_idx = -1;
i32 got_idx = -1;
i32 tlv_idx = -1;
tbb::spin_mutex mu;
std::atomic_uint8_t flags = 0;
u8 is_extern : 1 = false;
u8 is_lazy : 1 = false;
u8 is_common : 1 = false;
u8 referenced_dynamically : 1 = false;
inline u64 get_addr(Context<E> &ctx) const;
inline u64 get_got_addr(Context<E> &ctx) const;
inline u64 get_tlv_addr(Context<E> &ctx) const;
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym);
//
// output-chunks.cc
//
template <typename E>
class OutputSegment {
public:
static OutputSegment<E> *
get_instance(Context<E> &ctx, std::string_view name);
void set_offset(Context<E> &ctx, i64 fileoff, u64 vmaddr);
void copy_buf(Context<E> &ctx);
std::string_view name;
SegmentCommand cmd = {};
i32 seg_idx = -1;
std::vector<Chunk<E> *> chunks;
private:
OutputSegment(std::string_view name);
};
template <typename E>
class Chunk {
public:
inline Chunk(Context<E> &ctx, std::string_view segname,
std::string_view sectname);
virtual ~Chunk() = default;
virtual void compute_size(Context<E> &ctx) {};
virtual void copy_buf(Context<E> &ctx) {}
MachSection hdr = {};
u32 sect_idx = 0;
bool is_hidden = false;
bool is_regular = false;
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const Chunk<E> &chunk);
template <typename E>
class OutputMachHeader : public Chunk<E> {
public:
OutputMachHeader(Context<E> &ctx)
: Chunk<E>(ctx, "__TEXT", "__mach_header") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
};
template <typename E>
class OutputSection : public Chunk<E> {
public:
static OutputSection<E> *
get_instance(Context<E> &ctx, std::string_view segname,
std::string_view sectname);
OutputSection(Context<E> &ctx, std::string_view segname,
std::string_view sectname)
: Chunk<E>(ctx, segname, sectname) {
this->is_regular = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
void add_subsec(Subsection<E> *subsec) {
members.push_back(subsec);
this->hdr.p2align = std::max<u32>(this->hdr.p2align, subsec->p2align);
this->hdr.attr |= subsec->isec.hdr.attr;
this->hdr.type = subsec->isec.hdr.type;
}
std::vector<Subsection<E> *> members;
};
class RebaseEncoder {
public:
RebaseEncoder();
void add(i64 seg_idx, i64 offset);
void flush();
void finish();
std::vector<u8> buf;
private:
i64 cur_seg = -1;
i64 cur_off = 0;
i64 times = 0;
};
template <typename E>
class OutputRebaseSection : public Chunk<E> {
public:
OutputRebaseSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__rebase") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
class BindEncoder {
public:
BindEncoder();
void add(i64 dylib_idx, std::string_view sym, i64 flags, i64 seg_idx,
i64 offset);
void finish();
std::vector<u8> buf;
private:
std::string_view last_sym;
i64 last_flags = -1;
i64 last_dylib = -1;
i64 last_seg = -1;
i64 last_off = -1;
};
template <typename E>
class OutputBindSection : public Chunk<E> {
public:
OutputBindSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__binding") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
template <typename E>
class OutputLazyBindSection : public Chunk<E> {
public:
OutputLazyBindSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__lazy_binding") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
}
void add(Context<E> &ctx, Symbol<E> &sym, i64 flags);
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
class ExportEncoder {
public:
void add(std::string_view name, u32 flags, u64 addr);
i64 finish();
void write_trie(u8 *buf) {
write_trie(buf, root);
}
private:
struct Entry {
std::string_view name;
u32 flags;
u64 addr;
};
struct TrieNode {
std::string_view prefix;
bool is_leaf = false;
u32 flags = 0;
u64 addr = 0;
u32 offset = -1;
std::vector<TrieNode> children;
};
static i64 common_prefix_len(std::span<Entry> entries, i64 len);
static TrieNode construct_trie(std::span<Entry> entries, i64 len);
static i64 set_offset(TrieNode &node, i64 offset);
void write_trie(u8 *buf, TrieNode &node);
TrieNode root;
std::vector<Entry> entries;
};
template <typename E>
class OutputExportSection : public Chunk<E> {
public:
OutputExportSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__export") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
private:
ExportEncoder enc;
};
template <typename E>
class OutputFunctionStartsSection : public Chunk<E> {
public:
OutputFunctionStartsSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__func_starts") {
this->is_hidden = true;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
template <typename E>
class OutputSymtabSection : public Chunk<E> {
public:
OutputSymtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__symbol_table") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
struct Entry {
Symbol<E> *sym;
i64 stroff;
};
std::vector<Entry> locals;
std::vector<Entry> globals;
std::vector<Entry> undefs;
};
template <typename E>
class OutputStrtabSection : public Chunk<E> {
public:
OutputStrtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__string_table") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
}
i64 add_string(std::string_view str);
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::string contents{1, '\0'};
};
template <typename E>
class OutputIndirectSymtabSection : public Chunk<E> {
public:
OutputIndirectSymtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__ind_sym_tab") {
this->is_hidden = true;
}
static constexpr i64 ENTRY_SIZE = 4;
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
struct Entry {
Symbol<E> *sym;
i64 symtab_idx;
};
std::vector<Entry> stubs;
std::vector<Entry> gots;
};
template <typename E>
class CodeSignatureSection : public Chunk<E> {
public:
CodeSignatureSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__code_signature") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(16U);
}
void compute_size(Context<E> &ctx) override;
void write_signature(Context<E> &ctx);
static constexpr i64 BLOCK_SIZE = 4096;
};
template <typename E>
class DataInCodeSection : public Chunk<E> {
public:
DataInCodeSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__data_in_code") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(alignof(DataInCodeEntry));
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<DataInCodeEntry> contents;
};
template <typename E>
class StubsSection : public Chunk<E> {
public:
StubsSection(Context<E> &ctx) : Chunk<E>(ctx, "__TEXT", "__stubs") {
this->hdr.p2align = std::countr_zero(2U);
this->hdr.type = S_SYMBOL_STUBS;
this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
this->hdr.reserved2 = E::stub_size;
}
void add(Context<E> &ctx, Symbol<E> *sym);
void copy_buf(Context<E> &ctx) override;
std::vector<Symbol<E> *> syms;
std::vector<u32> bind_offsets;
};
template <typename E>
class StubHelperSection : public Chunk<E> {
public:
StubHelperSection(Context<E> &ctx)
: Chunk<E>(ctx, "__TEXT", "__stub_helper") {
this->hdr.p2align = std::countr_zero(4U);
this->hdr.attr = S_ATTR_SOME_INSTRUCTIONS | S_ATTR_PURE_INSTRUCTIONS;
}
void copy_buf(Context<E> &ctx) override;
};
template <typename E>
class UnwindEncoder {
public:
std::vector<u8> encode(Context<E> &ctx, std::span<UnwindRecord<E>> records);
private:
u32 encode_personality(Context<E> &ctx, Symbol<E> *sym);
std::vector<std::span<UnwindRecord<E>>>
split_records(Context<E> &ctx, std::span<UnwindRecord<E>>);
std::vector<Symbol<E> *> personalities;
};
template <typename E>
class UnwindInfoSection : public Chunk<E> {
public:
UnwindInfoSection(Context<E> &ctx)
: Chunk<E>(ctx, "__TEXT", "__unwind_info") {
this->hdr.p2align = std::countr_zero(4U);
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
std::vector<u8> contents;
};
template <typename E>
class GotSection : public Chunk<E> {
public:
GotSection(Context<E> &ctx) : Chunk<E>(ctx, "__DATA_CONST", "__got") {
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_NON_LAZY_SYMBOL_POINTERS;
}
void add(Context<E> &ctx, Symbol<E> *sym);
void copy_buf(Context<E> &ctx) override;
std::vector<Symbol<E> *> syms;
};
template <typename E>
class LazySymbolPtrSection : public Chunk<E> {
public:
LazySymbolPtrSection(Context<E> &ctx)
: Chunk<E>(ctx, "__DATA", "__la_symbol_ptr") {
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_LAZY_SYMBOL_POINTERS;
}
void copy_buf(Context<E> &ctx) override;
};
template <typename E>
class ThreadPtrsSection : public Chunk<E> {
public:
ThreadPtrsSection(Context<E> &ctx)
: Chunk<E>(ctx, "__DATA", "__thread_ptrs") {
this->hdr.p2align = std::countr_zero(8U);
this->hdr.type = S_THREAD_LOCAL_VARIABLE_POINTERS;
}
void add(Context<E> &ctx, Symbol<E> *sym);
void copy_buf(Context<E> &ctx) override;
std::vector<Symbol<E> *> syms;
};
//
// mapfile.cc
//
template <typename E>
void print_map(Context<E> &ctx);
//
// dumper.cc
//
void dump_file(std::string path);
//
// output-file.cc
//
template <typename E>
class OutputFile {
public:
static std::unique_ptr<OutputFile>
open(Context<E> &ctx, std::string path, i64 filesize, i64 perm);
virtual void close(Context<E> &ctx) = 0;
virtual ~OutputFile() {}
u8 *buf = nullptr;
std::string path;
i64 filesize;
bool is_mmapped;
bool is_unmapped = false;
protected:
OutputFile(std::string path, i64 filesize, bool is_mmapped)
: path(path), filesize(filesize), is_mmapped(is_mmapped) {}
};
//
// yaml.cc
//
struct YamlNode {
std::variant<std::string_view,
std::vector<YamlNode>,
std::map<std::string_view, YamlNode>> data;
};
struct YamlError {
std::string msg;
i64 pos;
};
std::variant<std::vector<YamlNode>, YamlError>
parse_yaml(std::string_view str);
//
// tapi.cc
//
struct TextDylib {
std::string_view uuid;
std::string_view install_name;
std::string_view current_version = "1.0";
std::string_view parent_umbrella;
std::vector<std::string_view> reexported_libs;
std::vector<std::string_view> exports;
};
template <typename E>
TextDylib parse_tbd(Context<E> &ctx, MappedFile<Context<E>> *mf);
//
// cmdline.cc
//
template <typename E>
void parse_nonpositional_args(Context<E> &ctx,
std::vector<std::string> &remaining);
//
// dead-strip.cc
//
template <typename E>
void dead_strip(Context<E> &ctx);
//
// main.cc
//
template <typename E>
struct Context {
Context() {
text_seg = OutputSegment<E>::get_instance(*this, "__TEXT");
data_const_seg = OutputSegment<E>::get_instance(*this, "__DATA_CONST");
data_seg = OutputSegment<E>::get_instance(*this, "__DATA");
linkedit_seg = OutputSegment<E>::get_instance(*this, "__LINKEDIT");
text = OutputSection<E>::get_instance(*this, "__TEXT", "__text");
data = OutputSection<E>::get_instance(*this, "__DATA", "__data");
bss = OutputSection<E>::get_instance(*this, "__DATA", "__bss");
cstring = OutputSection<E>::get_instance(*this, "__TEXT", "__cstring");
common = OutputSection<E>::get_instance(*this, "__DATA", "__common");
bss->hdr.type = S_ZEROFILL;
cstring->hdr.type = S_CSTRING_LITERALS;
common->hdr.type = S_ZEROFILL;
}
Context(const Context<E> &) = delete;
void checkpoint() {
if (has_error) {
cleanup();
_exit(1);
}
}
// Command-line arguments
struct {
bool ObjC = false;
bool adhoc_codesign = true;
bool color_diagnostics = false;
bool dead_strip = true;
bool dead_strip_dylibs = false;
bool deduplicate = true;
bool demangle = false;
bool dylib = false;
bool dynamic = true;
bool fatal_warnings = false;
bool noinhibit_exec = false;
bool trace = false;
i64 arch = CPU_TYPE_ARM64;
i64 headerpad = 256;
i64 pagezero_size = 0;
i64 platform = PLATFORM_MACOS;
i64 platform_min_version = 0;
i64 platform_sdk_version = 0;
std::string chroot;
std::string entry = "_main";
std::string map;
std::string output = "a.out";
std::vector<std::string> framework_paths;
std::vector<std::string> library_paths;
std::vector<std::string> rpath;
std::vector<std::string> syslibroot;
} arg;
std::vector<std::string_view> cmdline_args;
u32 output_type = MH_EXECUTE;
bool has_error = false;
tbb::concurrent_hash_map<std::string_view, Symbol<E>> symbol_map;
std::unique_ptr<OutputFile<E>> output_file;
u8 *buf;
tbb::concurrent_vector<std::unique_ptr<ObjectFile<E>>> obj_pool;
tbb::concurrent_vector<std::unique_ptr<DylibFile<E>>> dylib_pool;
tbb::concurrent_vector<std::unique_ptr<u8[]>> string_pool;
tbb::concurrent_vector<std::unique_ptr<MappedFile<Context<E>>>> mf_pool;
std::vector<std::unique_ptr<OutputSection<E>>> osec_pool;
tbb::concurrent_vector<std::unique_ptr<TimerRecord>> timer_records;
std::vector<ObjectFile<E> *> objs;
std::vector<DylibFile<E> *> dylibs;
OutputSegment<E> *text_seg = nullptr;
OutputSegment<E> *data_const_seg = nullptr;
OutputSegment<E> *data_seg = nullptr;
OutputSegment<E> *linkedit_seg = nullptr;
std::vector<std::unique_ptr<OutputSegment<E>>> segments;
std::vector<Chunk<E> *> chunks;
OutputMachHeader<E> mach_hdr{*this};
StubsSection<E> stubs{*this};
StubHelperSection<E> stub_helper{*this};
UnwindInfoSection<E> unwind_info{*this};
GotSection<E> got{*this};
LazySymbolPtrSection<E> lazy_symbol_ptr{*this};
CodeSignatureSection<E> code_sig{*this};
DataInCodeSection<E> data_in_code{*this};
ThreadPtrsSection<E> thread_ptrs{*this};
OutputRebaseSection<E> rebase{*this};
OutputBindSection<E> bind{*this};
OutputLazyBindSection<E> lazy_bind{*this};
OutputExportSection<E> export_{*this};
OutputFunctionStartsSection<E> function_starts{*this};
OutputSymtabSection<E> symtab{*this};
OutputIndirectSymtabSection<E> indir_symtab{*this};
OutputStrtabSection<E> strtab{*this};
OutputSection<E> *text = nullptr;
OutputSection<E> *data = nullptr;
OutputSection<E> *bss = nullptr;
OutputSection<E> *cstring = nullptr;
OutputSection<E> *common = nullptr;
};
int main(int argc, char **argv);
//
// Inline functions
//
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputSection<E> &sec) {
out << sec.file << "(" << sec.hdr.get_segname() << ","
<< sec.hdr.get_sectname() << ")";
return out;
}
template <typename E>
u64 Subsection<E>::get_addr(Context<E> &ctx) const {
return ctx.arg.pagezero_size + raddr;
}
template <typename E>
u64 Symbol<E>::get_addr(Context<E> &ctx) const {
if (subsec)
return subsec->get_addr(ctx) + value;
if (stub_idx != -1)
return ctx.stubs.hdr.addr + stub_idx * E::stub_size;
return value;
}
template <typename E>
u64 Symbol<E>::get_got_addr(Context<E> &ctx) const {
assert(got_idx != -1);
return ctx.got.hdr.addr + got_idx * E::word_size;
}
template <typename E>
u64 Symbol<E>::get_tlv_addr(Context<E> &ctx) const {
assert(tlv_idx != -1);
return ctx.thread_ptrs.hdr.addr + tlv_idx * E::word_size;
}
template <typename E>
inline Symbol<E> *get_symbol(Context<E> &ctx, std::string_view name) {
typename decltype(ctx.symbol_map)::const_accessor acc;
ctx.symbol_map.insert(acc, {name, Symbol<E>(name)});
return (Symbol<E> *)(&acc->second);
}
template <typename E>
inline std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym) {
out << sym.name;
return out;
}
template <typename E>
Chunk<E>::Chunk(Context<E> &ctx, std::string_view segname,
std::string_view sectname) {
ctx.chunks.push_back(this);
hdr.set_segname(segname);
hdr.set_sectname(sectname);
}
template <typename E>
u64 UnwindRecord<E>::get_func_raddr(Context<E> &ctx) const {
return subsec->raddr + offset;
}
} // namespace mold::macho
+622
View File
@@ -0,0 +1,622 @@
#include "mold.h"
#include "../archive-file.h"
namespace mold::macho {
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputFile<E> &file) {
if (file.archive_name.empty())
out << path_clean(file.mf->name);
else
out << path_clean(file.archive_name) << "(" << path_clean(file.mf->name) + ")";
return out;
}
template <typename E>
ObjectFile<E> *
ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
ObjectFile<E> *obj = new ObjectFile<E>;
obj->mf = mf;
obj->archive_name = archive_name;
obj->is_alive = archive_name.empty();
ctx.obj_pool.emplace_back(obj);
return obj;
};
template <typename E>
void ObjectFile<E>::parse(Context<E> &ctx) {
parse_sections(ctx);
parse_symtab(ctx);
split_subsections(ctx);
parse_data_in_code(ctx);
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec)
isec->parse_relocations(ctx);
if (unwind_sec)
parse_compact_unwind(ctx, *unwind_sec);
}
template <typename E>
void ObjectFile<E>::parse_sections(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
// Read all but a symtab section
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
if (lc.cmd != LC_SEGMENT_64)
continue;
SegmentCommand &cmd = *(SegmentCommand *)&lc;
MachSection *mach_sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
for (MachSection &msec : std::span(mach_sec, mach_sec + cmd.nsects)) {
sections.push_back(nullptr);
if (msec.match("__LD", "__compact_unwind")) {
unwind_sec = &msec;
continue;
}
if (msec.attr & S_ATTR_DEBUG)
continue;
sections.back().reset(new InputSection<E>(ctx, *this, msec));
}
}
}
template <typename E>
void ObjectFile<E>::parse_symtab(Context<E> &ctx) {
SymtabCommand *cmd = (SymtabCommand *)find_load_command(ctx, LC_SYMTAB);
if (!cmd)
return;
mach_syms = {(MachSym *)(this->mf->data + cmd->symoff), cmd->nsyms};
this->syms.reserve(mach_syms.size());
i64 nlocal = 0;
for (MachSym &msym : mach_syms)
if (!msym.ext)
nlocal++;
local_syms.reserve(nlocal);
for (MachSym &msym : mach_syms) {
std::string_view name = (char *)(this->mf->data + cmd->stroff + msym.stroff);
if (msym.ext) {
this->syms.push_back(get_symbol(ctx, name));
} else {
local_syms.emplace_back(name);
this->syms.push_back(&local_syms.back());
}
}
}
struct SplitRegion {
u32 offset;
u32 size;
u32 symidx;
bool is_alt_entry;
};
template <typename E>
struct SplitInfo {
SplitInfo(InputSection<E> *isec) : isec(isec) {}
InputSection<E> *isec;
std::vector<SplitRegion> regions;
};
template <typename E>
static std::vector<SplitInfo<E>> split(Context<E> &ctx, ObjectFile<E> &file) {
std::vector<SplitInfo<E>> vec;
for (std::unique_ptr<InputSection<E>> &isec : file.sections)
vec.push_back({isec.get()});
for (i64 i = 0; i < file.mach_syms.size(); i++) {
MachSym &msym = file.mach_syms[i];
if (msym.type == N_SECT && file.sections[msym.sect - 1]) {
SplitRegion r;
r.offset = msym.value - file.sections[msym.sect - 1]->hdr.addr;
r.symidx = i;
r.is_alt_entry = (msym.desc & N_ALT_ENTRY);
vec[msym.sect - 1].regions.push_back(r);
}
}
std::erase_if(vec, [](const SplitInfo<E> &info) { return !info.isec; });
sort(vec, [](const SplitInfo<E> &a, const SplitInfo<E> &b) {
return a.isec->hdr.addr < b.isec->hdr.addr;
});
for (SplitInfo<E> &info : vec) {
std::vector<SplitRegion> &r = info.regions;
if (r.empty()) {
r.push_back({0, (u32)info.isec->hdr.size, (u32)-1, false});
continue;
}
sort(r, [](const SplitRegion &a, const SplitRegion &b) {
return a.offset < b.offset;
});
if (r[0].offset > 0)
r.insert(r.begin(), {0, r[0].offset, (u32)-1, false});
for (i64 i = 1; i < r.size(); i++)
if (r[i - 1].offset == r[i].offset)
r[i++].is_alt_entry = true;
i64 last = -1;
for (i64 i = 0; i < r.size(); i++) {
if (!r[i].is_alt_entry) {
if (last != -1)
r[last].size = r[i].offset - r[last].offset;
last = i;
}
}
if (last != -1)
r[last].size = info.isec->hdr.size - r[last].offset;
}
return vec;
}
template <typename E>
void ObjectFile<E>::split_subsections(Context<E> &ctx) {
sym_to_subsec.resize(mach_syms.size());
for (SplitInfo<E> &info : split(ctx, *this)) {
InputSection<E> &isec = *info.isec;
for (SplitRegion &r : info.regions) {
if (!r.is_alt_entry) {
Subsection<E> *subsec = new Subsection<E>{
.isec = isec,
.input_offset = r.offset,
.input_size = r.size,
.input_addr = (u32)(isec.hdr.addr + r.offset),
.p2align = (u8)isec.hdr.p2align,
};
subsections.emplace_back(subsec);
}
if (r.symidx != -1)
sym_to_subsec[r.symidx] = subsections.size() - 1;
}
}
for (i64 i = 0; i < mach_syms.size(); i++)
if (!mach_syms[i].ext)
override_symbol(ctx, i);
}
template <typename E>
void ObjectFile<E>::parse_data_in_code(Context<E> &ctx) {
if (auto *cmd = (LinkEditDataCommand *)find_load_command(ctx, LC_DATA_IN_CODE))
data_in_code_entries = {
(DataInCodeEntry *)(this->mf->data + cmd->dataoff),
cmd->datasize / sizeof(DataInCodeEntry)
};
}
template <typename E>
LoadCommand *ObjectFile<E>::find_load_command(Context<E> &ctx, u32 type) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
if (lc.cmd == type)
return &lc;
p += lc.cmdsize;
}
return nullptr;
}
template <typename E>
i64 ObjectFile<E>::find_subsection_idx(Context<E> &ctx, u32 addr) {
auto it = std::upper_bound(
subsections.begin(), subsections.end(), addr,
[&](u32 addr, const std::unique_ptr<Subsection<E>> &subsec) {
return addr < subsec->input_addr;
});
if (it == subsections.begin())
return -1;
return it - subsections.begin() - 1;
}
template <typename E>
Subsection<E> *ObjectFile<E>::find_subsection(Context<E> &ctx, u32 addr) {
i64 i = find_subsection_idx(ctx, addr);
return (i == -1) ? nullptr : subsections[i].get();
}
template <typename E>
void ObjectFile<E>::parse_compact_unwind(Context<E> &ctx, MachSection &hdr) {
if (hdr.size % sizeof(CompactUnwindEntry))
Fatal(ctx) << *this << ": invalid __compact_unwind section size";
i64 num_entries = hdr.size / sizeof(CompactUnwindEntry);
unwind_records.reserve(num_entries);
CompactUnwindEntry *src = (CompactUnwindEntry *)(this->mf->data + hdr.offset);
// Read compact unwind entries
for (i64 i = 0; i < num_entries; i++)
unwind_records.emplace_back(src[i].code_len, src[i].encoding);
// Read relocations
MachRel *mach_rels = (MachRel *)(this->mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++) {
MachRel &r = mach_rels[i];
if (r.offset >= hdr.size)
Fatal(ctx) << *this << ": relocation offset too large: " << i;
i64 idx = r.offset / sizeof(CompactUnwindEntry);
UnwindRecord<E> &dst = unwind_records[idx];
auto error = [&] {
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
};
switch (r.offset % sizeof(CompactUnwindEntry)) {
case offsetof(CompactUnwindEntry, code_start): {
if (r.is_pcrel || r.p2size != 3 || r.is_extern || r.type)
error();
Subsection<E> *target = find_subsection(ctx, src[idx].code_start);
if (!target)
error();
dst.subsec = target;
dst.offset = src[idx].code_start - target->input_addr;
break;
}
case offsetof(CompactUnwindEntry, personality):
if (r.is_pcrel || r.p2size != 3 || !r.is_extern || r.type)
error();
dst.personality = this->syms[r.idx];
break;
case offsetof(CompactUnwindEntry, lsda): {
if (r.is_pcrel || r.p2size != 3 || r.is_extern || r.type)
error();
i32 addr = *(i32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
Subsection<E> *target = find_subsection(ctx, addr);
if (!target)
error();
dst.lsda = target;
dst.lsda_offset = addr - target->input_addr;
break;
}
default:
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
}
}
for (i64 i = 0; i < num_entries; i++)
if (!unwind_records[i].subsec)
Fatal(ctx) << ": __compact_unwind: missing relocation at " << i;
// Sort unwind entries by offset
sort(unwind_records, [](const UnwindRecord<E> &a, const UnwindRecord<E> &b) {
return std::tuple(a.subsec->input_addr, a.offset) <
std::tuple(b.subsec->input_addr, b.offset);
});
// Associate unwind entries to subsections
for (i64 i = 0; i < num_entries;) {
Subsection<E> &subsec = *unwind_records[i].subsec;
subsec.unwind_offset = i;
i64 j = i + 1;
while (j < num_entries && unwind_records[j].subsec == &subsec)
j++;
subsec.nunwind = j - i;
i = j;
}
}
// Symbols with higher priorities overwrites symbols with lower priorities.
// Here is the list of priorities, from the highest to the lowest.
//
// 1. Strong defined symbol
// 2. Weak defined symbol
// 3. Strong defined symbol in a DSO
// 4. Weak defined symbol in a DSO
// 5. Strong or weak defined symbol in an archive
// 6. Common symbol
// 7. Unclaimed (nonexistent) symbol
//
// Ties are broken by file priority.
template <typename E>
static u64 get_rank(InputFile<E> *file, MachSym &msym, bool is_lazy) {
if (msym.is_common())
return (6 << 24) + file->priority;
if (is_lazy)
return (5 << 24) + file->priority;
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
static u64 get_rank(Symbol<E> &sym) {
InputFile<E> *file = sym.file;
if (!file)
return 7 << 24;
if (sym.is_common)
return (6 << 24) + file->priority;
if (!file->archive_name.empty())
return (5 << 24) + file->priority;
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
void ObjectFile<E>::override_symbol(Context<E> &ctx, i64 idx) {
Symbol<E> &sym = *this->syms[idx];
MachSym &msym = mach_syms[idx];
sym.file = this;
sym.is_extern = msym.ext;
sym.is_lazy = false;
switch (msym.type) {
case N_UNDF:
assert(msym.is_common());
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = true;
break;
case N_ABS:
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = false;
break;
case N_SECT:
sym.subsec = subsections[sym_to_subsec[idx]].get();
sym.value = msym.value - sym.subsec->input_addr;
sym.is_common = false;
break;
default:
Fatal(ctx) << sym << ": unknown symbol type: " << (u64)msym.type;
}
}
template <typename E>
void ObjectFile<E>::resolve_regular_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext || msym.is_undef())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, msym, false) < get_rank(sym))
override_symbol(ctx, i);
}
}
template <typename E>
void ObjectFile<E>::resolve_lazy_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext || msym.is_undef() || msym.is_common())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, msym, true) < get_rank(sym)) {
sym.file = this;
sym.subsec = nullptr;
sym.value = 0;
sym.is_extern = false;
sym.is_lazy = true;
sym.is_common = false;
}
}
}
template <typename E>
bool ObjectFile<E>::is_objc_object(Context<E> &ctx) {
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec->hdr.match("__DATA", "__objc_catlist") ||
isec->hdr.match("__TEXT", "__swift"))
return true;
for (i64 i = 0; i < this->syms.size(); i++)
if (!mach_syms[i].is_undef() && mach_syms[i].ext &&
this->syms[i]->name.starts_with("_OBJC_CLASS_$_"))
return true;
return false;
}
template <typename E>
std::vector<ObjectFile<E> *> ObjectFile<E>::mark_live_objects(Context<E> &ctx) {
std::vector<ObjectFile<E> *> vec;
assert(this->is_alive);
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext)
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (msym.is_undef()) {
if (sym.file && !sym.file->is_alive.exchange(true))
vec.push_back((ObjectFile<E> *)sym.file);
continue;
}
if (get_rank(this, msym, false) < get_rank(sym))
override_symbol(ctx, i);
}
return vec;
}
template <typename E>
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> &sym = *this->syms[i];
MachSym &msym = mach_syms[i];
if (sym.file == this && sym.is_common) {
InputSection<E> *isec = get_common_sec(ctx);
Subsection<E> *subsec = new Subsection<E>{
.isec = *isec,
.input_size = (u32)msym.value,
.p2align = (u8)msym.p2align,
};
subsections.emplace_back(subsec);
sym.subsec = subsec;
sym.value = 0;
sym.is_common = false;
}
}
}
template <typename E>
void ObjectFile<E>::check_duplicate_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> *sym = this->syms[i];
MachSym &msym = mach_syms[i];
if (sym && !msym.is_undef() && !msym.is_common() && sym->file != this)
Error(ctx) << "duplicate symbol: " << *this << ": " << *sym->file
<< ": " << *sym;
}
}
template <typename E>
InputSection<E> *ObjectFile<E>::get_common_sec(Context<E> &ctx) {
if (!common_sec) {
MachSection *hdr = new MachSection;
common_hdr.reset(hdr);
memset(hdr, 0, sizeof(*hdr));
hdr->set_segname("__DATA");
hdr->set_sectname("__common");
hdr->type = S_ZEROFILL;
common_sec = new InputSection<E>(ctx, *this, *hdr);
sections.emplace_back(common_sec);
}
return common_sec;
}
template <typename E>
DylibFile<E> *DylibFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
DylibFile<E> *dylib = new DylibFile<E>;
dylib->mf = mf;
ctx.dylib_pool.emplace_back(dylib);
return dylib;
};
template <typename E>
void DylibFile<E>::read_trie(Context<E> &ctx, u8 *start, i64 offset,
const std::string &prefix) {
u8 *buf = start + offset;
if (*buf) {
read_uleb(buf); // size
read_uleb(buf); // flags
read_uleb(buf); // addr
this->syms.push_back(get_symbol(ctx, prefix));
} else {
buf++;
}
i64 nchild = *buf++;
for (i64 i = 0; i < nchild; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(ctx, start, off, prefix + suffix);
}
}
template <typename E>
void DylibFile<E>::parse_dylib(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
switch (lc.cmd) {
case LC_ID_DYLIB: {
DylibCommand &cmd = *(DylibCommand *)p;
install_name = (char *)p + cmd.nameoff;
break;
}
case LC_DYLD_INFO_ONLY: {
DyldInfoCommand &cmd = *(DyldInfoCommand *)p;
if (cmd.export_off)
read_trie(ctx, this->mf->data + cmd.export_off);
break;
}
case LC_DYLD_EXPORTS_TRIE: {
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)p;
read_trie(ctx, this->mf->data + cmd.dataoff);
break;
}
}
p += lc.cmdsize;
}
}
template <typename E>
void DylibFile<E>::parse(Context<E> &ctx) {
switch (get_file_type(this->mf)) {
case FileType::TAPI: {
TextDylib tbd = parse_tbd(ctx, this->mf);
for (std::string_view sym : tbd.exports)
this->syms.push_back(get_symbol(ctx, sym));
install_name = tbd.install_name;
break;
}
case FileType::MACH_DYLIB:
parse_dylib(ctx);
break;
default:
Fatal(ctx) << this->mf->name << ": is not a dylib";
}
}
template <typename E>
void DylibFile<E>::resolve_symbols(Context<E> &ctx) {
for (Symbol<E> *sym : this->syms) {
std::scoped_lock lock(sym->mu);
if (sym->file && sym->file->priority < this->priority)
continue;
sym->file = this;
sym->is_extern = true;
}
}
#define INSTANTIATE(E) \
template class ObjectFile<E>; \
template class DylibFile<E>; \
template std::ostream &operator<<(std::ostream &, const InputFile<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
File diff suppressed because it is too large. Load diff
+120
View File
@@ -0,0 +1,120 @@
// This file implements the same functionality as elf/output-file.cc.
// We have two different implemetntations for ELF and Mach-O because
// we implemented a special optimization for ELF or Linux.
//
// On Linux, it is faster to write to an existing file than creating a
// new file and writing to it. So, we overwrite an existing executable
// if exists.
//
// On macOS, it looks like the system does not assume that an executable
// is mutated once it is created. Due to some code signing mechanism or
// something, we always have to create a fresh file.
#include "mold.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
template <typename E>
class MemoryMappedOutputFile : public OutputFile<E> {
public:
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, true) {
std::string tmp_path = filepath(path).parent_path() / ".mold-XXXXXX";
output_tmpfile = (char *)save_string(ctx, tmp_path).data();
i64 fd = mkstemp(output_tmpfile);
if (fd == -1)
Fatal(ctx) << "cannot open " << output_tmpfile << ": " << errno_string();
if (ftruncate(fd, filesize))
Fatal(ctx) << "ftruncate failed: " << errno_string();
if (fchmod(fd, perm) == -1)
Fatal(ctx) << "fchmod failed: " << errno_string();
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
::close(fd);
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
munmap(this->buf, this->filesize);
if (rename(output_tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << errno_string();
output_tmpfile = nullptr;
}
};
template <typename E>
class MallocOutputFile : public OutputFile<E> {
public:
MallocOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, false), perm(perm) {
this->buf = (u8 *)mmap(NULL, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << "mmap failed: " << errno_string();
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
if (this->path == "-") {
fwrite(this->buf, this->filesize, 1, stdout);
fclose(stdout);
return;
}
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
FILE *fp = fdopen(fd, "w");
fwrite(this->buf, this->filesize, 1, fp);
fclose(fp);
}
private:
i64 perm;
};
template <typename E>
std::unique_ptr<OutputFile<E>>
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
bool is_special = false;
if (path == "-") {
is_special = true;
} else {
struct stat st;
if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) != S_IFREG)
is_special = true;
}
std::unique_ptr<OutputFile> file;
if (is_special)
file = std::make_unique<MallocOutputFile<E>>(ctx, path, filesize, perm);
else
file = std::make_unique<MemoryMappedOutputFile<E>>(ctx, path, filesize, perm);
return file;
}
#define INSTANTIATE(E) \
template class OutputFile<E>
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+127
View File
@@ -0,0 +1,127 @@
#include "mold.h"
#include <optional>
namespace mold::macho {
static std::vector<YamlNode>
get_vector(YamlNode &node, std::string_view key) {
if (auto *map = std::get_if<std::map<std::string_view, YamlNode>>(&node.data))
if (auto it = map->find(key); it != map->end())
if (auto *vec = std::get_if<std::vector<YamlNode>>(&it->second.data))
return *vec;
return {};
}
static std::optional<std::string_view>
get_string(YamlNode &node, std::string_view key) {
if (auto *map = std::get_if<std::map<std::string_view, YamlNode>>(&node.data))
if (auto it = map->find(key); it != map->end())
if (auto *str = std::get_if<std::string_view>(&it->second.data))
return *str;
return {};
}
static bool contains(const std::vector<YamlNode> &vec, std::string_view key) {
for (const YamlNode &mem : vec)
if (const std::string_view *s = std::get_if<std::string_view>(&mem.data))
if (*s == key)
return true;
return false;
}
static std::optional<TextDylib> to_tbd(YamlNode &node, std::string_view arch) {
if (!contains(get_vector(node, "targets"), arch))
return {};
TextDylib tbd;
for (YamlNode &mem : get_vector(node, "uuids"))
if (auto target = get_string(mem, "target"))
if (*target == arch)
if (auto value = get_string(mem, "value"))
tbd.uuid = *value;
if (auto val = get_string(node, "install-name"))
tbd.install_name = *val;
if (auto val = get_string(node, "current-version"))
tbd.current_version = *val;
for (YamlNode &mem : get_vector(node, "parent-umbrella"))
if (contains(get_vector(mem, "targets"), arch))
if (auto val = get_string(mem, "umbrella"))
tbd.parent_umbrella = *val;
for (YamlNode &mem : get_vector(node, "reexported-libraries"))
if (contains(get_vector(mem, "targets"), arch))
for (YamlNode &mem : get_vector(mem, "libraries"))
if (auto *lib = std::get_if<std::string_view>(&mem.data))
tbd.reexported_libs.push_back(*lib);
for (std::string_view key : {"exports", "reexports"})
for (YamlNode &mem : get_vector(node, key))
if (contains(get_vector(mem, "targets"), arch))
for (YamlNode &mem : get_vector(mem, "symbols"))
if (auto *sym = std::get_if<std::string_view>(&mem.data))
tbd.exports.push_back(*sym);
return tbd;
}
template <typename E>
static TextDylib squash(Context<E> &ctx, std::span<TextDylib> tbds) {
std::unordered_map<std::string_view, TextDylib> map;
TextDylib main = std::move(tbds[0]);
for (TextDylib &tbd : tbds.subspan(1))
map[tbd.install_name] = std::move(tbd);
std::vector<std::string_view> libs;
for (std::string_view lib : main.reexported_libs) {
auto it = map.find(lib);
if (it != map.end())
append(main.exports, it->second.exports);
else
libs.push_back(lib);
}
main.reexported_libs = std::move(libs);
return main;
}
template <typename E>
static TextDylib parse(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string_view arch) {
std::string_view contents = mf->get_contents();
std::variant<std::vector<YamlNode>, YamlError> res = parse_yaml(contents);
if (YamlError *err = std::get_if<YamlError>(&res)) {
i64 lineno = std::count(contents.begin(), contents.begin() + err->pos, '\n');
Fatal(ctx) << mf->name << ":" << (lineno + 1)
<< ": YAML parse error: " << err->msg;
}
std::vector<YamlNode> &nodes = std::get<std::vector<YamlNode>>(res);
if (nodes.empty())
Fatal(ctx) << mf->name << ": malformed TBD file";
std::vector<TextDylib> vec;
for (YamlNode &node : nodes)
if (std::optional<TextDylib> dylib = to_tbd(node, arch))
vec.push_back(*dylib);
return squash(ctx, vec);
}
template <>
TextDylib parse_tbd(Context<ARM64> &ctx, MappedFile<Context<ARM64>> *mf) {
return parse(ctx, mf, "arm64-macos");
}
template <>
TextDylib parse_tbd(Context<X86_64> &ctx, MappedFile<Context<X86_64>> *mf) {
return parse(ctx, mf, "x86_64-macos");
}
} // namespace mold::macho
+360
View File
@@ -0,0 +1,360 @@
#include "mold.h"
#include <optional>
namespace mold::macho {
struct Token {
enum { STRING = 1, INDENT, DEDENT, END };
u8 kind = 0;
std::string_view str;
};
class YamlParser {
public:
YamlParser(std::string_view input) : input(input) {}
std::variant<std::vector<YamlNode>, YamlError> parse();
private:
std::optional<YamlError> tokenize();
void tokenize_bare_string(std::string_view &str);
std::optional<YamlError> tokenize_list(std::string_view &str);
std::optional<YamlError> tokenize_string(std::string_view &str, char end);
std::variant<YamlNode, YamlError> parse_element(std::span<Token> &tok);
std::variant<YamlNode, YamlError> parse_list(std::span<Token> &tok);
std::variant<YamlNode, YamlError> parse_map(std::span<Token> &tok);
std::variant<YamlNode, YamlError> parse_flow_element(std::span<Token> &tok);
std::variant<YamlNode, YamlError> parse_flow_list(std::span<Token> &tok);
std::string_view input;
std::vector<Token> tokens;
};
std::optional<YamlError> YamlParser::tokenize() {
std::vector<i64> indents = {0};
auto indent = [&](std::string_view str, i64 depth) {
tokens.push_back({Token::INDENT, str});
indents.push_back(depth);
};
auto dedent = [&](std::string_view str) {
assert(indents.size() > 1);
tokens.push_back({Token::DEDENT, str});
indents.pop_back();
};
auto skip_line = [](std::string_view &str) {
size_t pos = str.find('\n');
if (pos == str.npos)
str = str.substr(str.size());
else
str = str.substr(pos + 1);
};
auto tokenize_line = [&](std::string_view &str) -> std::optional<YamlError> {
const char *start = str.data();
if (str.starts_with("---")) {
while (indents.size() > 1)
dedent(str);
tokens.push_back({Token::END, str.substr(0, 3)});
skip_line(str);
return {};
}
if (str.starts_with("...")) {
while (indents.size() > 1)
dedent(str);
tokens.push_back({Token::END, str.substr(0, 3)});
str = str.substr(str.size());
return {};
}
size_t pos = str.find_first_not_of(" \t");
if (pos == str.npos || str[pos] == '#' || str[pos] == '\n') {
skip_line(str);
return {};
}
if (indents.back() != pos) {
if (indents.back() < pos) {
indent(str, pos);
} else {
while (indents.back() != pos) {
if (pos < indents.back())
dedent(str);
else
return YamlError{"bad indentation", start - input.data()};
}
}
}
str = str.substr(pos);
while (!str.empty()) {
if (str[0] == '\n') {
str = str.substr(1);
return {};
}
if (str.starts_with("- ")) {
tokens.push_back({'-', str.substr(0, 1)});
size_t pos = str.find_first_not_of(" \t", 1);
if (pos == str.npos || str[pos] == '\n') {
skip_line(str);
return {};
}
str = str.substr(pos);
indent(str, str.data() - start);
continue;
}
if (str.starts_with('['))
return tokenize_list(str);
if (str.starts_with('\'')) {
if (std::optional<YamlError> err = tokenize_string(str, '\''))
return err;
continue;
}
if (str.starts_with('"')) {
if (std::optional<YamlError> err = tokenize_string(str, '"'))
return err;
continue;
}
if (str.starts_with('#')) {
skip_line(str);
return {};
}
if (str.starts_with(':')) {
tokens.push_back({':', str.substr(0, 1)});
size_t pos = str.find_first_not_of(" \t", 1);
if (pos == str.npos || str[pos] == '\n') {
skip_line(str);
return {};
}
str = str.substr(pos);
continue;
}
tokenize_bare_string(str);
}
return {};
};
std::string_view str = input;
while (!str.empty())
if (std::optional<YamlError> err = tokenize_line(str))
return err;
return {};
}
std::optional<YamlError> YamlParser::tokenize_list(std::string_view &str) {
const char *start = str.data();
tokens.push_back({'[', str.substr(0, 1)});
str = str.substr(1);
while (!str.empty() && str[0] != ']') {
if (size_t pos = str.find_first_not_of(" \t\n"); pos) {
str = str.substr(pos);
continue;
}
if (str.starts_with('\'')) {
if (std::optional<YamlError> err = tokenize_string(str, '\''))
return err;
continue;
}
if (str.starts_with('"')) {
if (std::optional<YamlError> err = tokenize_string(str, '"'))
return err;
continue;
}
if (str.starts_with(',')) {
tokens.push_back({',', str.substr(0, 1)});
str = str.substr(1);
continue;
}
tokenize_bare_string(str);
}
if (str.empty())
return YamlError{"unclosed list", start - input.data()};
const char *bracket = str.data();
tokens.push_back({']', str.substr(0, 1)});
str = str.substr(1);
while (!str.empty() && (str[0] == ' ' || str[0] == '\t'))
str = str.substr(1);
if (str.empty() || str[0] != '\n')
return YamlError{"no newline after ']'", bracket - input.data()};
str = str.substr(1);
return {};
}
std::optional<YamlError>
YamlParser::tokenize_string(std::string_view &str, char end) {
const char *start = str.data();
size_t pos = str.find(end, 1);
if (pos == str.npos)
return YamlError{"unterminated string literal", start - input.data()};
tokens.push_back({Token::STRING, str.substr(1, pos - 1)});
str = str.substr(pos + 1);
return {};
}
void
YamlParser::tokenize_bare_string(std::string_view &str) {
size_t pos = str.find_first_not_of(
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-/.");
if (pos == str.npos)
pos = str.size();
tokens.push_back({Token::STRING, str.substr(0, pos)});
str = str.substr(pos);
}
std::variant<std::vector<YamlNode>, YamlError> YamlParser::parse() {
if (std::optional<YamlError> err = tokenize())
return *err;
std::span<Token> tok(tokens);
std::vector<YamlNode> vec;
while (!tok.empty()) {
if (tok[0].kind == Token::END) {
tok = tok.subspan(1);
continue;
}
std::variant<YamlNode, YamlError> elem = parse_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
vec.push_back(std::get<YamlNode>(elem));
if (tok[0].kind != Token::END)
return YamlError{"stray token", tok[0].str.data() - input.data()};
}
return vec;
}
std::variant<YamlNode, YamlError>
YamlParser::parse_element(std::span<Token> &tok) {
if (tok[0].kind == Token::INDENT) {
tok = tok.subspan(1);
std::variant<YamlNode, YamlError> elem = parse_element(tok);
assert(tok[0].kind == Token::DEDENT);
tok = tok.subspan(1);
return elem;
}
if (tok[0].kind == '-')
return parse_list(tok);
if (tok.size() > 2 && tok[0].kind == Token::STRING && tok[1].kind == ':')
return parse_map(tok);
return parse_flow_element(tok);
}
std::variant<YamlNode, YamlError>
YamlParser::parse_list(std::span<Token> &tok) {
std::vector<YamlNode> vec;
while (tok[0].kind != Token::END && tok[0].kind != Token::DEDENT) {
if (tok[0].kind != '-')
return YamlError{"list element expected", tok[0].str.data() - input.data()};
tok = tok.subspan(1);
std::variant<YamlNode, YamlError> elem = parse_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
vec.push_back(std::get<YamlNode>(elem));
}
return YamlNode{vec};
}
std::variant<YamlNode, YamlError>
YamlParser::parse_map(std::span<Token> &tok) {
std::map<std::string_view, YamlNode> map;
while (tok[0].kind != Token::END && tok[0].kind != Token::DEDENT) {
if (tok.size() < 2 || tok[0].kind != Token::STRING || tok[1].kind != ':')
return YamlError{"map key expected", tok[0].str.data() - input.data()};
std::string_view key = tok[0].str;
tok = tok.subspan(2);
std::variant<YamlNode, YamlError> elem = parse_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
map[key] = std::get<YamlNode>(elem);
}
return YamlNode{map};
}
std::variant<YamlNode, YamlError>
YamlParser::parse_flow_element(std::span<Token> &tok) {
if (tok[0].kind == '[') {
tok = tok.subspan(1);
return parse_flow_list(tok);
}
if (tok[0].kind != Token::STRING)
return YamlError{"scalar expected", tok[0].str.data() - input.data()};
std::string_view val = tok[0].str;
tok = tok.subspan(1);
return YamlNode{val};
}
std::variant<YamlNode, YamlError>
YamlParser::parse_flow_list(std::span<Token> &tok) {
std::vector<YamlNode> vec;
const char *start = tok[0].str.data();
while (tok[0].kind != ']' && tok[0].kind != Token::END) {
std::variant<YamlNode, YamlError> elem = parse_flow_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
vec.push_back(std::get<YamlNode>(elem));
if (tok[0].kind == ']')
break;
if (tok[0].kind != ',')
return YamlError{"comma expected", tok[0].str.data() - input.data()};
tok = tok.subspan(1);
}
if (tok[0].kind == Token::END)
return YamlError{"unterminated flow list", start - input.data()};
tok = tok.subspan(1);
return YamlNode{vec};
}
std::variant<std::vector<YamlNode>, YamlError>
parse_yaml(std::string_view str) {
return YamlParser(str).parse();
}
} // namespace mold::macho
+27 -662
View File
@@ -1,682 +1,47 @@
#include "mold.h"
#include "elf/mold.h"
#include "macho/mold.h"
#include <functional>
#include <iomanip>
#include <map>
#include <cstring>
#include <signal.h>
#include <tbb/global_control.h>
#include <tbb/parallel_for_each.h>
#include <unistd.h>
#include <unordered_set>
template <typename E>
std::string_view save_string(Context<E> &ctx, const std::string &str) {
u8 *buf = new u8[str.size() + 1];
memcpy(buf, str.data(), str.size());
buf[str.size()] = '\0';
ctx.owning_bufs.push_back(std::unique_ptr<u8[]>(buf));
return {(char *)buf, str.size()};
}
#ifdef USE_SYSTEM_MIMALLOC
#include <mimalloc-new-delete.h>
#endif
std::string get_version_string() {
if (strlen(GIT_HASH) == 0)
return "mold " MOLD_VERSION " (compatible with GNU ld and GNU gold)";
return "mold " MOLD_VERSION " (" GIT_HASH
"; compatible with GNU ld and GNU gold)";
}
namespace mold {
std::regex glob_to_regex(std::string_view pattern) {
std::stringstream ss;
for (u8 c : pattern) {
if (c == '*')
ss << ".*";
else
ss << "\\x" << std::hex << std::setw(2) << std::setfill('0') << (int)c;
}
return std::regex(ss.str(), std::regex::optimize);
}
#ifdef GIT_HASH
const std::string mold_version =
"mold " MOLD_VERSION " (" GIT_HASH "; compatible with GNU ld)";
#else
const std::string mold_version =
"mold " MOLD_VERSION " (compatible with GNU ld)";
#endif
template <typename E>
static ObjectFile<E> *new_object_file(Context<E> &ctx, MemoryMappedFile<E> *mb,
std::string archive_name) {
static Counter count("parsed_objs");
count++;
bool in_lib = (!archive_name.empty() && !ctx.whole_archive);
ObjectFile<E> *file = ObjectFile<E>::create(ctx, mb, archive_name, in_lib);
file->priority = ctx.file_priority++;
ctx.tg.run([file, &ctx]() { file->parse(ctx); });
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
}
template <typename E>
static SharedFile<E> *new_shared_file(Context<E> &ctx, MemoryMappedFile<E> *mb) {
SharedFile<E> *file = SharedFile<E>::create(ctx, mb);
file->priority = ctx.file_priority++;
ctx.tg.run([file, &ctx]() { file->parse(ctx); });
if (ctx.arg.trace)
SyncOut(ctx) << "trace: " << *file;
return file;
}
template <typename E>
void read_file(Context<E> &ctx, MemoryMappedFile<E> *mb) {
if (ctx.visited.contains(mb->name))
return;
if (ctx.is_preloading) {
switch (get_file_type(ctx, mb)) {
case FileType::OBJ:
ctx.obj_cache.store(mb, new_object_file(ctx, mb, ""));
return;
case FileType::DSO:
ctx.dso_cache.store(mb, new_shared_file(ctx, mb));
return;
case FileType::AR:
for (MemoryMappedFile<E> *child : read_fat_archive_members(ctx, mb))
if (get_file_type(ctx, child) == FileType::OBJ)
ctx.obj_cache.store(mb, new_object_file(ctx, child, mb->name));
return;
case FileType::THIN_AR:
for (MemoryMappedFile<E> *child : read_thin_archive_members(ctx, mb))
if (get_file_type(ctx, child) == FileType::OBJ)
ctx.obj_cache.store(child, new_object_file(ctx, child, mb->name));
return;
case FileType::TEXT:
parse_linker_script(ctx, mb);
return;
case FileType::LLVM_BITCODE:
Fatal(ctx) << mb->name << ": looks like this is an LLVM bitcode, but"
<< " mold does not support LTO";
default:
Fatal(ctx) << mb->name << ": unknown file type";
}
}
switch (get_file_type(ctx, mb)) {
case FileType::OBJ:
if (ObjectFile<E> *obj = ctx.obj_cache.get_one(mb))
ctx.objs.push_back(obj);
else
ctx.objs.push_back(new_object_file(ctx, mb, ""));
return;
case FileType::DSO:
if (SharedFile<E> *obj = ctx.dso_cache.get_one(mb))
ctx.dsos.push_back(obj);
else
ctx.dsos.push_back(new_shared_file(ctx, mb));
ctx.visited.insert(mb->name);
return;
case FileType::AR: {
std::vector<ObjectFile<E> *> objs = ctx.obj_cache.get(mb);
if (!objs.empty()) {
append(ctx.objs, objs);
} else {
for (MemoryMappedFile<E> *child : read_fat_archive_members(ctx, mb))
if (get_file_type(ctx, child) == FileType::OBJ)
ctx.objs.push_back(new_object_file(ctx, child, mb->name));
}
ctx.visited.insert(mb->name);
return;
}
case FileType::THIN_AR:
for (MemoryMappedFile<E> *child : read_thin_archive_members(ctx, mb)) {
if (ObjectFile<E> *obj = ctx.obj_cache.get_one(child))
ctx.objs.push_back(obj);
else if (get_file_type(ctx, child) == FileType::OBJ)
ctx.objs.push_back(new_object_file(ctx, child, mb->name));
}
ctx.visited.insert(mb->name);
return;
case FileType::TEXT:
parse_linker_script(ctx, mb);
return;
case FileType::LLVM_BITCODE:
Fatal(ctx) << mb->name << ": looks like this is an LLVM bitcode, "
<< "but mold does not support LTO";
default:
Fatal(ctx) << mb->name << ": unknown file type";
}
}
template <typename E>
void cleanup() {
if (OutputFile<E>::tmpfile)
unlink(OutputFile<E>::tmpfile);
if (output_tmpfile)
unlink(output_tmpfile);
if (socket_tmpfile)
unlink(socket_tmpfile);
}
template <typename E>
static void signal_handler(int) {
cleanup<E>();
cleanup();
_exit(1);
}
// Read the beginning of a given file and returns its machine type
// (e.g. EM_X86_64 or EM_386). Return -1 if unknown.
template <typename E>
static i64 get_machine_type(Context<E> &ctx, MemoryMappedFile<E> *mb) {
switch (get_file_type(ctx, mb)) {
case FileType::DSO:
return ((ElfEhdr<E> *)mb->data(ctx))->e_machine;
case FileType::AR:
for (MemoryMappedFile<E> *child : read_fat_archive_members(ctx, mb))
if (get_file_type(ctx, child) == FileType::OBJ)
return ((ElfEhdr<E> *)child->data(ctx))->e_machine;
return -1;
case FileType::THIN_AR:
for (MemoryMappedFile<E> *child : read_thin_archive_members(ctx, mb))
if (get_file_type(ctx, child) == FileType::OBJ)
return ((ElfEhdr<E> *)child->data(ctx))->e_machine;
return -1;
case FileType::TEXT:
return get_script_output_type(ctx, mb);
default:
return -1;
}
void install_signal_handler() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
}
template <typename E>
static MemoryMappedFile<E> *open_library(Context<E> &ctx, std::string path) {
MemoryMappedFile<E> *mb = MemoryMappedFile<E>::open(ctx, path);
if (!mb)
return nullptr;
i64 type = get_machine_type(ctx, mb);
if (type == -1 || type == E::e_machine)
return mb;
Warn(ctx) << path << ": skipping incompatible file";
return nullptr;
}
template <typename E>
MemoryMappedFile<E> *find_library(Context<E> &ctx, std::string name) {
if (name.starts_with(':')) {
for (std::string_view dir : ctx.arg.library_paths) {
std::string root = dir.starts_with("/") ? ctx.arg.sysroot : "";
std::string path = root + std::string(dir) + "/" + name.substr(1);
if (MemoryMappedFile<E> *mb = open_library(ctx, path))
return mb;
}
Fatal(ctx) << "library not found: " << name;
}
for (std::string_view dir : ctx.arg.library_paths) {
std::string root = dir.starts_with("/") ? ctx.arg.sysroot : "";
std::string stem = root + std::string(dir) + "/lib" + name;
if (!ctx.is_static)
if (MemoryMappedFile<E> *mb = open_library(ctx, stem + ".so"))
return mb;
if (MemoryMappedFile<E> *mb = open_library(ctx, stem + ".a"))
return mb;
}
Fatal(ctx) << "library not found: " << name;
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string_view> args) {
std::vector<std::tuple<bool, bool, bool>> state;
while (!args.empty()) {
std::string_view arg;
if (read_flag(args, "as-needed")) {
ctx.as_needed = true;
} else if (read_flag(args, "no-as-needed")) {
ctx.as_needed = false;
} else if (read_flag(args, "whole-archive")) {
ctx.whole_archive = true;
} else if (read_flag(args, "no-whole-archive")) {
ctx.whole_archive = false;
} else if (read_flag(args, "Bstatic")) {
ctx.is_static = true;
} else if (read_flag(args, "Bdynamic")) {
ctx.is_static = false;
} else if (read_arg(ctx, args, arg, "version-script")) {
parse_version_script(ctx, std::string(arg));
} else if (read_arg(ctx, args, arg, "dynamic-list")) {
parse_dynamic_list(ctx, std::string(arg));
} else if (read_flag(args, "push-state")) {
state.push_back({ctx.as_needed, ctx.whole_archive, ctx.is_static});
} else if (read_flag(args, "pop-state")) {
if (state.empty())
Fatal(ctx) << "no state pushed before popping";
std::tie(ctx.as_needed, ctx.whole_archive, ctx.is_static) = state.back();
state.pop_back();
} else if (read_arg(ctx, args, arg, "l")) {
MemoryMappedFile<E> *mb = find_library(ctx, std::string(arg));
mb->given_fullpath = false;
read_file(ctx, mb);
} else {
read_file(ctx, MemoryMappedFile<E>::must_open(ctx, std::string(args[0])));
args = args.subspan(1);
}
}
}
template <typename E>
static void show_stats(Context<E> &ctx) {
for (ObjectFile<E> *obj : ctx.objs) {
static Counter defined("defined_syms");
defined += obj->first_global - 1;
static Counter undefined("undefined_syms");
undefined += obj->symbols.size() - obj->first_global;
for (std::unique_ptr<InputSection<E>> &sec : obj->sections) {
if (!sec || !sec->is_alive)
continue;
static Counter alloc("reloc_alloc");
static Counter nonalloc("reloc_nonalloc");
if (sec->shdr.sh_flags & SHF_ALLOC)
alloc += sec->get_rels(ctx).size();
else
nonalloc += sec->get_rels(ctx).size();
}
static Counter comdats("comdats");
comdats += obj->comdat_groups.size();
static Counter removed_comdats("removed_comdat_mem");
for (auto &pair : obj->comdat_groups)
if (ComdatGroup *group = pair.first; group->owner != obj->priority)
removed_comdats += pair.second.size();
static Counter num_cies("num_cies");
num_cies += obj->cies.size();
static Counter num_unique_cies("num_unique_cies");
for (CieRecord<E> &cie : obj->cies)
if (cie.is_leader)
num_unique_cies++;
static Counter num_fdes("num_fdes");
num_fdes += obj->fdes.size();
}
static Counter num_bytes("total_input_bytes");
for (std::unique_ptr<MemoryMappedFile<E>> &mb : ctx.owning_mbs)
num_bytes += mb->size();
static Counter num_input_sections("input_sections");
for (ObjectFile<E> *file : ctx.objs)
num_input_sections += file->sections.size();
static Counter num_output_chunks("output_chunks", ctx.chunks.size());
static Counter num_objs("num_objs", ctx.objs.size());
static Counter num_dsos("num_dsos", ctx.dsos.size());
Counter::print();
}
template <typename E>
int do_main(int argc, char **argv) {
Context<E> ctx;
// Process -run option first. process_run_subcommand() does not return.
if (argc >= 2)
if (std::string_view arg = argv[1]; arg == "-run" || arg == "--run")
process_run_subcommand(ctx, argc, argv);
Timer t_all(ctx, "all");
// Parse non-positional command line options
ctx.cmdline_args = expand_response_files(ctx, argv);
std::vector<std::string_view> file_args;
parse_nonpositional_args(ctx, file_args);
if (ctx.arg.relocatable) {
combine_objects(ctx, file_args);
return 0;
}
if (!ctx.arg.preload)
try_resume_daemon(ctx);
tbb::global_control tbb_cont(tbb::global_control::max_allowed_parallelism,
ctx.arg.thread_count);
signal(SIGINT, signal_handler<E>);
signal(SIGTERM, signal_handler<E>);
if (!ctx.arg.directory.empty() && chdir(ctx.arg.directory.c_str()) == -1)
Fatal(ctx) << "chdir failed: " << ctx.arg.directory
<< ": " << strerror(errno);
// Handle --wrap options if any.
for (std::string_view name : ctx.arg.wrap)
Symbol<E>::intern(ctx, name)->wrap = true;
// Handle --retain-symbols-file options if any.
if (ctx.arg.retain_symbols_file)
for (std::string_view name : *ctx.arg.retain_symbols_file)
Symbol<E>::intern(ctx, name)->write_to_symtab = true;
// Preload input files
std::function<void()> on_complete;
if (ctx.arg.preload) {
Timer t(ctx, "preload");
std::function<void()> wait_for_client;
daemonize(ctx, &wait_for_client, &on_complete);
ctx.reset_reader_context(true);
read_input_files(ctx, file_args);
ctx.tg.wait();
t.stop();
Timer t2(ctx, "wait_for_client");
wait_for_client();
} else if (ctx.arg.fork) {
on_complete = fork_child();
}
for (std::string_view arg : ctx.arg.trace_symbol)
Symbol<E>::intern(ctx, arg)->traced = true;
// Parse input files
{
Timer t(ctx, "parse");
ctx.reset_reader_context(false);
read_input_files(ctx, file_args);
ctx.tg.wait();
}
if (ctx.objs.empty())
Fatal(ctx) << "no input files";
// Uniquify shared object files by soname
{
std::unordered_set<std::string_view> seen;
erase(ctx.dsos, [&](SharedFile<E> *file) {
return !seen.insert(file->soname).second;
});
}
Timer t_total(ctx, "total");
Timer t_before_copy(ctx, "before_copy");
// 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);
// Remove redundant comdat sections (e.g. duplicate inline functions).
eliminate_comdats(ctx);
// Create .bss sections for common symbols.
convert_common_symbols(ctx);
// Apply version scripts.
apply_version_script(ctx);
// Parse symbol version suffixes (e.g. "foo@ver1").
parse_symbol_version(ctx);
// Set is_import and is_export bits for each symbol.
compute_import_export(ctx);
// Garbage-collect unreachable sections.
if (ctx.arg.gc_sections)
gc_sections(ctx);
// Merge identical read-only sections.
if (ctx.arg.icf)
icf_sections(ctx);
// Compute sizes of sections containing mergeable strings.
compute_merged_section_sizes(ctx);
// ctx input sections into output sections
bin_sections(ctx);
// Get a list of output sections.
append(ctx.chunks, collect_output_sections(ctx));
// Create a dummy file containing linker-synthesized symbols
// (e.g. `__bss_start`).
ctx.internal_obj = ObjectFile<E>::create_internal_file(ctx);
ctx.internal_obj->resolve_regular_symbols(ctx);
ctx.objs.push_back(ctx.internal_obj);
// Beyond this point, no new files will be added to ctx.objs
// or ctx.dsos.
// If we are linking a .so file, remaining undefined symbols does
// not cause a linker error. Instead, they are treated as if they
// were imported symbols.
//
// If we are linking an executable, weak undefs are converted to
// weakly imported symbol so that they'll have another chance to be
claim_unresolved_symbols(ctx);
// Beyond this point, no new symbols will be added to the result.
// Make sure that all symbols have been resolved.
if (!ctx.arg.allow_multiple_definition)
check_duplicate_symbols(ctx);
// .init_array and .fini_array contents have to be sorted by
// a special rule. Sort them.
sort_init_fini(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
// Sort sections by section attributes so that we'll have to
// create as few segments as possible.
sort(ctx.chunks, [&](OutputChunk<E> *a, OutputChunk<E> *b) {
return get_section_rank(ctx, a) < get_section_rank(ctx, b);
});
// Copy string referred by .dynamic to .dynstr.
for (SharedFile<E> *file : ctx.dsos)
ctx.dynstr->add_string(file->soname);
for (std::string_view str : ctx.arg.auxiliary)
ctx.dynstr->add_string(str);
for (std::string_view str : ctx.arg.filter)
ctx.dynstr->add_string(str);
if (!ctx.arg.rpaths.empty())
ctx.dynstr->add_string(ctx.arg.rpaths);
if (!ctx.arg.soname.empty())
ctx.dynstr->add_string(ctx.arg.soname);
// Scan relocations to find symbols that need entries in .got, .plt,
// .got.plt, .dynsym, .dynstr, etc.
scan_rels(ctx);
// Reserve a space for dynamic symbol strings in .dynstr and sort
// .dynsym contents if necessary. Beyond this point, no symbol will
// be added to .dynsym.
ctx.dynsym->finalize(ctx);
// Fill .gnu.version_d section contents.
ctx.verdef->construct(ctx);
// Fill .gnu.version_r section contents.
ctx.verneed->construct(ctx);
// Compute .symtab and .strtab sizes for each file.
{
Timer t(ctx, "compute_symtab");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->compute_symtab(ctx);
});
}
// .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");
erase(ctx.chunks, [](OutputChunk<E> *chunk) {
return chunk->kind == OutputChunk<E>::REGULAR &&
chunk->name == ".eh_frame";
});
ctx.eh_frame->construct(ctx);
}
// Now that we have computed sizes for all sections and assigned
// section indices to them, so we can fix section header contents
// for all output sections.
for (OutputChunk<E> *chunk : ctx.chunks)
chunk->update_shdr(ctx);
erase(ctx.chunks, [](OutputChunk<E> *chunk) {
return chunk->kind == OutputChunk<E>::SYNTHETIC &&
chunk->shdr.sh_size == 0;
});
// Set section indices.
for (i64 i = 0, shndx = 1; i < ctx.chunks.size(); i++)
if (ctx.chunks[i]->kind != OutputChunk<E>::HEADER)
ctx.chunks[i]->shndx = shndx++;
for (OutputChunk<E> *chunk : ctx.chunks)
chunk->update_shdr(ctx);
// Assign offsets to output sections
i64 filesize = set_osec_offsets(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 relocations for TLS symbols need the TLS segment
// address. Find it out 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;
}
}
t_before_copy.stop();
// Create an output file
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0777);
ctx.buf = ctx.output_file->buf;
Timer t_copy(ctx, "copy");
// Copy input sections to the output file
{
Timer t(ctx, "copy_buf");
tbb::parallel_for_each(ctx.chunks, [&](OutputChunk<E> *chunk) {
std::string name(chunk->name);
if (name.empty())
name = "(header)";
Timer t2(ctx, name, &t);
chunk->copy_buf(ctx);
});
Error<E>::checkpoint(ctx);
}
// Dynamic linker works better with sorted .rela.dyn section,
// so we sort them.
ctx.reldyn->sort(ctx);
// Zero-clear paddings between sections
clear_padding(ctx);
if (ctx.buildid) {
Timer t(ctx, "build_id");
ctx.buildid->write_buildid(ctx);
}
t_copy.stop();
// Commit
ctx.output_file->close(ctx);
t_total.stop();
t_all.stop();
if (ctx.arg.print_map)
print_map(ctx);
// Show stats numbers
if (ctx.arg.stats)
show_stats(ctx);
if (ctx.arg.perf)
Timer<E>::print(ctx);
std::cout << std::flush;
std::cerr << std::flush;
if (on_complete)
on_complete();
if (ctx.arg.quick_exit)
std::quick_exit(0);
for (std::function<void()> &fn : ctx.on_exit)
fn();
return 0;
}
enum class MachineType { X86_64, I386 };
static MachineType get_machine_type(int argc, char **argv) {
for (i64 i = 1; i < argc; i++) {
if (std::string_view(argv[i]) == "-m") {
if (i + 1 == argc)
break;
i++;
std::string_view val(argv[i]);
if (val == "elf_x86_64")
return MachineType::X86_64;
if (val == "elf_i386")
return MachineType::I386;
std::cerr << "unknown -m argument: " << val << "\n";
exit(1);
}
}
return MachineType::X86_64;
}
} // namespace mold
int main(int argc, char **argv) {
switch (get_machine_type(argc, argv)) {
case MachineType::X86_64:
return do_main<X86_64>(argc, argv);
case MachineType::I386:
return do_main<I386>(argc, argv);
}
std::string cmd = mold::filepath(argv[0]).filename();
if (cmd == "ld64" || cmd == "ld64.mold")
return mold::macho::main(argc, argv);
return mold::elf::main(argc, argv);
}
#define INSTANTIATE(E) \
template void read_file(Context<E> &, MemoryMappedFile<E> *); \
template std::string_view save_string(Context<E> &, const std::string &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
-118
View File
@@ -1,118 +0,0 @@
#include "mold.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
template <typename E>
MemoryMappedFile<E> *
MemoryMappedFile<E>::open(Context<E> &ctx, std::string path) {
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
struct stat st;
if (stat(path.c_str(), &st) == -1)
return nullptr;
u64 mtime = (u64)st.st_mtim.tv_sec * 1000000000 + st.st_mtim.tv_nsec;
u8 *data = nullptr;
if (st.st_size == 0) {
// mmap(2) doesn't allow 0 as an argument for length parameter,
// so this case has to be handled as a special case.
static u8 dummy[1];
data = dummy;
}
MemoryMappedFile *mb = new MemoryMappedFile(path, data, st.st_size, mtime);
ctx.owning_mbs.push_back(std::unique_ptr<MemoryMappedFile>(mb));
return mb;
}
template <typename E>
MemoryMappedFile<E> *
MemoryMappedFile<E>::must_open(Context<E> &ctx, std::string path) {
if (MemoryMappedFile *mb = MemoryMappedFile::open(ctx, path))
return mb;
Fatal(ctx) << "cannot open " << path;
}
template <typename E>
u8 *MemoryMappedFile<E>::data(Context<E> &ctx) {
if (data_)
return data_;
std::lock_guard lock(mu);
if (data_)
return data_;
i64 fd = ::open(name.c_str(), O_RDONLY);
if (fd == -1)
Fatal(ctx) << name << ": cannot open: " << strerror(errno);
data_ = (u8 *)mmap(nullptr, size_, PROT_READ, MAP_PRIVATE, fd, 0);
if (data_ == MAP_FAILED)
Fatal(ctx) << name << ": mmap failed: " << strerror(errno);
close(fd);
return data_;
}
template <typename E>
MemoryMappedFile<E> *
MemoryMappedFile<E>::slice(Context<E> &ctx, std::string name, u64 start,
u64 size) {
MemoryMappedFile *mb = new MemoryMappedFile<E>(name, data_ + start, size);
ctx.owning_mbs.push_back(std::unique_ptr<MemoryMappedFile>(mb));
mb->parent = this;
return mb;
}
template <typename E>
MemoryMappedFile<E>::~MemoryMappedFile() {
if (data_ && !parent)
munmap(data_, size_);
}
template <typename E>
static bool is_text_file(Context<E> &ctx, MemoryMappedFile<E> *mb) {
u8 *data = mb->data(ctx);
return mb->size() >= 4 &&
isprint(data[0]) &&
isprint(data[1]) &&
isprint(data[2]) &&
isprint(data[3]);
}
template <typename E>
FileType get_file_type(Context<E> &ctx, MemoryMappedFile<E> *mb) {
u8 *data = mb->data(ctx);
if (mb->size() >= 20 && memcmp(data, "\177ELF", 4) == 0) {
ElfEhdr<E> &ehdr = *(ElfEhdr<E> *)data;
if (ehdr.e_type == ET_REL)
return FileType::OBJ;
if (ehdr.e_type == ET_DYN)
return FileType::DSO;
return FileType::UNKNOWN;
}
if (mb->size() >= 8 && memcmp(data, "!<arch>\n", 8) == 0)
return FileType::AR;
if (mb->size() >= 8 && memcmp(data, "!<thin>\n", 8) == 0)
return FileType::THIN_AR;
if (is_text_file(ctx, mb))
return FileType::TEXT;
if (mb->size() >= 4 && memcmp(data, "\xDE\xC0\x17\x0B", 4) == 0)
return FileType::LLVM_BITCODE;
if (mb->size() >= 4 && memcmp(data, "BC\xC0\xDE", 4) == 0)
return FileType::LLVM_BITCODE;
return FileType::UNKNOWN;
}
#define INSTANTIATE(E) \
template class MemoryMappedFile<E>; \
template FileType get_file_type(Context<E> &, MemoryMappedFile<E> *)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
-2
View File
@@ -1,2 +0,0 @@
include(${CMAKE_CURRENT_LIST_DIR}/mimalloc.cmake)
get_filename_component(MIMALLOC_TARGET_DIR "${CMAKE_CURRENT_LIST_DIR}" PATH)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 164 KiB

-87
View File
@@ -1,87 +0,0 @@
/*!
* jQuery JavaScript Library v1.7.2
* http://jquery.com/
*
* Copyright 2011, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Wed Mar 21 12:46:34 2012 -0700
*/
(function(bd,L){var av=bd.document,bu=bd.navigator,bm=bd.location;var b=(function(){var bF=function(b0,b1){return new bF.fn.init(b0,b1,bD)},bU=bd.jQuery,bH=bd.$,bD,bY=/^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,bM=/\S/,bI=/^\s+/,bE=/\s+$/,bA=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,bN=/^[\],:{}\s]*$/,bW=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,bP=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,bJ=/(?:^|:|,)(?:\s*\[)+/g,by=/(webkit)[ \/]([\w.]+)/,bR=/(opera)(?:.*version)?[ \/]([\w.]+)/,bQ=/(msie) ([\w.]+)/,bS=/(mozilla)(?:.*? rv:([\w.]+))?/,bB=/-([a-z]|[0-9])/ig,bZ=/^-ms-/,bT=function(b0,b1){return(b1+"").toUpperCase()},bX=bu.userAgent,bV,bC,e,bL=Object.prototype.toString,bG=Object.prototype.hasOwnProperty,bz=Array.prototype.push,bK=Array.prototype.slice,bO=String.prototype.trim,bv=Array.prototype.indexOf,bx={};bF.fn=bF.prototype={constructor:bF,init:function(b0,b4,b3){var b2,b5,b1,b6;if(!b0){return this}if(b0.nodeType){this.context=this[0]=b0;this.length=1;return this}if(b0==="body"&&!b4&&av.body){this.context=av;this[0]=av.body;this.selector=b0;this.length=1;return this}if(typeof b0==="string"){if(b0.charAt(0)==="<"&&b0.charAt(b0.length-1)===">"&&b0.length>=3){b2=[null,b0,null]}else{b2=bY.exec(b0)}if(b2&&(b2[1]||!b4)){if(b2[1]){b4=b4 instanceof bF?b4[0]:b4;b6=(b4?b4.ownerDocument||b4:av);b1=bA.exec(b0);if(b1){if(bF.isPlainObject(b4)){b0=[av.createElement(b1[1])];bF.fn.attr.call(b0,b4,true)}else{b0=[b6.createElement(b1[1])]}}else{b1=bF.buildFragment([b2[1]],[b6]);b0=(b1.cacheable?bF.clone(b1.fragment):b1.fragment).childNodes}return bF.merge(this,b0)}else{b5=av.getElementById(b2[2]);if(b5&&b5.parentNode){if(b5.id!==b2[2]){return b3.find(b0)}this.length=1;this[0]=b5}this.context=av;this.selector=b0;return this}}else{if(!b4||b4.jquery){return(b4||b3).find(b0)}else{return this.constructor(b4).find(b0)}}}else{if(bF.isFunction(b0)){return b3.ready(b0)}}if(b0.selector!==L){this.selector=b0.selector;this.context=b0.context}return bF.makeArray(b0,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return bK.call(this,0)},get:function(b0){return b0==null?this.toArray():(b0<0?this[this.length+b0]:this[b0])},pushStack:function(b1,b3,b0){var b2=this.constructor();if(bF.isArray(b1)){bz.apply(b2,b1)}else{bF.merge(b2,b1)}b2.prevObject=this;b2.context=this.context;if(b3==="find"){b2.selector=this.selector+(this.selector?" ":"")+b0}else{if(b3){b2.selector=this.selector+"."+b3+"("+b0+")"}}return b2},each:function(b1,b0){return bF.each(this,b1,b0)},ready:function(b0){bF.bindReady();bC.add(b0);return this},eq:function(b0){b0=+b0;return b0===-1?this.slice(b0):this.slice(b0,b0+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(bK.apply(this,arguments),"slice",bK.call(arguments).join(","))},map:function(b0){return this.pushStack(bF.map(this,function(b2,b1){return b0.call(b2,b1,b2)}))},end:function(){return this.prevObject||this.constructor(null)},push:bz,sort:[].sort,splice:[].splice};bF.fn.init.prototype=bF.fn;bF.extend=bF.fn.extend=function(){var b9,b2,b0,b1,b6,b7,b5=arguments[0]||{},b4=1,b3=arguments.length,b8=false;if(typeof b5==="boolean"){b8=b5;b5=arguments[1]||{};b4=2}if(typeof b5!=="object"&&!bF.isFunction(b5)){b5={}}if(b3===b4){b5=this;--b4}for(;b4<b3;b4++){if((b9=arguments[b4])!=null){for(b2 in b9){b0=b5[b2];b1=b9[b2];if(b5===b1){continue}if(b8&&b1&&(bF.isPlainObject(b1)||(b6=bF.isArray(b1)))){if(b6){b6=false;b7=b0&&bF.isArray(b0)?b0:[]}else{b7=b0&&bF.isPlainObject(b0)?b0:{}}b5[b2]=bF.extend(b8,b7,b1)}else{if(b1!==L){b5[b2]=b1}}}}}return b5};bF.extend({noConflict:function(b0){if(bd.$===bF){bd.$=bH}if(b0&&bd.jQuery===bF){bd.jQuery=bU}return bF},isReady:false,readyWait:1,holdReady:function(b0){if(b0){bF.readyWait++}else{bF.ready(true)}},ready:function(b0){if((b0===true&&!--bF.readyWait)||(b0!==true&&!bF.isReady)){if(!av.body){return setTimeout(bF.ready,1)}bF.isReady=true;if(b0!==true&&--bF.readyWait>0){return}bC.fireWith(av,[bF]);if(bF.fn.trigger){bF(av).trigger("ready").off("ready")}}},bindReady:function(){if(bC){return}bC=bF.Callbacks("once memory");if(av.readyState==="complete"){return setTimeout(bF.ready,1)}if(av.addEventListener){av.addEventListener("DOMContentLoaded",e,false);bd.addEventListener("load",bF.ready,false)}else{if(av.attachEvent){av.attachEvent("onreadystatechange",e);bd.attachEvent("onload",bF.ready);var b0=false;try{b0=bd.frameElement==null}catch(b1){}if(av.documentElement.doScroll&&b0){bw()}}}},isFunction:function(b0){return bF.type(b0)==="function"},isArray:Array.isArray||function(b0){return bF.type(b0)==="array"},isWindow:function(b0){return b0!=null&&b0==b0.window},isNumeric:function(b0){return !isNaN(parseFloat(b0))&&isFinite(b0)},type:function(b0){return b0==null?String(b0):bx[bL.call(b0)]||"object"},isPlainObject:function(b2){if(!b2||bF.type(b2)!=="object"||b2.nodeType||bF.isWindow(b2)){return false}try{if(b2.constructor&&!bG.call(b2,"constructor")&&!bG.call(b2.constructor.prototype,"Line truncated
/*!
* Sizzle CSS Selector Engine
* Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){var bH=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,bC="sizcache"+(Math.random()+"").replace(".",""),bI=0,bL=Object.prototype.toString,bB=false,bA=true,bK=/\\/g,bO=/\r\n/g,bQ=/\W/;[0,0].sort(function(){bA=false;return 0});var by=function(bV,e,bY,bZ){bY=bY||[];e=e||av;var b1=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!bV||typeof bV!=="string"){return bY}var bS,b3,b6,bR,b2,b5,b4,bX,bU=true,bT=by.isXML(e),bW=[],b0=bV;do{bH.exec("");bS=bH.exec(b0);if(bS){b0=bS[3];bW.push(bS[1]);if(bS[2]){bR=bS[3];break}}}while(bS);if(bW.length>1&&bD.exec(bV)){if(bW.length===2&&bE.relative[bW[0]]){b3=bM(bW[0]+bW[1],e,bZ)}else{b3=bE.relative[bW[0]]?[e]:by(bW.shift(),e);while(bW.length){bV=bW.shift();if(bE.relative[bV]){bV+=bW.shift()}b3=bM(bV,b3,bZ)}}}else{if(!bZ&&bW.length>1&&e.nodeType===9&&!bT&&bE.match.ID.test(bW[0])&&!bE.match.ID.test(bW[bW.length-1])){b2=by.find(bW.shift(),e,bT);e=b2.expr?by.filter(b2.expr,b2.set)[0]:b2.set[0]}if(e){b2=bZ?{expr:bW.pop(),set:bF(bZ)}:by.find(bW.pop(),bW.length===1&&(bW[0]==="~"||bW[0]==="+")&&e.parentNode?e.parentNode:e,bT);b3=b2.expr?by.filter(b2.expr,b2.set):b2.set;if(bW.length>0){b6=bF(b3)}else{bU=false}while(bW.length){b5=bW.pop();b4=b5;if(!bE.relative[b5]){b5=""}else{b4=bW.pop()}if(b4==null){b4=e}bE.relative[b5](b6,b4,bT)}}else{b6=bW=[]}}if(!b6){b6=b3}if(!b6){by.error(b5||bV)}if(bL.call(b6)==="[object Array]"){if(!bU){bY.push.apply(bY,b6)}else{if(e&&e.nodeType===1){for(bX=0;b6[bX]!=null;bX++){if(b6[bX]&&(b6[bX]===true||b6[bX].nodeType===1&&by.contains(e,b6[bX]))){bY.push(b3[bX])}}}else{for(bX=0;b6[bX]!=null;bX++){if(b6[bX]&&b6[bX].nodeType===1){bY.push(b3[bX])}}}}}else{bF(b6,bY)}if(bR){by(bR,b1,bY,bZ);by.uniqueSort(bY)}return bY};by.uniqueSort=function(bR){if(bJ){bB=bA;bR.sort(bJ);if(bB){for(var e=1;e<bR.length;e++){if(bR[e]===bR[e-1]){bR.splice(e--,1)}}}}return bR};by.matches=function(e,bR){return by(e,null,null,bR)};by.matchesSelector=function(e,bR){return by(bR,null,null,[e]).length>0};by.find=function(bX,e,bY){var bW,bS,bU,bT,bV,bR;if(!bX){return[]}for(bS=0,bU=bE.order.length;bS<bU;bS++){bV=bE.order[bS];if((bT=bE.leftMatch[bV].exec(bX))){bR=bT[1];bT.splice(1,1);if(bR.substr(bR.length-1)!=="\\"){bT[1]=(bT[1]||"").replace(bK,"");bW=bE.find[bV](bT,e,bY);if(bW!=null){bX=bX.replace(bE.match[bV],"");break}}}}if(!bW){bW=typeof e.getElementsByTagName!=="undefined"?e.getElementsByTagName("*"):[]}return{set:bW,expr:bX}};by.filter=function(b1,b0,b4,bU){var bW,e,bZ,b6,b3,bR,bT,bV,b2,bS=b1,b5=[],bY=b0,bX=b0&&b0[0]&&by.isXML(b0[0]);while(b1&&b0.length){for(bZ in bE.filter){if((bW=bE.leftMatch[bZ].exec(b1))!=null&&bW[2]){bR=bE.filter[bZ];bT=bW[1];e=false;bW.splice(1,1);if(bT.substr(bT.length-1)==="\\"){continue}if(bY===b5){b5=[]}if(bE.preFilter[bZ]){bW=bE.preFilter[bZ](bW,bY,b4,b5,bU,bX);if(!bW){e=b6=true}else{if(bW===true){continue}}}if(bW){for(bV=0;(b3=bY[bV])!=null;bV++){if(b3){b6=bR(b3,bW,bV,bY);b2=bU^b6;if(b4&&b6!=null){if(b2){e=true}else{bY[bV]=false}}else{if(b2){b5.push(b3);e=true}}}}}if(b6!==L){if(!b4){bY=b5}b1=b1.replace(bE.match[bZ],"");if(!e){return[]}break}}}if(b1===bS){if(e==null){by.error(b1)}else{break}}bS=b1}return bY};by.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)};var bw=by.getText=function(bU){var bS,bT,e=bU.nodeType,bR="";if(e){if(e===1||e===9||e===11){if(typeof bU.textContent==="string"){return bU.textContent}else{if(typeof bU.innerText==="string"){return bU.innerText.replace(bO,"")}else{for(bU=bU.firstChild;bU;bU=bU.nextSibling){bR+=bw(bU)}}}}else{if(e===3||e===4){return bU.nodeValue}}}else{for(bS=0;(bT=bU[bS]);bS++){if(bT.nodeType!==8){bR+=bw(bT)}}}return bR};var bE=by.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(e){return e.getAttribute("href")},type:function(e){return e.getAttribute("type")}},relative:{"+":function(bW,bR){var bT=typeof bR==="string",bV=bT&&!bQ.test(bR),bX=bT&&!bV;if(bV){bR=bR.toLowerCase()}for(var bS=0,e=bW.length,bU;bS<e;bS++){if((bU=bW[bS])){while((bU=bU.previousSibling)&&bU.nodeType!==1){}bW[bS]=bX||bU&&bU.nodeName.toLowerCase()===bR?bU||false:bU===bR}}if(bX){by.filter(bR,bW,true)}},">":function(bW,bR){var bV,bU=typeof bR==="string",bS=0,e=bW.length;if(bU&&!bQ.test(bR)){bR=bR.toLowerCase();for(;bS<e;bS++){bV=bW[bS];if(bV){var bT=bV.parentNode;bW[bS]=bT.nodeName.toLowerCase()===bR?bT:falsLine truncated
* jQuery UI 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI
*/
(function(a,d){a.ui=a.ui||{};if(a.ui.version){return}a.extend(a.ui,{version:"1.8.18",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});a.fn.extend({propAttr:a.fn.prop||a.fn.attr,_focus:a.fn.focus,focus:function(e,f){return typeof e==="number"?this.each(function(){var g=this;setTimeout(function(){a(g).focus();if(f){f.call(g)}},e)}):this._focus.apply(this,arguments)},scrollParent:function(){var e;if((a.browser.msie&&(/(static|relative)/).test(this.css("position")))||(/absolute/).test(this.css("position"))){e=this.parents().filter(function(){return(/(relative|absolute|fixed)/).test(a.curCSS(this,"position",1))&&(/(auto|scroll)/).test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0)}else{e=this.parents().filter(function(){return(/(auto|scroll)/).test(a.curCSS(this,"overflow",1)+a.curCSS(this,"overflow-y",1)+a.curCSS(this,"overflow-x",1))}).eq(0)}return(/fixed/).test(this.css("position"))||!e.length?a(document):e},zIndex:function(h){if(h!==d){return this.css("zIndex",h)}if(this.length){var f=a(this[0]),e,g;while(f.length&&f[0]!==document){e=f.css("position");if(e==="absolute"||e==="relative"||e==="fixed"){g=parseInt(f.css("zIndex"),10);if(!isNaN(g)&&g!==0){return g}}f=f.parent()}}return 0},disableSelection:function(){return this.bind((a.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(e){e.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});a.each(["Width","Height"],function(g,e){var f=e==="Width"?["Left","Right"]:["Top","Bottom"],h=e.toLowerCase(),k={innerWidth:a.fn.innerWidth,innerHeight:a.fn.innerHeight,outerWidth:a.fn.outerWidth,outerHeight:a.fn.outerHeight};function j(m,l,i,n){a.each(f,function(){l-=parseFloat(a.curCSS(m,"padding"+this,true))||0;if(i){l-=parseFloat(a.curCSS(m,"border"+this+"Width",true))||0}if(n){l-=parseFloat(a.curCSS(m,"margin"+this,true))||0}});return l}a.fn["inner"+e]=function(i){if(i===d){return k["inner"+e].call(this)}return this.each(function(){a(this).css(h,j(this,i)+"px")})};a.fn["outer"+e]=function(i,l){if(typeof i!=="number"){return k["outer"+e].call(this,i)}return this.each(function(){a(this).css(h,j(this,i,true,l)+"px")})}});function c(g,e){var j=g.nodeName.toLowerCase();if("area"===j){var i=g.parentNode,h=i.name,f;if(!g.href||!h||i.nodeName.toLowerCase()!=="map"){return false}f=a("img[usemap=#"+h+"]")[0];return !!f&&b(f)}return(/input|select|textarea|button|object/.test(j)?!g.disabled:"a"==j?g.href||e:e)&&b(g)}function b(e){return !a(e).parents().andSelf().filter(function(){return a.curCSS(this,"visibility")==="hidden"||a.expr.filters.hidden(this)}).length}a.extend(a.expr[":"],{data:function(g,f,e){return !!a.data(g,e[3])},focusable:function(e){return c(e,!isNaN(a.attr(e,"tabindex")))},tabbable:function(g){var e=a.attr(g,"tabindex"),f=isNaN(e);return(f||e>=0)&&c(g,!f)}});a(function(){var e=document.body,f=e.appendChild(f=document.createElement("div"));f.offsetHeight;a.extend(f.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});a.support.minHeight=f.offsetHeight===100;a.support.selectstart="onselectstart" in f;e.removeChild(f).style.display="none"});a.extend(a.ui,{plugin:{add:function(f,g,j){var h=a.ui[f].prototype;for(var e in j){h.plugins[e]=h.plugins[e]||[];h.plugins[e].push([g,j[e]])}},call:function(e,g,f){var j=e.plugins[g];if(!j||!e.element[0].parentNode){return}for(var h=0;h<j.length;h++){if(e.options[j[h][0]]){j[h][1].apply(e.element,f)}}}},contains:function(f,e){return document.compareDocumentPosition?f.compareDocumentPosition(e)&16:f!==e&&f.contains(e)},hasScroll:function(h,f){if(a(h).css("overflow")==="hidden"){return false}var e=(f&&f==="left")?"scrollLeft":"scrollTop",g=false;if(h[e]>0){return true}h[e]=1;g=(h[e]>0);h[e]=0;return g},isOverAxis:function(f,e,g){return(f>e)&&(f<(e+g))},isOver:function(j,f,i,h,e,g){return a.ui.isOverAxis(j,i,e)&&a.ui.isOverAxis(f,h,g)}})})(jQuery);/*!
* jQuery UI Widget 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Widget
*/
(function(b,d){if(b.cleanData){var c=b.cleanData;b.cleanData=function(f){for(var g=0,h;(h=f[g])!=null;g++){try{b(h).triggerHandler("remove")}catch(j){}}c(f)}}else{var a=b.fn.remove;b.fn.remove=function(e,f){return this.each(function(){if(!f){if(!e||b.filter(e,[this]).length){b("*",this).add([this]).each(function(){try{b(this).triggerHandler("remove")}catch(g){}})}}return a.call(b(this),e,f)})}}b.widget=function(f,h,e){var g=f.split(".")[0],j;f=f.split(".")[1];j=g+"-"+f;if(!e){e=h;h=b.Widget}b.expr[":"][j]=function(k){return !!b.data(k,f)};b[g]=b[g]||{};b[g][f]=function(k,l){if(arguments.length){this._createWidget(k,l)}};var i=new h();i.options=b.extend(true,{},i.options);b[g][f].prototype=b.extend(true,i,{namespace:g,widgetName:f,widgetEventPrefix:b[g][f].prototype.widgetEventPrefix||f,widgetBaseClass:j},e);b.widget.bridge(f,b[g][f])};b.widget.bridge=function(f,e){b.fn[f]=function(i){var g=typeof i==="string",h=Array.prototype.slice.call(arguments,1),j=this;i=!g&&h.length?b.extend.apply(null,[true,i].concat(h)):i;if(g&&i.charAt(0)==="_"){return j}if(g){this.each(function(){var k=b.data(this,f),l=k&&b.isFunction(k[i])?k[i].apply(k,h):k;if(l!==k&&l!==d){j=l;return false}})}else{this.each(function(){var k=b.data(this,f);if(k){k.option(i||{})._init()}else{b.data(this,f,new e(i,this))}})}return j}};b.Widget=function(e,f){if(arguments.length){this._createWidget(e,f)}};b.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",options:{disabled:false},_createWidget:function(f,g){b.data(g,this.widgetName,this);this.element=b(g);this.options=b.extend(true,{},this.options,this._getCreateOptions(),f);var e=this;this.element.bind("remove."+this.widgetName,function(){e.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")},widget:function(){return this.element},option:function(f,g){var e=f;if(arguments.length===0){return b.extend({},this.options)}if(typeof f==="string"){if(g===d){return this.options[f]}e={};e[f]=g}this._setOptions(e);return this},_setOptions:function(f){var e=this;b.each(f,function(g,h){e._setOption(g,h)});return this},_setOption:function(e,f){this.options[e]=f;if(e==="disabled"){this.widget()[f?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",f)}return this},enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(e,f,g){var j,i,h=this.options[e];g=g||{};f=b.Event(f);f.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase();f.target=this.element[0];i=f.originalEvent;if(i){for(j in i){if(!(j in f)){f[j]=i[j]}}}this.element.trigger(f,g);return !(b.isFunction(h)&&h.call(this.element[0],f,g)===false||f.isDefaultPrevented())}}})(jQuery);/*!
* jQuery UI Mouse 1.8.18
*
* Copyright 2011, AUTHORS.txt (http://jqueryui.com/about)
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* http://docs.jquery.com/UI/Mouse
*
* Depends:
* jquery.ui.widget.js
*/
(function(b,c){var a=false;b(document).mouseup(function(d){a=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var d=this;this.element.bind("mousedown."+this.widgetName,function(e){return d._mouseDown(e)}).bind("click."+this.widgetName,function(e){if(true===b.data(e.target,d.widgetName+".preventClickEvent")){b.removeData(e.target,d.widgetName+".preventClickEvent");e.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+this.widgetName)},_mouseDown:function(f){if(a){return}(this._mouseStarted&&this._mouseUp(f));this._mouseDownEvent=f;var e=this,g=(f.which==1),d=(typeof this.options.cancel=="string"&&f.target.nodeName?b(f.target).closest(this.options.cancel).length:false);if(!g||d||!this._mouseCapture(f)){return true}this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){e.mouseDelayMet=true},this.options.delay)}if(this._mouseDistanceMet(f)&&this._mouseDelayMet(f)){this._mouseStarted=(this._mouseStart(f)!==false);if(!this._mouseStarted){f.preventDefault();return true}}if(true===b.data(f.target,this.widgetName+".preventClickEvent")){b.removeData(f.target,this.widgetName+".preventClickEvent")}this._mouseMoveDelegate=function(h){return e._mouseMove(h)};this._mouseUpDelegate=function(h){return e._mouseUp(h)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);f.preventDefault();a=true;return true},_mouseMove:function(d){if(b.browser.msie&&!(document.documentMode>=9)&&!d.button){return this._mouseUp(d)}if(this._mouseStarted){this._mouseDrag(d);return d.preventDefault()}if(this._mouseDistanceMet(d)&&this._mouseDelayMet(d)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,d)!==false);(this._mouseStarted?this._mouseDrag(d):this._mouseUp(d))}return !this._mouseStarted},_mouseUp:function(d){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;if(d.target==this._mouseDownEvent.target){b.data(d.target,this.widgetName+".preventClickEvent",true)}this._mouseStop(d)}return false},_mouseDistanceMet:function(d){return(Math.max(Math.abs(this._mouseDownEvent.pageX-d.pageX),Math.abs(this._mouseDownEvent.pageY-d.pageY))>=this.options.distance)},_mouseDelayMet:function(d){return this.mouseDelayMet},_mouseStart:function(d){},_mouseDrag:function(d){},_mouseStop:function(d){},_mouseCapture:function(d){return true}})})(jQuery);(function(c,d){c.widget("ui.resizable",c.ui.mouse,{widgetEventPrefix:"resize",options:{alsoResize:false,animate:false,animateDuration:"slow",animateEasing:"swing",aspectRatio:false,autoHide:false,containment:false,ghost:false,grid:false,handles:"e,s,se",helper:false,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:1000},_create:function(){var f=this,k=this.options;this.element.addClass("ui-resizable");c.extend(this,{_aspectRatio:!!(k.aspectRatio),aspectRatio:k.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:k.helper||k.ghost||k.animate?k.helper||"ui-resizable-helper":null});if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)){this.element.wrap(c('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")}));this.element=this.element.parent().data("resizable",this.element.data("resizable"));this.elementIsWrapper=true;this.element.css({marginLeft:this.originalElement.css("marginLeft"),marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom")});this.originalElement.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});this.originalResizeStyle=this.originalElement.css("resize");this.originalElement.css("resize","none");this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"}));this.originalElement.css({margin:this.originalElement.css("margin")});this._proportionallyResize()}this.handles=k.handles||(!c(".ui-resizable-handle",this.element).length?"e,s,se":{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"});if(this.handles.constructor==String){if(this.handles=="all"){this.handles="n,e,s,w,se,sw,ne,nw"}var l=this.handles.split(",");this.handles={};for(var g=0;g<l.length;g++){var j=c.trim(l[g]),e="ui-resizable-"+j;var h=c('<div class="ui-resizable-handle '+e+'"></div>');if(/sw|se|ne|nw/.test(j)){h.css({zIndex:++k.zIndex})}if("se"==j){h.addClass("ui-icon ui-icon-gripsmall-diagonal-se")}this.handles[j]=".ui-resizable-"+j;this.element.append(h)}}this._renderLine truncated
* jQuery hashchange event - v1.3 - 7/21/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,e,b){var c="hashchange",h=document,f,g=$.event.special,i=h.documentMode,d="on"+c in e&&(i===b||i>7);function a(j){j=j||location.href;return"#"+j.replace(/^[^#]*#?(.*)$/,"$1")}$.fn[c]=function(j){return j?this.bind(c,j):this.trigger(c)};$.fn[c].delay=50;g[c]=$.extend(g[c],{setup:function(){if(d){return false}$(f.start)},teardown:function(){if(d){return false}$(f.stop)}});f=(function(){var j={},p,m=a(),k=function(q){return q},l=k,o=k;j.start=function(){p||n()};j.stop=function(){p&&clearTimeout(p);p=b};function n(){var r=a(),q=o(m);if(r!==m){l(m=r,q);$(e).trigger(c)}else{if(q!==m){location.href=location.href.replace(/#.*/,"")+q}}p=setTimeout(n,$.fn[c].delay)}$.browser.msie&&!d&&(function(){var q,r;j.start=function(){if(!q){r=$.fn[c].src;r=r&&r+a();q=$('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){r||l(a());n()}).attr("src",r||"javascript:0").insertAfter("body")[0].contentWindow;h.onpropertychange=function(){try{if(event.propertyName==="title"){q.document.title=h.title}}catch(s){}}}};j.stop=k;o=function(){return a(q.location.href)};l=function(v,s){var u=q.document,t=$.fn[c].domain;if(v!==s){u.title=h.title;u.open();t&&u.write('<script>document.domain="'+t+'"<\/script>');u.close();q.location.hash=v}}})();return j})()})(jQuery,this);(function(c){var a=c.scrollTo=function(f,e,d){c(window).scrollTo(f,e,d)};a.defaults={axis:"xy",duration:parseFloat(c.fn.jquery)>=1.3?0:1};a.window=function(d){return c(window)._scrollable()};c.fn._scrollable=function(){return this.map(function(){var e=this,d=!e.nodeName||c.inArray(e.nodeName.toLowerCase(),["iframe","#document","html","body"])!=-1;if(!d){return e}var f=(e.contentWindow||e).document||e.ownerDocument||e;return c.browser.safari||f.compatMode=="BackCompat"?f.body:f.documentElement})};c.fn.scrollTo=function(f,e,d){if(typeof e=="object"){d=e;e=0}if(typeof d=="function"){d={onAfter:d}}if(f=="max"){f=9000000000}d=c.extend({},a.defaults,d);e=e||d.speed||d.duration;d.queue=d.queue&&d.axis.length>1;if(d.queue){e/=2}d.offset=b(d.offset);d.over=b(d.over);return this._scrollable().each(function(){var l=this,j=c(l),k=f,i,g={},m=j.is("html,body");switch(typeof k){case"number":case"string":if(/^([+-]=)?\d+(\.\d+)?(px|%)?$/.test(k)){k=b(k);break}k=c(k,this);case"object":if(k.is||k.style){i=(k=c(k)).offset()}}c.each(d.axis.split(""),function(q,r){var s=r=="x"?"Left":"Top",u=s.toLowerCase(),p="scroll"+s,o=l[p],n=a.max(l,r);if(i){g[p]=i[u]+(m?0:o-j.offset()[u]);if(d.margin){g[p]-=parseInt(k.css("margin"+s))||0;g[p]-=parseInt(k.css("border"+s+"Width"))||0}g[p]+=d.offset[u]||0;if(d.over[u]){g[p]+=k[r=="x"?"width":"height"]()*d.over[u]}}else{var t=k[u];g[p]=t.slice&&t.slice(-1)=="%"?parseFloat(t)/100*n:t}if(/^\d+$/.test(g[p])){g[p]=g[p]<=0?0:Math.min(g[p],n)}if(!q&&d.queue){if(o!=g[p]){h(d.onAfterFirst)}delete g[p]}});h(d.onAfter);function h(n){j.animate(g,e,d.easing,n&&function(){n.call(this,f,d)})}}).end()};a.max=function(j,i){var h=i=="x"?"Width":"Height",e="scroll"+h;if(!c(j).is("html,body")){return j[e]-c(j)[h.toLowerCase()]()}var g="client"+h,f=j.ownerDocument.documentElement,d=j.ownerDocument.body;return Math.max(f[e],d[e])-Math.min(f[g],d[g])};function b(d){return typeof d=="object"?d:{top:d,left:d}}})(jQuery);/*!
PowerTip - v1.2.0 - 2013-04-03
http://stevenbenner.github.com/jquery-powertip/
Copyright (c) 2013 Steven Benner (http://stevenbenner.com/).
Released under MIT license.
https://raw.github.com/stevenbenner/jquery-powertip/master/LICENSE.txt
*/
(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{a(jQuery)}}(function(k){var A=k(document),s=k(window),w=k("body");var n="displayController",e="hasActiveHover",d="forcedOpen",u="hasMouseMove",f="mouseOnToPopup",g="originalTitle",y="powertip",o="powertipjq",l="powertiptarget",E=180/Math.PI;var c={isTipOpen:false,isFixedTipOpen:false,isClosing:false,tipOpenImminent:false,activeHover:null,currentX:0,currentY:0,previousX:0,previousY:0,desyncTimeout:null,mouseTrackingActive:false,delayInProgress:false,windowWidth:0,windowHeight:0,scrollTop:0,scrollLeft:0};var p={none:0,top:1,bottom:2,left:4,right:8};k.fn.powerTip=function(F,N){if(!this.length){return this}if(k.type(F)==="string"&&k.powerTip[F]){return k.powerTip[F].call(this,this,N)}var O=k.extend({},k.fn.powerTip.defaults,F),G=new x(O);h();this.each(function M(){var R=k(this),Q=R.data(y),P=R.data(o),T=R.data(l),S;if(R.data(n)){k.powerTip.destroy(R)}S=R.attr("title");if(!Q&&!T&&!P&&S){R.data(y,S);R.data(g,S);R.removeAttr("title")}R.data(n,new t(R,O,G))});if(!O.manual){this.on({"mouseenter.powertip":function J(P){k.powerTip.show(this,P)},"mouseleave.powertip":function L(){k.powerTip.hide(this)},"focus.powertip":function K(){k.powerTip.show(this)},"blur.powertip":function H(){k.powerTip.hide(this,true)},"keydown.powertip":function I(P){if(P.keyCode===27){k.powerTip.hide(this,true)}}})}return this};k.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:false,popupId:"powerTip",intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:false,offset:10,mouseOnToPopup:false,manual:false};k.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se","n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]};k.powerTip={show:function z(F,G){if(G){i(G);c.previousX=G.pageX;c.previousY=G.pageY;k(F).data(n).show()}else{k(F).first().data(n).show(true,true)}return F},reposition:function r(F){k(F).first().data(n).resetPosition();return F},hide:function D(G,F){if(G){k(G).first().data(n).hide(F)}else{if(c.activeHover){c.activeHover.data(n).hide(true)}}return G},destroy:function C(G){k(G).off(".powertip").each(function F(){var I=k(this),H=[g,n,e,d];if(I.data(g)){I.attr("title",I.data(g));H.push(y)}I.removeData(H)});return G}};k.powerTip.showTip=k.powerTip.show;k.powerTip.closeTip=k.powerTip.hide;function b(){var F=this;F.top="auto";F.left="auto";F.right="auto";F.bottom="auto";F.set=function(H,G){if(k.isNumeric(G)){F[H]=Math.round(G)}}}function t(K,N,F){var J=null;function L(P,Q){M();if(!K.data(e)){if(!P){c.tipOpenImminent=true;J=setTimeout(function O(){J=null;I()},N.intentPollInterval)}else{if(Q){K.data(d,true)}F.showTip(K)}}}function G(P){M();c.tipOpenImminent=false;if(K.data(e)){K.data(d,false);if(!P){c.delayInProgress=true;J=setTimeout(function O(){J=null;F.hideTip(K);c.delayInProgress=false},N.closeDelay)}else{F.hideTip(K)}}}function I(){var Q=Math.abs(c.previousX-c.currentX),O=Math.abs(c.previousY-c.currentY),P=Q+O;if(P<N.intentSensitivity){F.showTip(K)}else{c.previousX=c.currentX;c.previousY=c.currentY;L()}}function M(){J=clearTimeout(J);c.delayInProgress=false}function H(){F.resetPosition(K)}this.show=L;this.hide=G;this.cancel=M;this.resetPosition=H}function j(){function G(M,L,J,O,P){var K=L.split("-")[0],N=new b(),I;if(q(M)){I=H(M,K)}else{I=F(M,K)}switch(L){case"n":N.set("left",I.left-(J/2));N.set("bottom",c.windowHeight-I.top+P);break;case"e":N.set("left",I.left+P);N.set("top",I.top-(O/2));break;case"s":N.set("left",I.left-(J/2));N.set("top",I.top+P);break;case"w":N.set("top",I.top-(O/2));N.set("right",c.windowWidth-I.left+P);break;case"nw":N.set("bottom",c.windowHeight-I.top+P);N.set("right",c.windowWidth-I.left-20);break;case"nw-alt":N.set("left",I.left);N.set("bottom",c.windowHeight-I.top+P);break;case"ne":N.set("left",I.left-20);N.set("bottom",c.windowHeight-I.top+P);break;case"ne-alt":N.set("bottom",c.windowHeight-I.top+P);N.set("right",c.windowWidth-I.left);break;case"sw":N.set("top",I.top+P);N.set("right",c.windowWidth-I.left-20);break;case"sw-alt":N.set("left",I.left);N.set("top",I.top+P);break;case"se":N.set("left",I.left-20);N.set("top",I.top+P);break;case"se-alt":N.set("top",I.top+P);N.set("right",c.windowWidth-I.left);break}return N}function F(K,J){var O=K.offset(),N=K.outerWidth(),I=K.outerHeight(),M,L;switch(J){case"n":M=O.left+N/2;L=O.top;break;case"e":M=O.left+N;L=O.top+I/2;break;case"s":M=O.left+N/2;L=O.top+I;break;case"w":M=O.left;L=O.top+I/2;break;case"nw":M=O.left;L=O.top;break;case"ne":M=O.left+N;L=O.top;break;case"sw":M=O.left;L=O.top+I;bLine truncated
* jQuery UI Touch Punch 0.2.3
*
* Copyright 2011–2014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
(function(b){b.support.touch="ontouchend" in document;if(!b.support.touch){return}var d=b.ui.mouse.prototype,f=d._mouseInit,c=d._mouseDestroy,a;function e(h,i){if(h.originalEvent.touches.length>1){return}h.preventDefault();var j=h.originalEvent.changedTouches[0],g=document.createEvent("MouseEvents");g.initMouseEvent(i,true,true,window,1,j.screenX,j.screenY,j.clientX,j.clientY,false,false,false,false,0,null);h.target.dispatchEvent(g)}d._touchStart=function(h){var g=this;if(a||!g._mouseCapture(h.originalEvent.changedTouches[0])){return}a=true;g._touchMoved=false;e(h,"mouseover");e(h,"mousemove");e(h,"mousedown")};d._touchMove=function(g){if(!a){return}this._touchMoved=true;e(g,"mousemove")};d._touchEnd=function(g){if(!a){return}e(g,"mouseup");e(g,"mouseout");if(!this._touchMoved){e(g,"click")}a=false};d._mouseInit=function(){var g=this;g.element.bind({touchstart:b.proxy(g,"_touchStart"),touchmove:b.proxy(g,"_touchMove"),touchend:b.proxy(g,"_touchEnd")});f.call(g)};d._mouseDestroy=function(){var g=this;g.element.unbind({touchstart:b.proxy(g,"_touchStart"),touchmove:b.proxy(g,"_touchMove"),touchend:b.proxy(g,"_touchEnd")});c.call(g)}})(jQuery);/*!
* SmartMenus jQuery Plugin - v1.0.0 - January 27, 2016
* http://www.smartmenus.org/
*
* Copyright Vasil Dinkov, Vadikom Web Ltd.
* http://vadikom.com
*
* Licensed MIT
*/
(function(a){if(typeof define==="function"&&define.amd){define(["jquery"],a)}else{if(typeof module==="object"&&typeof module.exports==="object"){module.exports=a(require("jquery"))}else{a(jQuery)}}}(function(a){var b=[],e=!!window.createPopup,f=false,d="ontouchstart" in window,h=false,g=window.requestAnimationFrame||function(l){return setTimeout(l,1000/60)},c=window.cancelAnimationFrame||function(l){clearTimeout(l)};function k(m){var n=".smartmenus_mouse";if(!h&&!m){var o=true,l=null;a(document).bind(i([["mousemove",function(s){var t={x:s.pageX,y:s.pageY,timeStamp:new Date().getTime()};if(l){var q=Math.abs(l.x-t.x),p=Math.abs(l.y-t.y);if((q>0||p>0)&&q<=2&&p<=2&&t.timeStamp-l.timeStamp<=300){f=true;if(o){var r=a(s.target).closest("a");if(r.is("a")){a.each(b,function(){if(a.contains(this.$root[0],r[0])){this.itemEnter({currentTarget:r[0]});return false}})}o=false}}}l=t}],[d?"touchstart":"pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut",function(p){if(j(p.originalEvent)){f=false}}]],n));h=true}else{if(h&&m){a(document).unbind(n);h=false}}}function j(l){return !/^(4|mouse)$/.test(l.pointerType)}function i(l,n){if(!n){n=""}var m={};a.each(l,function(o,p){m[p[0].split(" ").join(n+" ")+n]=p[1]});return m}a.SmartMenus=function(m,l){this.$root=a(m);this.opts=l;this.rootId="";this.accessIdPrefix="";this.$subArrow=null;this.activatedItems=[];this.visibleSubMenus=[];this.showTimeout=0;this.hideTimeout=0;this.scrollTimeout=0;this.clickActivated=false;this.focusActivated=false;this.zIndexInc=0;this.idInc=0;this.$firstLink=null;this.$firstSub=null;this.disabled=false;this.$disableOverlay=null;this.$touchScrollingSub=null;this.cssTransforms3d="perspective" in m.style||"webkitPerspective" in m.style;this.wasCollapsible=false;this.init()};a.extend(a.SmartMenus,{hideAll:function(){a.each(b,function(){this.menuHideAll()})},destroy:function(){while(b.length){b[0].destroy()}k(true)},prototype:{init:function(n){var l=this;if(!n){b.push(this);this.rootId=(new Date().getTime()+Math.random()+"").replace(/\D/g,"");this.accessIdPrefix="sm-"+this.rootId+"-";if(this.$root.hasClass("sm-rtl")){this.opts.rightToLeftSubMenus=true}var r=".smartmenus";this.$root.data("smartmenus",this).attr("data-smartmenus-id",this.rootId).dataSM("level",1).bind(i([["mouseover focusin",a.proxy(this.rootOver,this)],["mouseout focusout",a.proxy(this.rootOut,this)],["keydown",a.proxy(this.rootKeyDown,this)]],r)).delegate("a",i([["mouseenter",a.proxy(this.itemEnter,this)],["mouseleave",a.proxy(this.itemLeave,this)],["mousedown",a.proxy(this.itemDown,this)],["focus",a.proxy(this.itemFocus,this)],["blur",a.proxy(this.itemBlur,this)],["click",a.proxy(this.itemClick,this)]],r));r+=this.rootId;if(this.opts.hideOnClick){a(document).bind(i([["touchstart",a.proxy(this.docTouchStart,this)],["touchmove",a.proxy(this.docTouchMove,this)],["touchend",a.proxy(this.docTouchEnd,this)],["click",a.proxy(this.docClick,this)]],r))}a(window).bind(i([["resize orientationchange",a.proxy(this.winResize,this)]],r));if(this.opts.subIndicators){this.$subArrow=a("<span/>").addClass("sub-arrow");if(this.opts.subIndicatorsText){this.$subArrow.html(this.opts.subIndicatorsText)}}k()}this.$firstSub=this.$root.find("ul").each(function(){l.menuInit(a(this))}).eq(0);this.$firstLink=this.$root.find("a").eq(0);if(this.opts.markCurrentItem){var p=/(index|default)\.[^#\?\/]*/i,m=/#.*/,q=window.location.href.replace(p,""),o=q.replace(m,"");this.$root.find("a").each(function(){var s=this.href.replace(p,""),t=a(this);if(s==q||s==o){t.addClass("current");if(l.opts.markCurrentTree){t.parentsUntil("[data-smartmenus-id]","ul").each(function(){a(this).dataSM("parent-a").addClass("current")})}}})}this.wasCollapsible=this.isCollapsible()},destroy:function(m){if(!m){var n=".smartmenus";this.$root.removeData("smartmenus").removeAttr("data-smartmenus-id").removeDataSM("level").unbind(n).undelegate(n);n+=this.rootId;a(document).unbind(n);a(window).unbind(n);if(this.opts.subIndicators){this.$subArrow=null}}this.menuHideAll();var l=this;this.$root.find("ul").each(function(){var o=a(this);if(o.dataSM("scroll-arrows")){o.dataSM("scroll-arrows").remove()}if(o.dataSM("shown-before")){if(l.opts.subMenusMinWidth||l.opts.subMenusMaxWidth){o.css({width:"",minWidth:"",maxWidth:""}).removeClass("sm-nowrap")}if(o.dataSM("scroll-arrows")){o.dataSM("scroll-arrows").remove()}o.css({zIndex:"",top:"",left:"",marginLeft:"",marginTop:"",display:""})}if((o.attr("id")||"").indexOf(l.accessIdPrefix)==0){o.removeAttr("id")}}).removeDataSM("in-mega").removeDataSM("shown-before").removeDataSM("ie-shim").removeDataSM("scroll-arrows").removeDataSM("parent-a").removeDataSM("level").removeDataSM("beforefirstshowfired").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeAttr("aria-expanded");this.$root.find("a.has-submenu").each(function(){var o=a(this);if(o.attr("id").indexOf(l.accessIdPrefix)==0){o.removeAttr("id")}}).removeClass("has-submenu").removeDataSM("sub").removeAttr("aria-haspLine truncated
-256
View File
@@ -1,256 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.15"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>mi-malloc: mimalloc-doc.h Source File</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="resize.js"></script>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(document).ready(initResizable);
/* @license-end */</script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(document).ready(function() { init_search(); });
/* @license-end */
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">mi-malloc
&#160;<span id="projectnumber">1.6</span>
</div>
</td>
<td> <div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.15 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
var searchBox = new SearchBox("searchBox", "search",false,'Search');
/* @license-end */
</script>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
<div id="nav-tree">
<div id="nav-tree-contents">
<div id="nav-sync" class="sync"></div>
</div>
</div>
<div id="splitbar" style="-moz-user-select:none;"
class="ui-resizable-handle">
</div>
</div>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(document).ready(function(){initNavTree('mimalloc-doc_8h_source.html','');});
/* @license-end */
</script>
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="header">
<div class="headertitle">
<div class="title">mimalloc-doc.h</div> </div>
</div><!--header-->
<div class="contents">
<div class="fragment"><div class="line"><a name="l00001"></a><span class="lineno"> 1</span>&#160;<span class="comment">/* ----------------------------------------------------------------------------</span></div><div class="line"><a name="l00002"></a><span class="lineno"> 2</span>&#160;<span class="comment">Copyright (c) 2018, Microsoft Research, Daan Leijen</span></div><div class="line"><a name="l00003"></a><span class="lineno"> 3</span>&#160;<span class="comment">This is free software; you can redistribute it and/or modify it under the</span></div><div class="line"><a name="l00004"></a><span class="lineno"> 4</span>&#160;<span class="comment">terms of the MIT license. A copy of the license can be found in the file</span></div><div class="line"><a name="l00005"></a><span class="lineno"> 5</span>&#160;<span class="comment">&quot;LICENSE&quot; at the root of this distribution.</span></div><div class="line"><a name="l00006"></a><span class="lineno"> 6</span>&#160;<span class="comment">-----------------------------------------------------------------------------*/</span></div><div class="line"><a name="l00007"></a><span class="lineno"> 7</span>&#160;</div><div class="line"><a name="l00008"></a><span class="lineno"> 8</span>&#160;<span class="preprocessor">#error &quot;documentation file only!&quot;</span></div><div class="line"><a name="l00009"></a><span class="lineno"> 9</span>&#160;</div><div class="line"><a name="l00010"></a><span class="lineno"> 10</span>&#160;</div><div class="line"><a name="l00091"></a><span class="lineno"> 91</span>&#160;</div><div class="line"><a name="l00095"></a><span class="lineno"> 95</span>&#160;</div><div class="line"><a name="l00099"></a><span class="lineno"> 99</span>&#160;<span class="keywordtype">void</span> <a class="code" href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95">mi_free</a>(<span class="keywordtype">void</span>* p);</div><div class="line"><a name="l00100"></a><span class="lineno"> 100</span>&#160;</div><div class="line"><a name="l00105"></a><span class="lineno"> 105</span>&#160;<span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a">mi_malloc</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00106"></a><span class="lineno"> 106</span>&#160;</div><div class="line"><a name="l00111"></a><span class="lineno"> 111</span>&#160;<span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000">mi_zalloc</a>(<span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00112"></a><span class="lineno"> 112</span>&#160;</div><div class="line"><a name="l00122"></a><span class="lineno"> 122</span>&#160;<span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d">mi_calloc</a>(<span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00123"></a><span class="lineno"> 123</span>&#160;</div><div class="line"><a name="l00136"></a><span class="lineno"> 136</span>&#160;<span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6">mi_realloc</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00137"></a><span class="lineno"> 137</span>&#160;</div><div class="line"><a name="l00148"></a><span class="lineno"> 148</span>&#160;<span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc">mi_recalloc</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00149"></a><span class="lineno"> 149</span>&#160;</div><div class="line"><a name="l00163"></a><span class="lineno"> 163</span>&#160;<span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4">mi_expand</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><a name="l00164"></a><span class="lineno"> 164</span>&#160;</div><div class="line"><a name="l00174"></a><span class="lineno"> 174</span>&#160;<span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6">mi_mallocn</a>(<span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size);</div><div class="line"><a name="l00175"></a><span class="lineno"> 175</span>&#160;</div><div class="line"><a name="l00185"></a><span class="lineno"> 185</span>&#160;<span class="keywordtype">void</span>* <a class="code" href="group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853">mi_reallocn</a>(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</spLine truncated
<div class="ttc" id="group__cpp_html_gaeaded64eda71ed6b1d569d3e723abc4a"><div class="ttname"><a href="group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a">mi_new_nothrow</a></div><div class="ttdeci">void * mi_new_nothrow(size_t n)</div><div class="ttdoc">like mi_malloc, but when out of memory, use std::get_new_handler but return NULL on failure.</div></div>
<div class="ttc" id="group__malloc_html_ga61d57b4144ba24fba5c1e9b956d13853"><div class="ttname"><a href="group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853">mi_reallocn</a></div><div class="ttdeci">void * mi_reallocn(void *p, size_t count, size_t size)</div><div class="ttdoc">Re-allocate memory to count elements of size bytes.</div></div>
<div class="ttc" id="group__aligned_html_ga68930196751fa2cca9e1fd0d71bade56"><div class="ttname"><a href="group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56">mi_malloc_aligned</a></div><div class="ttdeci">void * mi_malloc_aligned(size_t size, size_t alignment)</div><div class="ttdoc">Allocate size bytes aligned by alignment.</div></div>
<div class="ttc" id="group__zeroinit_html_ga4ff5e92ad73585418a072c9d059e5cf9"><div class="ttname"><a href="group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9">mi_recalloc_aligned_at</a></div><div class="ttdeci">void * mi_recalloc_aligned_at(void *p, size_t newcount, size_t size, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__extended_html_ga3bb8468b8cfcc6e2a61d98aee85c5f99"><div class="ttname"><a href="group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99">mi_stats_reset</a></div><div class="ttdeci">void mi_stats_reset(void)</div><div class="ttdoc">Reset statistics.</div></div>
<div class="ttc" id="group__heap_html_gafc603b696bd14cae6da28658f950d98c"><div class="ttname"><a href="group__heap.html#gafc603b696bd14cae6da28658f950d98c">mi_heap_realloc_aligned</a></div><div class="ttdeci">void * mi_heap_realloc_aligned(mi_heap_t *heap, void *p, size_t newsize, size_t alignment)</div></div>
<div class="ttc" id="group__options_html_ga459ad98f18b3fc9275474807fe0ca188"><div class="ttname"><a href="group__options.html#ga459ad98f18b3fc9275474807fe0ca188">mi_option_is_enabled</a></div><div class="ttdeci">bool mi_option_is_enabled(mi_option_t option)</div></div>
<div class="ttc" id="group__cpp_html_gaab78a32f55149e9fbf432d5288e38e1e"><div class="ttname"><a href="group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e">mi_new_realloc</a></div><div class="ttdeci">void * mi_new_realloc(void *p, size_t newsize)</div><div class="ttdoc">like mi_realloc(), but when out of memory, use std::get_new_handler and raise std::bad_alloc exceptio...</div></div>
<div class="ttc" id="group__malloc_html_ga23a0fbb452b5dce8e31fab1a1958cacc"><div class="ttname"><a href="group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc">mi_recalloc</a></div><div class="ttdeci">void * mi_recalloc(void *p, size_t count, size_t size)</div><div class="ttdoc">Re-allocate memory to count elements of size bytes, with extra memory initialized to zero.</div></div>
<div class="ttc" id="group__malloc_html_ga0b05e2bf0f73e7401ae08597ff782ac6"><div class="ttname"><a href="group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6">mi_mallocn</a></div><div class="ttdeci">void * mi_mallocn(size_t count, size_t size)</div><div class="ttdoc">Allocate count elements of size bytes.</div></div>
<div class="ttc" id="group__posix_html_ga4531c9e775bb3ae12db57c1ba8a5d7de"><div class="ttname"><a href="group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de">mi_malloc_size</a></div><div class="ttdeci">size_t mi_malloc_size(const void *p)</div></div>
<div class="ttc" id="group__options_html_ga9a13d05fcb77489cb06d4d017ebd8bed"><div class="ttname"><a href="group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed">mi_option_set_enabled</a></div><div class="ttdeci">void mi_option_set_enabled(mi_option_t option, bool enable)</div></div>
<div class="ttc" id="group__posix_html_gacff84f226ba9feb2031b8992e5579447"><div class="ttname"><a href="group__posix.html#gacff84f226ba9feb2031b8992e5579447">mi_posix_memalign</a></div><div class="ttdeci">int mi_posix_memalign(void **p, size_t alignment, size_t size)</div></div>
<div class="ttc" id="group__extended_html_ga854b1de8cb067c7316286c28b2fcd3d1"><div class="ttname"><a href="group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1">mi_stats_merge</a></div><div class="ttdeci">void mi_stats_merge(void)</div><div class="ttdoc">Merge thread local statistics with the main statistics and reset.</div></div>
<div class="ttc" id="group__cpp_html_gae7bc4f56cd57ed3359060ff4f38bda81"><div class="ttname"><a href="group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81">mi_new_n</a></div><div class="ttdeci">void * mi_new_n(size_t count, size_t size) noexcept(false)</div><div class="ttdoc">like mi_mallocn(), but when out of memory, use std::get_new_handler and raise std::bad_alloc exceptio...</div></div>
<div class="ttc" id="group__options_html_ga7ef623e440e6e5545cb08c94e71e4b90"><div class="ttname"><a href="group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90">mi_option_set_default</a></div><div class="ttdeci">void mi_option_set_default(mi_option_t option, long value)</div></div>
<div class="ttc" id="group__extended_html_ga537f13b299ddf801e49a5a94fde02c79"><div class="ttname"><a href="group__extended.html#ga537f13b299ddf801e49a5a94fde02c79">mi_stats_print_out</a></div><div class="ttdeci">void mi_stats_print_out(mi_output_fun *out, void *arg)</div><div class="ttdoc">Print the main statistics.</div></div>
<div class="ttc" id="group__extended_html_ga251d369cda3f1c2a955c555486ed90e5"><div class="ttname"><a href="group__extended.html#ga251d369cda3f1c2a955c555486ed90e5">mi_error_fun</a></div><div class="ttdeci">void() mi_error_fun(int err, void *arg)</div><div class="ttdoc">Type of error callback functions.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:391</div></div>
<div class="ttc" id="group__zeroinit_html_ga8c292e142110229a2980b37ab036dbc6"><div class="ttname"><a href="group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6">mi_rezalloc</a></div><div class="ttdeci">void * mi_rezalloc(void *p, size_t newsize)</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b">mi_option_eager_commit</a></div><div class="ttdoc">Eagerly commit segments (4MiB) (enabled by default).</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:781</div></div>
<div class="ttc" id="group__heap_html_ga903104592c8ed53417a3762da6241133"><div class="ttname"><a href="group__heap.html#ga903104592c8ed53417a3762da6241133">mi_heap_zalloc</a></div><div class="ttdeci">void * mi_heap_zalloc(mi_heap_t *heap, size_t size)</div><div class="ttdoc">Allocate zero-initialized in a specific heap.</div></div>
<div class="ttc" id="group__options_html_gaf84921c32375e25754dc2ee6a911fa60"><div class="ttname"><a href="group__options.html#gaf84921c32375e25754dc2ee6a911fa60">mi_option_set</a></div><div class="ttdeci">void mi_option_set(mi_option_t option, long value)</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad">mi_option_eager_region_commit</a></div><div class="ttdoc">Eagerly commit large (256MiB) memory regions (enabled by default, except on Windows)</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:782</div></div>
<div class="ttc" id="group__posix_html_ga705dc7a64bffacfeeb0141501a5c35d7"><div class="ttname"><a href="group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7">mi_cfree</a></div><div class="ttdeci">void mi_cfree(void *p)</div><div class="ttdoc">Just as free but also checks if the pointer p belongs to our heap.</div></div>
<div class="ttc" id="group__zeroinit_html_ga3e7e5c291acf1c7fd7ffd9914a9f945f"><div class="ttname"><a href="group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f">mi_recalloc_aligned</a></div><div class="ttdeci">void * mi_recalloc_aligned(void *p, size_t newcount, size_t size, size_t alignment)</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a">_mi_option_last</a></div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:793</div></div>
<div class="ttc" id="group__aligned_html_gaf66a9ae6c6f08bd6be6fb6ea771faffb"><div class="ttname"><a href="group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb">mi_realloc_aligned_at</a></div><div class="ttdeci">void * mi_realloc_aligned_at(void *p, size_t newsize, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__analysis_html_ae0085e6e1cf059a4eb7767e30e9991b8"><div class="ttname"><a href="group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8">mi_heap_area_t::blocks</a></div><div class="ttdeci">void * blocks</div><div class="ttdoc">start of the area containing heap blocks</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:737</div></div>
<div class="ttc" id="group__aligned_html_ga4028d1cf4aa4c87c880747044a8322ae"><div class="ttname"><a href="group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae">mi_realloc_aligned</a></div><div class="ttdeci">void * mi_realloc_aligned(void *p, size_t newsize, size_t alignment)</div></div>
<div class="ttc" id="group__options_html_ga04180ae41b0d601421dd62ced40ca050"><div class="ttname"><a href="group__options.html#ga04180ae41b0d601421dd62ced40ca050">mi_option_enable</a></div><div class="ttdeci">void mi_option_enable(mi_option_t option)</div></div>
<div class="ttc" id="group__posix_html_gad5a69c8fea96aa2b7a7c818c2130090a"><div class="ttname"><a href="group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a">mi__posix_memalign</a></div><div class="ttdeci">int mi__posix_memalign(void **p, size_t alignment, size_t size)</div></div>
<div class="ttc" id="group__malloc_html_gaf2c7b89c327d1f60f59e68b9ea644d95"><div class="ttname"><a href="group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95">mi_free</a></div><div class="ttdeci">void mi_free(void *p)</div><div class="ttdoc">Free previously allocated memory.</div></div>
<div class="ttc" id="group__heap_html_ga139d6b09dbf50c3c2523d0f4d1cfdeb5"><div class="ttname"><a href="group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5">mi_heap_strdup</a></div><div class="ttdeci">char * mi_heap_strdup(mi_heap_t *heap, const char *s)</div><div class="ttdoc">Duplicate a string in a specific heap.</div></div>
<div class="ttc" id="group__heap_html_ga00e95ba1e01acac3cfd95bb7a357a6f0"><div class="ttname"><a href="group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0">mi_heap_realpath</a></div><div class="ttdeci">char * mi_heap_realpath(mi_heap_t *heap, const char *fname, char *resolved_name)</div><div class="ttdoc">Resolve a file path name using a specific heap to allocate the result.</div></div>
<div class="ttc" id="group__heap_html_ga08ca6419a5c057a4d965868998eef487"><div class="ttname"><a href="group__heap.html#ga08ca6419a5c057a4d965868998eef487">mi_heap_calloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_calloc_aligned_at(mi_heap_t *heap, size_t count, size_t size, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__extended_html_ga7d862c2affd5790381da14eb102a364d"><div class="ttname"><a href="group__extended.html#ga7d862c2affd5790381da14eb102a364d">mi_process_info</a></div><div class="ttdeci">void mi_process_info(size_t *elapsed_msecs, size_t *user_msecs, size_t *system_msecs, size_t *current_rss, size_t *peak_rss, size_t *current_commit, size_t *peak_commit, size_t *page_faults)</div><div class="ttdoc">Return process information (time and memory usage).</div></div>
<div class="ttc" id="group__aligned_html_ga53dddb4724042a90315b94bc268fb4c9"><div class="ttname"><a href="group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9">mi_calloc_aligned</a></div><div class="ttdeci">void * mi_calloc_aligned(size_t count, size_t size, size_t alignment)</div></div>
<div class="ttc" id="group__heap_html_gaa450a59c6c7ae5fdbd1c2b80a8329ef0"><div class="ttname"><a href="group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0">mi_heap_zalloc_aligned</a></div><div class="ttdeci">void * mi_heap_zalloc_aligned(mi_heap_t *heap, size_t size, size_t alignment)</div></div>
<div class="ttc" id="group__extended_html_ga220f29f40a44404b0061c15bc1c31152"><div class="ttname"><a href="group__extended.html#ga220f29f40a44404b0061c15bc1c31152">mi_zalloc_small</a></div><div class="ttdeci">void * mi_zalloc_small(size_t size)</div><div class="ttdoc">Allocate a zero initialized small object.</div></div>
<div class="ttc" id="group__malloc_html_gaaabf971c2571891433477e2d21a35266"><div class="ttname"><a href="group__malloc.html#gaaabf971c2571891433477e2d21a35266">mi_strndup</a></div><div class="ttdeci">char * mi_strndup(const char *s, size_t n)</div><div class="ttdoc">Allocate and duplicate a string up to n bytes.</div></div>
<div class="ttc" id="group__malloc_html_gaaee66a1d483c3e28f585525fb96707e4"><div class="ttname"><a href="group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4">mi_expand</a></div><div class="ttdeci">void * mi_expand(void *p, size_t newsize)</div><div class="ttdoc">Try to re-allocate memory to newsize bytes in place.</div></div>
<div class="ttc" id="group__posix_html_gaeb325c39b887d3b90d85d1eb1712fb1e"><div class="ttname"><a href="group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e">mi_pvalloc</a></div><div class="ttdeci">void * mi_pvalloc(size_t size)</div></div>
<div class="ttc" id="group__options_html_ga65518b69ec5d32336b50e07f74b3f629"><div class="ttname"><a href="group__options.html#ga65518b69ec5d32336b50e07f74b3f629">mi_option_set_enabled_default</a></div><div class="ttdeci">void mi_option_set_enabled_default(mi_option_t option, bool enable)</div></div>
<div class="ttc" id="group__zeroinit_html_gac90da54fa7e5d10bdc97ce0b51dce2eb"><div class="ttname"><a href="group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb">mi_heap_rezalloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_rezalloc_aligned_at(mi_heap_t *heap, void *p, size_t newsize, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__malloc_html_gafdd9d8bb2986e668ba9884f28af38000"><div class="ttname"><a href="group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000">mi_zalloc</a></div><div class="ttdeci">void * mi_zalloc(size_t size)</div><div class="ttdoc">Allocate zero-initialized size bytes.</div></div>
<div class="ttc" id="group__zeroinit_html_gacfad83f14eb5d6a42a497a898e19fc76"><div class="ttname"><a href="group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76">mi_heap_rezalloc</a></div><div class="ttdeci">void * mi_heap_rezalloc(mi_heap_t *heap, void *p, size_t newsize)</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1">mi_option_segment_cache</a></div><div class="ttdoc">The number of segments per thread to keep cached.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:785</div></div>
<div class="ttc" id="group__heap_html_gaa6702b3c48e9e53e50e81b36f5011d55"><div class="ttname"><a href="group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55">mi_heap_calloc</a></div><div class="ttdeci">void * mi_heap_calloc(mi_heap_t *heap, size_t count, size_t size)</div><div class="ttdoc">Allocate count zero-initialized elements in a specific heap.</div></div>
<div class="ttc" id="group__heap_html_ga4af03a6e2b93fae77424d93f889705c3"><div class="ttname"><a href="group__heap.html#ga4af03a6e2b93fae77424d93f889705c3">mi_heap_calloc_aligned</a></div><div class="ttdeci">void * mi_heap_calloc_aligned(mi_heap_t *heap, size_t count, size_t size, size_t alignment)</div></div>
<div class="ttc" id="group__extended_html_gaad25050b19f30cd79397b227e0157a3f"><div class="ttname"><a href="group__extended.html#gaad25050b19f30cd79397b227e0157a3f">mi_is_redirected</a></div><div class="ttdeci">bool mi_is_redirected()</div><div class="ttdoc">Is the C runtime malloc API redirected?</div></div>
<div class="ttc" id="group__analysis_html_a332a6c14d736a99699d5453a1cb04b41"><div class="ttname"><a href="group__analysis.html#a332a6c14d736a99699d5453a1cb04b41">mi_heap_area_t::block_size</a></div><div class="ttdeci">size_t block_size</div><div class="ttdoc">size in bytes of one block</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:741</div></div>
<div class="ttc" id="group__posix_html_ga48fad8648a2f1dab9c87ea9448a52088"><div class="ttname"><a href="group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088">mi_reallocarray</a></div><div class="ttdeci">void * mi_reallocarray(void *p, size_t count, size_t size)</div></div>
<div class="ttc" id="group__extended_html_ga3132f521fb756fc0e8ec0b74fb58df50"><div class="ttname"><a href="group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50">mi_reserve_huge_os_pages_interleave</a></div><div class="ttdeci">int mi_reserve_huge_os_pages_interleave(size_t pages, size_t numa_nodes, size_t timeout_msecs)</div><div class="ttdoc">Reserve pages of huge OS pages (1GiB) evenly divided over numa_nodes nodes, but stops after at most t...</div></div>
<div class="ttc" id="group__extended_html_ga299dae78d25ce112e384a98b7309c5be"><div class="ttname"><a href="group__extended.html#ga299dae78d25ce112e384a98b7309c5be">mi_deferred_free_fun</a></div><div class="ttdeci">void() mi_deferred_free_fun(bool force, unsigned long long heartbeat, void *arg)</div><div class="ttdoc">Type of deferred free functions.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:352</div></div>
<div class="ttc" id="group__extended_html_ga5f071b10d4df1c3658e04e7fd67a94e6"><div class="ttname"><a href="group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6">mi_is_in_heap_region</a></div><div class="ttdeci">bool mi_is_in_heap_region(const void *p)</div><div class="ttdoc">Is a pointer part of our heap?</div></div>
<div class="ttc" id="group__cpp_html_gaef2c2bdb4f70857902d3c8903ac095f3"><div class="ttname"><a href="group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3">mi_new_aligned</a></div><div class="ttdeci">void * mi_new_aligned(std::size_t n, std::align_val_t alignment) noexcept(false)</div><div class="ttdoc">like mi_malloc_aligned(), but when out of memory, use std::get_new_handler and raise std::bad_alloc e...</div></div>
<div class="ttc" id="group__malloc_html_gaf11eb497da57bdfb2de65eb191c69db6"><div class="ttname"><a href="group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6">mi_realloc</a></div><div class="ttdeci">void * mi_realloc(void *p, size_t newsize)</div><div class="ttdoc">Re-allocate memory to newsize bytes.</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2">mi_option_reserve_huge_os_pages</a></div><div class="ttdoc">The number of huge OS pages (1GiB in size) to reserve at the start of the program.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:784</div></div>
<div class="ttc" id="group__heap_html_ga4a21070eb4e7cce018133c8d5f4b0527"><div class="ttname"><a href="group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527">mi_heap_reallocf</a></div><div class="ttdeci">void * mi_heap_reallocf(mi_heap_t *heap, void *p, size_t newsize)</div></div>
<div class="ttc" id="group__posix_html_ga72e9d7ffb5fe94d69bc722c8506e27bc"><div class="ttname"><a href="group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc">mi_free_size_aligned</a></div><div class="ttdeci">void mi_free_size_aligned(void *p, size_t size, size_t alignment)</div></div>
<div class="ttc" id="group__zeroinit_html_gae8b358c417e61d5307da002702b0a8e1"><div class="ttname"><a href="group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1">mi_rezalloc_aligned_at</a></div><div class="ttdeci">void * mi_rezalloc_aligned_at(void *p, size_t newsize, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968">mi_option_page_reset</a></div><div class="ttdoc">Reset page memory after mi_option_reset_delay milliseconds when it becomes free.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:786</div></div>
<div class="ttc" id="group__extended_html_ga0ae4581e85453456a0d658b2b98bf7bf"><div class="ttname"><a href="group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf">mi_thread_done</a></div><div class="ttdeci">void mi_thread_done(void)</div><div class="ttdoc">Uninitialize mimalloc on a thread.</div></div>
<div class="ttc" id="group__analysis_html_ga70c46687dc6e9dc98b232b02646f8bed"><div class="ttname"><a href="group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed">mi_heap_visit_blocks</a></div><div class="ttdeci">bool mi_heap_visit_blocks(const mi_heap_t *heap, bool visit_all_blocks, mi_block_visit_fun *visitor, void *arg)</div><div class="ttdoc">Visit all areas and blocks in a heap.</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74">mi_option_use_numa_nodes</a></div><div class="ttdoc">Pretend there are at most N NUMA nodes.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:789</div></div>
<div class="ttc" id="group__malloc_html_ga3406e8b168bc74c8637b11571a6da83a"><div class="ttname"><a href="group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a">mi_malloc</a></div><div class="ttdeci">void * mi_malloc(size_t size)</div><div class="ttdoc">Allocate size bytes.</div></div>
<div class="ttc" id="group__extended_html_gaa1d55e0e894be240827e5d87ec3a1f45"><div class="ttname"><a href="group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45">mi_register_error</a></div><div class="ttdeci">void mi_register_error(mi_error_fun *errfun, void *arg)</div><div class="ttdoc">Register an error callback function.</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536">mi_option_reset_decommits</a></div><div class="ttdoc">Experimental.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:790</div></div>
<div class="ttc" id="group__heap_html_ga8e3dbd46650dd26573cf307a2c8f1f5a"><div class="ttname"><a href="group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a">mi_heap_strndup</a></div><div class="ttdeci">char * mi_heap_strndup(mi_heap_t *heap, const char *s, size_t n)</div><div class="ttdoc">Duplicate a string of at most length n in a specific heap.</div></div>
<div class="ttc" id="group__analysis_html_gadfa01e2900f0e5d515ad5506b26f6d65"><div class="ttname"><a href="group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65">mi_block_visit_fun</a></div><div class="ttdeci">bool() mi_block_visit_fun(const mi_heap_t *heap, const mi_heap_area_t *area, void *block, size_t block_size, void *arg)</div><div class="ttdoc">Visitor function passed to mi_heap_visit_blocks()</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:751</div></div>
<div class="ttc" id="group__zeroinit_html_ga8648c5fbb22a80f0262859099f06dfbd"><div class="ttname"><a href="group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd">mi_heap_recalloc</a></div><div class="ttdeci">void * mi_heap_recalloc(mi_heap_t *heap, void *p, size_t newcount, size_t size)</div></div>
<div class="ttc" id="group__heap_html_ga23acd7680fb0976dde3783254c6c874b"><div class="ttname"><a href="group__heap.html#ga23acd7680fb0976dde3783254c6c874b">mi_heap_malloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_malloc_aligned_at(mi_heap_t *heap, size_t size, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__malloc_html_ga08cec32dd5bbe7da91c78d19f1b5bebe"><div class="ttname"><a href="group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe">mi_realpath</a></div><div class="ttdeci">char * mi_realpath(const char *fname, char *resolved_name)</div><div class="ttdoc">Resolve a file path name.</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0">mi_option_show_errors</a></div><div class="ttdoc">Print error messages to stderr.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:777</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d">mi_option_segment_reset</a></div><div class="ttdoc">Experimental.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:787</div></div>
<div class="ttc" id="group__zeroinit_html_ga375fa8a611c51905e592d5d467c49664"><div class="ttname"><a href="group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664">mi_heap_rezalloc_aligned</a></div><div class="ttdeci">void * mi_heap_rezalloc_aligned(mi_heap_t *heap, void *p, size_t newsize, size_t alignment)</div></div>
<div class="ttc" id="group__cpp_html_gab5e29558926d934c3f1cae8c815f942c"><div class="ttname"><a href="group__cpp.html#gab5e29558926d934c3f1cae8c815f942c">mi_new_aligned_nothrow</a></div><div class="ttdeci">void * mi_new_aligned_nothrow(size_t n, size_t alignment)</div><div class="ttdoc">like mi_malloc_aligned, but when out of memory, use std::get_new_handler but return NULL on failure.</div></div>
<div class="ttc" id="group__posix_html_gaab7fa71ea93b96873f5d9883db57d40e"><div class="ttname"><a href="group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e">mi_memalign</a></div><div class="ttdeci">void * mi_memalign(size_t alignment, size_t size)</div></div>
<div class="ttc" id="group__zeroinit_html_gacd71a7bce96aab38ae6de17af2eb2cf0"><div class="ttname"><a href="group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0">mi_rezalloc_aligned</a></div><div class="ttdeci">void * mi_rezalloc_aligned(void *p, size_t newsize, size_t alignment)</div></div>
<div class="ttc" id="group__analysis_html_gaa862aa8ed8d57d84cae41fc1022d71af"><div class="ttname"><a href="group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af">mi_heap_contains_block</a></div><div class="ttdeci">bool mi_heap_contains_block(mi_heap_t *heap, const void *p)</div><div class="ttdoc">Does a heap contain a pointer to a previously allocated block?</div></div>
<div class="ttc" id="group__heap_html_ga7922f7495cde30b1984d0e6072419298"><div class="ttname"><a href="group__heap.html#ga7922f7495cde30b1984d0e6072419298">mi_heap_collect</a></div><div class="ttdeci">void mi_heap_collect(mi_heap_t *heap, bool force)</div><div class="ttdoc">Release outstanding resources in a specific heap.</div></div>
<div class="ttc" id="group__zeroinit_html_ga496452c96f1de8c500be9fddf52edaf7"><div class="ttname"><a href="group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7">mi_heap_recalloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_recalloc_aligned_at(mi_heap_t *heap, void *p, size_t newcount, size_t size, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777">mi_option_verbose</a></div><div class="ttdoc">Print verbose messages to stderr.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:779</div></div>
<div class="ttc" id="group__aligned_html_ga5f8c2353766db522565e642fafd8a3f8"><div class="ttname"><a href="group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8">mi_zalloc_aligned_at</a></div><div class="ttdeci">void * mi_zalloc_aligned_at(size_t size, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__aligned_html_ga5850da130c936bd77db039dcfbc8295d"><div class="ttname"><a href="group__aligned.html#ga5850da130c936bd77db039dcfbc8295d">mi_malloc_aligned_at</a></div><div class="ttdeci">void * mi_malloc_aligned_at(size_t size, size_t alignment, size_t offset)</div><div class="ttdoc">Allocate size bytes aligned by alignment at a specified offset.</div></div>
<div class="ttc" id="group__heap_html_ga2ab1af8d438819b55319c7ef51d1e409"><div class="ttname"><a href="group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409">mi_heap_delete</a></div><div class="ttdeci">void mi_heap_delete(mi_heap_t *heap)</div><div class="ttdoc">Delete a previously allocated heap.</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf">mi_option_os_tag</a></div><div class="ttdoc">OS tag to assign to mimalloc'd memory.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:792</div></div>
<div class="ttc" id="group__heap_html_ga8db4cbb87314a989a9a187464d6b5e05"><div class="ttname"><a href="group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05">mi_heap_get_default</a></div><div class="ttdeci">mi_heap_t * mi_heap_get_default()</div><div class="ttdoc">Get the default heap that is used for mi_malloc() et al.</div></div>
<div class="ttc" id="group__extended_html_ga7795a13d20087447281858d2c771cca1"><div class="ttname"><a href="group__extended.html#ga7795a13d20087447281858d2c771cca1">mi_reserve_huge_os_pages_at</a></div><div class="ttdeci">int mi_reserve_huge_os_pages_at(size_t pages, int numa_node, size_t timeout_msecs)</div><div class="ttdoc">Reserve pages of huge OS pages (1GiB) at a specific numa_node, but stops after at most timeout_msecs ...</div></div>
<div class="ttc" id="group__options_html_gaebf6ff707a2e688ebb1a2296ca564054"><div class="ttname"><a href="group__options.html#gaebf6ff707a2e688ebb1a2296ca564054">mi_option_disable</a></div><div class="ttdeci">void mi_option_disable(mi_option_t option)</div></div>
<div class="ttc" id="group__posix_html_ga1326d2e4388630b5f81ca7206318b8e5"><div class="ttname"><a href="group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5">mi_aligned_alloc</a></div><div class="ttdeci">void * mi_aligned_alloc(size_t alignment, size_t size)</div></div>
<div class="ttc" id="group__posix_html_ga73baaf5951f5165ba0763d0c06b6a93b"><div class="ttname"><a href="group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b">mi_valloc</a></div><div class="ttdeci">void * mi_valloc(size_t size)</div></div>
<div class="ttc" id="group__extended_html_gaf8e73efc2cbca9ebfdfb166983a04c17"><div class="ttname"><a href="group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17">mi_thread_init</a></div><div class="ttdeci">void mi_thread_init(void)</div><div class="ttdoc">Initialize mimalloc on a thread.</div></div>
<div class="ttc" id="group__extended_html_gac057927cd06c854b45fe7847e921bd47"><div class="ttname"><a href="group__extended.html#gac057927cd06c854b45fe7847e921bd47">mi_good_size</a></div><div class="ttdeci">size_t mi_good_size(size_t size)</div><div class="ttdoc">Return the used allocation size.</div></div>
<div class="ttc" id="group__extended_html_ga2d126e5c62d3badc35445e5d84166df2"><div class="ttname"><a href="group__extended.html#ga2d126e5c62d3badc35445e5d84166df2">mi_stats_print</a></div><div class="ttdeci">void mi_stats_print(void *out)</div><div class="ttdoc">Deprecated.</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c">mi_option_eager_commit_delay</a></div><div class="ttdoc">Experimental.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:791</div></div>
<div class="ttc" id="group__zeroinit_html_ga9f3f999396c8f77ca5e80e7b40ac29e3"><div class="ttname"><a href="group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3">mi_heap_recalloc_aligned</a></div><div class="ttdeci">void * mi_heap_recalloc_aligned(mi_heap_t *heap, void *p, size_t newcount, size_t size, size_t alignment)</div></div>
<div class="ttc" id="group__heap_html_ga851da6c43fe0b71c1376cee8aef90db0"><div class="ttname"><a href="group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0">mi_heap_mallocn</a></div><div class="ttdeci">void * mi_heap_mallocn(mi_heap_t *heap, size_t count, size_t size)</div><div class="ttdoc">Allocate count elements in a specific heap.</div></div>
<div class="ttc" id="group__analysis_html_structmi__heap__area__t"><div class="ttname"><a href="group__analysis.html#structmi__heap__area__t">mi_heap_area_t</a></div><div class="ttdoc">An area of heap space contains blocks of a single size.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:736</div></div>
<div class="ttc" id="group__extended_html_gab1dac8476c46cb9eecab767eb40c1525"><div class="ttname"><a href="group__extended.html#gab1dac8476c46cb9eecab767eb40c1525">mi_thread_stats_print_out</a></div><div class="ttdeci">void mi_thread_stats_print_out(mi_output_fun *out, void *arg)</div><div class="ttdoc">Print out heap statistics for this thread.</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda">mi_option_show_stats</a></div><div class="ttdoc">Print statistics to stderr when the program is done.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:778</div></div>
<div class="ttc" id="group__aligned_html_ga0cadbcf5b89a7b6fb171bc8df8734819"><div class="ttname"><a href="group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819">mi_zalloc_aligned</a></div><div class="ttdeci">void * mi_zalloc_aligned(size_t size, size_t alignment)</div></div>
<div class="ttc" id="group__analysis_html_ae848a3e6840414891035423948ca0383"><div class="ttname"><a href="group__analysis.html#ae848a3e6840414891035423948ca0383">mi_heap_area_t::reserved</a></div><div class="ttdeci">size_t reserved</div><div class="ttdoc">bytes reserved for this area</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:738</div></div>
<div class="ttc" id="group__heap_html_ga34a47cde5a5b38c29f1aa3c5e76943c2"><div class="ttname"><a href="group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2">mi_heap_t</a></div><div class="ttdeci">struct mi_heap_s mi_heap_t</div><div class="ttdoc">Type of first-class heaps.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:529</div></div>
<div class="ttc" id="group__analysis_html_ab820302c5cd0df133eb8e51650a008b4"><div class="ttname"><a href="group__analysis.html#ab820302c5cd0df133eb8e51650a008b4">mi_heap_area_t::used</a></div><div class="ttdeci">size_t used</div><div class="ttdoc">bytes in use by allocated blocks</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:740</div></div>
<div class="ttc" id="group__extended_html_ga3460a6ca91af97be4058f523d3cb8ece"><div class="ttname"><a href="group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece">mi_register_deferred_free</a></div><div class="ttdeci">void mi_register_deferred_free(mi_deferred_free_fun *deferred_free, void *arg)</div><div class="ttdoc">Register a deferred free function.</div></div>
<div class="ttc" id="group__posix_html_gae01389eedab8d67341ff52e2aad80ebb"><div class="ttname"><a href="group__posix.html#gae01389eedab8d67341ff52e2aad80ebb">mi_free_size</a></div><div class="ttdeci">void mi_free_size(void *p, size_t size)</div></div>
<div class="ttc" id="group__extended_html_ga421430e2226d7d468529cec457396756"><div class="ttname"><a href="group__extended.html#ga421430e2226d7d468529cec457396756">mi_collect</a></div><div class="ttdeci">void mi_collect(bool force)</div><div class="ttdoc">Eagerly free memory.</div></div>
<div class="ttc" id="group__cpp_html_ga756f4b2bc6a7ecd0a90baea8e90c7907"><div class="ttname"><a href="group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907">mi_new_reallocn</a></div><div class="ttdeci">void * mi_new_reallocn(void *p, size_t newcount, size_t size)</div><div class="ttdoc">like mi_reallocn(), but when out of memory, use std::get_new_handler and raise std::bad_alloc excepti...</div></div>
<div class="ttc" id="group__heap_html_ga9f9c0844edb9717f4feacd79116b8e0d"><div class="ttname"><a href="group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d">mi_heap_destroy</a></div><div class="ttdeci">void mi_heap_destroy(mi_heap_t *heap)</div><div class="ttdoc">Destroy a heap, freeing all its still allocated blocks.</div></div>
<div class="ttc" id="group__aligned_html_ga08647c4593f3b2eef24a919a73eba3a3"><div class="ttname"><a href="group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3">mi_calloc_aligned_at</a></div><div class="ttdeci">void * mi_calloc_aligned_at(size_t count, size_t size, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e">mi_option_large_os_pages</a></div><div class="ttdoc">Use large OS pages (2MiB in size) if possible.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:783</div></div>
<div class="ttc" id="group__heap_html_gac74e94ad9b0c9b57c1c4d88b8825b7a8"><div class="ttname"><a href="group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8">mi_heap_reallocn</a></div><div class="ttdeci">void * mi_heap_reallocn(mi_heap_t *heap, void *p, size_t count, size_t size)</div></div>
<div class="ttc" id="group__extended_html_gae5b17ff027cd2150b43a33040250cf3f"><div class="ttname"><a href="group__extended.html#gae5b17ff027cd2150b43a33040250cf3f">mi_register_output</a></div><div class="ttdeci">void mi_register_output(mi_output_fun *out, void *arg)</div><div class="ttdoc">Register an output function.</div></div>
<div class="ttc" id="group__cpp_html_structmi__stl__allocator"><div class="ttname"><a href="group__cpp.html#structmi__stl__allocator">mi_stl_allocator</a></div><div class="ttdoc">std::allocator implementation for mimalloc for use in STL containers.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:880</div></div>
<div class="ttc" id="group__heap_html_gaa1a1c7a1f4da6826b5a25b70ef878368"><div class="ttname"><a href="group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368">mi_heap_malloc_small</a></div><div class="ttdeci">void * mi_heap_malloc_small(mi_heap_t *heap, size_t size)</div><div class="ttdoc">Allocate a small object in a specific heap.</div></div>
<div class="ttc" id="group__heap_html_gaaef3395f66be48f37bdc8322509c5d81"><div class="ttname"><a href="group__heap.html#gaaef3395f66be48f37bdc8322509c5d81">mi_heap_realloc</a></div><div class="ttdeci">void * mi_heap_realloc(mi_heap_t *heap, void *p, size_t newsize)</div></div>
<div class="ttc" id="group__posix_html_ga06d07cf357bbac5c73ba5d0c0c421e17"><div class="ttname"><a href="group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17">mi_malloc_usable_size</a></div><div class="ttdeci">size_t mi_malloc_usable_size(const void *p)</div></div>
<div class="ttc" id="group__extended_html_gad823d23444a4b77a40f66bf075a98a0c"><div class="ttname"><a href="group__extended.html#gad823d23444a4b77a40f66bf075a98a0c">mi_output_fun</a></div><div class="ttdeci">void() mi_output_fun(const char *msg, void *arg)</div><div class="ttdoc">Type of output functions.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:376</div></div>
<div class="ttc" id="group__malloc_html_gac7cffe13f1f458ed16789488bf92b9b2"><div class="ttname"><a href="group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2">mi_strdup</a></div><div class="ttdeci">char * mi_strdup(const char *s)</div><div class="ttdoc">Allocate and duplicate a string.</div></div>
<div class="ttc" id="group__heap_html_gaf96c788a1bf553fe2d371de9365e047c"><div class="ttname"><a href="group__heap.html#gaf96c788a1bf553fe2d371de9365e047c">mi_heap_realloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_realloc_aligned_at(mi_heap_t *heap, void *p, size_t newsize, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__malloc_html_gafe68ac7c5e24a65cd55c9d6b152211a0"><div class="ttname"><a href="group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0">mi_reallocf</a></div><div class="ttdeci">void * mi_reallocf(void *p, size_t newsize)</div><div class="ttdoc">Re-allocate memory to newsize bytes,.</div></div>
<div class="ttc" id="group__malloc_html_ga97fedb4f7107c592fd7f0f0a8949a57d"><div class="ttname"><a href="group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d">mi_calloc</a></div><div class="ttdeci">void * mi_calloc(size_t count, size_t size)</div><div class="ttdoc">Allocate zero-initialized count elements of size bytes.</div></div>
<div class="ttc" id="group__heap_html_ga45fb43a62776fbebbdf1edd99b527954"><div class="ttname"><a href="group__heap.html#ga45fb43a62776fbebbdf1edd99b527954">mi_heap_zalloc_aligned_at</a></div><div class="ttdeci">void * mi_heap_zalloc_aligned_at(mi_heap_t *heap, size_t size, size_t alignment, size_t offset)</div></div>
<div class="ttc" id="group__extended_html_ga7136c2e55cb22c98ecf95d08d6debb99"><div class="ttname"><a href="group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99">mi_malloc_small</a></div><div class="ttdeci">void * mi_malloc_small(size_t size)</div><div class="ttdoc">Allocate a small object.</div></div>
<div class="ttc" id="group__analysis_html_ga628c237489c2679af84a4d0d143b3dd5"><div class="ttname"><a href="group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5">mi_check_owned</a></div><div class="ttdeci">bool mi_check_owned(const void *p)</div><div class="ttdoc">Check safely if any pointer is part of the default heap of this thread.</div></div>
<div class="ttc" id="group__heap_html_gab5b87e1805306f70df38789fcfcf6653"><div class="ttname"><a href="group__heap.html#gab5b87e1805306f70df38789fcfcf6653">mi_heap_malloc_aligned</a></div><div class="ttdeci">void * mi_heap_malloc_aligned(mi_heap_t *heap, size_t size, size_t alignment)</div></div>
<div class="ttc" id="group__options_html_ga7e8af195cc81d3fa64ccf2662caa565a"><div class="ttname"><a href="group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a">mi_option_get</a></div><div class="ttdeci">long mi_option_get(mi_option_t option)</div></div>
<div class="ttc" id="group__heap_html_ga5d03fbe062ffcf38f0f417fd968357fc"><div class="ttname"><a href="group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc">mi_heap_get_backing</a></div><div class="ttdeci">mi_heap_t * mi_heap_get_backing()</div><div class="ttdoc">Get the backing heap.</div></div>
<div class="ttc" id="group__posix_html_ga0d28d5cf61e6bfbb18c63092939fe5c9"><div class="ttname"><a href="group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9">mi_free_aligned</a></div><div class="ttdeci">void mi_free_aligned(void *p, size_t alignment)</div></div>
<div class="ttc" id="group__cpp_html_gaad048a9fce3d02c5909cd05c6ec24545"><div class="ttname"><a href="group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545">mi_new</a></div><div class="ttdeci">void * mi_new(std::size_t n) noexcept(false)</div><div class="ttdoc">like mi_malloc(), but when out of memory, use std::get_new_handler and raise std::bad_alloc exception...</div></div>
<div class="ttc" id="group__options_html_ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5"><div class="ttname"><a href="group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5">mi_option_reset_delay</a></div><div class="ttdoc">Delay in milli-seconds before resetting a page (100ms by default)</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:788</div></div>
<div class="ttc" id="group__heap_html_ga766f672ba56f2fbfeb9d9dbb0b7f6b11"><div class="ttname"><a href="group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11">mi_heap_new</a></div><div class="ttdeci">mi_heap_t * mi_heap_new()</div><div class="ttdoc">Create a new heap that can be used for allocation.</div></div>
<div class="ttc" id="group__heap_html_ga9cbed01e42c0647907295de92c3fa296"><div class="ttname"><a href="group__heap.html#ga9cbed01e42c0647907295de92c3fa296">mi_heap_malloc</a></div><div class="ttdeci">void * mi_heap_malloc(mi_heap_t *heap, size_t size)</div><div class="ttdoc">Allocate in a specific heap.</div></div>
<div class="ttc" id="group__analysis_html_ab47526df656d8837ec3e97f11b83f835"><div class="ttname"><a href="group__analysis.html#ab47526df656d8837ec3e97f11b83f835">mi_heap_area_t::committed</a></div><div class="ttdeci">size_t committed</div><div class="ttdoc">current committed bytes of this area</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:739</div></div>
<div class="ttc" id="group__options_html_gafebf7ed116adb38ae5218bc3ce06884c"><div class="ttname"><a href="group__options.html#gafebf7ed116adb38ae5218bc3ce06884c">mi_option_t</a></div><div class="ttdeci">mi_option_t</div><div class="ttdoc">Runtime options.</div><div class="ttdef"><b>Definition:</b> mimalloc-doc.h:775</div></div>
<div class="ttc" id="group__analysis_html_ga0d67c1789faaa15ff366c024fcaf6377"><div class="ttname"><a href="group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377">mi_heap_check_owned</a></div><div class="ttdeci">bool mi_heap_check_owned(mi_heap_t *heap, const void *p)</div><div class="ttdoc">Check safely if any pointer is part of a heap.</div></div>
<div class="ttc" id="group__heap_html_gab8631ec88c8d26641b68b5d25dcd4422"><div class="ttname"><a href="group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422">mi_heap_set_default</a></div><div class="ttdeci">mi_heap_t * mi_heap_set_default(mi_heap_t *heap)</div><div class="ttdoc">Set the default heap to use for mi_malloc() et al.</div></div>
</div><!-- fragment --></div><!-- contents -->
</div><!-- doc-content -->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
<li class="navelem"><b>mimalloc-doc.h</b></li>
<li class="footer">Generated by
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
</ul>
</div>
</body>
</html>
-50
View File
@@ -1,50 +0,0 @@
/*
@ @licstart The following is the entire license notice for the
JavaScript code in this file.
Copyright (C) 1997-2017 by Dimitri van Heesch
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
@licend The above is the entire license notice
for the JavaScript code in this file
*/
var NAVTREE =
[
[ "mi-malloc", "index.html", [
[ "Building", "build.html", null ],
[ "Using the library", "using.html", null ],
[ "Environment Options", "environment.html", null ],
[ "Overriding Malloc", "overrides.html", null ],
[ "Performance", "bench.html", null ],
[ "Modules", "modules.html", "modules" ],
[ "Data Structures", "annotated.html", [
[ "Data Structures", "annotated.html", "annotated_dup" ],
[ "Data Structure Index", "classes.html", null ],
[ "Data Fields", "functions.html", [
[ "All", "functions.html", null ],
[ "Variables", "functions_vars.html", null ]
] ]
] ]
] ]
];
var NAVTREEINDEX =
[
"annotated.html"
];
var SYNCONMSG = 'click to disable panel synchronisation';
var SYNCOFFMSG = 'click to enable panel synchronisation';
-142
View File
@@ -1,142 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen 1.8.15"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>mi-malloc: Overriding Malloc</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="navtree.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="resize.js"></script>
<script type="text/javascript" src="navtreedata.js"></script>
<script type="text/javascript" src="navtree.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(document).ready(initResizable);
/* @license-end */</script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(document).ready(function() { init_search(); });
/* @license-end */
</script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
<link href="mimalloc-doxygen.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectlogo"><img alt="Logo" src="mimalloc-logo.svg"/></td>
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">mi-malloc
&#160;<span id="projectnumber">1.6</span>
</div>
</td>
<td> <div id="MSearchBox" class="MSearchBoxInactive">
<span class="left">
<img id="MSearchSelect" src="search/mag_sel.png"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
alt=""/>
<input type="text" id="MSearchField" value="Search" accesskey="S"
onfocus="searchBox.OnSearchFieldFocus(true)"
onblur="searchBox.OnSearchFieldFocus(false)"
onkeyup="searchBox.OnSearchFieldChange(event)"/>
</span><span class="right">
<a id="MSearchClose" href="javascript:searchBox.CloseResultsWindow()"><img id="MSearchCloseImg" border="0" src="search/close.png" alt=""/></a>
</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.8.15 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
var searchBox = new SearchBox("searchBox", "search",false,'Search');
/* @license-end */
</script>
</div><!-- top -->
<div id="side-nav" class="ui-resizable side-nav-resizable">
<div id="nav-tree">
<div id="nav-tree-contents">
<div id="nav-sync" class="sync"></div>
</div>
</div>
<div id="splitbar" style="-moz-user-select:none;"
class="ui-resizable-handle">
</div>
</div>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:cf05388f2679ee054f2beb29a391d25f4e673ac3&amp;dn=gpl-2.0.txt GPL-v2 */
$(document).ready(function(){initNavTree('overrides.html','');});
/* @license-end */
</script>
<div id="doc-content">
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
onmouseover="return searchBox.OnSearchSelectShow()"
onmouseout="return searchBox.OnSearchSelectHide()"
onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>
<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0"
name="MSearchResults" id="MSearchResults">
</iframe>
</div>
<div class="PageDoc"><div class="header">
<div class="headertitle">
<div class="title">Overriding Malloc </div> </div>
</div><!--header-->
<div class="contents">
<div class="textblock"><p>Overriding the standard <code>malloc</code> can be done either <em>dynamically</em> or <em>statically</em>.</p>
<h2>Dynamic override</h2>
<p>This is the recommended way to override the standard malloc interface.</p>
<h3>Linux, BSD</h3>
<p>On these systems we preload the mimalloc shared library so all calls to the standard <code>malloc</code> interface are resolved to the <em>mimalloc</em> library.</p>
<ul>
<li><code>env LD_PRELOAD=/usr/lib/libmimalloc.so myprogram</code></li>
</ul>
<p>You can set extra environment variables to check that mimalloc is running, like: </p><div class="fragment"><div class="line">env MIMALLOC_VERBOSE=1 LD_PRELOAD=/usr/lib/libmimalloc.so myprogram</div></div><!-- fragment --><p> or run with the debug version to get detailed statistics: </p><div class="fragment"><div class="line">env MIMALLOC_SHOW_STATS=1 LD_PRELOAD=/usr/lib/libmimalloc-debug.so myprogram</div></div><!-- fragment --><h3>MacOS</h3>
<p>On macOS we can also preload the mimalloc shared library so all calls to the standard <code>malloc</code> interface are resolved to the <em>mimalloc</em> library.</p>
<ul>
<li><code>env DYLD_FORCE_FLAT_NAMESPACE=1 DYLD_INSERT_LIBRARIES=/usr/lib/libmimalloc.dylib myprogram</code></li>
</ul>
<p>Note that certain security restrictions may apply when doing this from the <a href="https://stackoverflow.com/questions/43941322/dyld-insert-libraries-ignored-when-calling-application-through-bash">shell</a>.</p>
<p>(Note: macOS support for dynamic overriding is recent, please report any issues.)</p>
<h3>Windows</h3>
<p>Overriding on Windows is robust and has the particular advantage to be able to redirect all malloc/free calls that go through the (dynamic) C runtime allocator, including those from other DLL's or libraries.</p>
<p>The overriding on Windows requires that you link your program explicitly with the mimalloc DLL and use the C-runtime library as a DLL (using the <code>/MD</code> or <code>/MDd</code> switch). Also, the <code>mimalloc-redirect.dll</code> (or <code>mimalloc-redirect32.dll</code>) must be available in the same folder as the main <code>mimalloc-override.dll</code> at runtime (as it is a dependency). The redirection DLL ensures that all calls to the C runtime malloc API get redirected to mimalloc (in <code>mimalloc-override.dll</code>).</p>
<p>To ensure the mimalloc DLL is loaded at run-time it is easiest to insert some call to the mimalloc API in the <code>main</code> function, like <code>mi_version()</code> (or use the <code>/INCLUDE:mi_version</code> switch on the linker). See the <code>mimalloc-override-test</code> project for an example on how to use this. For best performance on Windows with C++, it is also recommended to also override the <code>new</code>/<code>delete</code> operations (by including <a href="https://github.com/microsoft/mimalloc/blob/master/include/mimalloc-new-delete.h"><code>mimalloc-new-delete.h</code></a> a single(!) source file in your project).</p>
<p>The environment variable <code>MIMALLOC_DISABLE_REDIRECT=1</code> can be used to disable dynamic overriding at run-time. Use <code>MIMALLOC_VERBOSE=1</code> to check if mimalloc was successfully redirected.</p>
<p>(Note: in principle, it is possible to even patch existing executables without any recompilation if they are linked with the dynamic C runtime (<code>ucrtbase.dll</code>) &ndash; just put the <code>mimalloc-override.dll</code> into the import table (and put <code>mimalloc-redirect.dll</code> in the same folder) Such patching can be done for example with <a href="https://ntcore.com/?page_id=388">CFF Explorer</a>).</p>
<h2>Static override</h2>
<p>On Unix systems, you can also statically link with <em>mimalloc</em> to override the standard malloc interface. The recommended way is to link the final program with the <em>mimalloc</em> single object file (<code>mimalloc-override.o</code>). We use an object file instead of a library file as linkers give preference to that over archives to resolve symbols. To ensure that the standard malloc interface resolves to the <em>mimalloc</em> library, link it as the first object file. For example:</p>
<div class="fragment"><div class="line">gcc -o myprogram mimalloc-<span class="keyword">override</span>.o myfile1.c ...</div></div><!-- fragment --><h2>List of Overrides:</h2>
<p>The specific functions that get redirected to the <em>mimalloc</em> library are:</p>
<div class="fragment"><div class="line"><span class="comment">// C</span></div><div class="line"><span class="keywordtype">void</span>* malloc(<span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">void</span>* calloc(<span class="keywordtype">size_t</span> size, <span class="keywordtype">size_t</span> n);</div><div class="line"><span class="keywordtype">void</span>* realloc(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><span class="keywordtype">void</span> free(<span class="keywordtype">void</span>* p);</div><div class="line"></div><div class="line"><span class="comment">// C++</span></div><div class="line"><span class="keywordtype">void</span> <span class="keyword">operator</span> <span class="keyword">delete</span>(<span class="keywordtype">void</span>* p);</div><div class="line"><span class="keywordtype">void</span> <span class="keyword">operator</span> <span class="keyword">delete</span>[](<span class="keywordtype">void</span>* p);</div><div class="line"></div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>(std::size_t n) noexcept(<span class="keyword">false</span>);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[](std::size_t n) noexcept(<span class="keyword">false</span>);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>( std::size_t n, std::align_val_t align) noexcept(<span class="keyword">false</span>);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[]( std::size_t n, std::align_val_t align) noexcept(<span class="keyword">false</span>);</div><div class="line"></div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span> ( std::size_t count, <span class="keyword">const</span> std::nothrow_t&amp; tag);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[]( std::size_t count, <span class="keyword">const</span> std::nothrow_t&amp; tag);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span> ( std::size_t count, std::align_val_t al, <span class="keyword">const</span> std::nothrow_t&amp;);</div><div class="line"><span class="keywordtype">void</span>* <span class="keyword">operator</span> <span class="keyword">new</span>[]( std::size_t count, std::align_val_t al, <span class="keyword">const</span> std::nothrow_t&amp;);</div><div class="line"></div><div class="line"><span class="comment">// Posix</span></div><div class="line"><span class="keywordtype">int</span> posix_memalign(<span class="keywordtype">void</span>** p, <span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"></div><div class="line"><span class="comment">// Linux</span></div><div class="line"><span class="keywordtype">void</span>* memalign(<span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">void</span>* aligned_alloc(<span class="keywordtype">size_t</span> alignment, <span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">void</span>* valloc(<span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">void</span>* pvalloc(<span class="keywordtype">size_t</span> size);</div><div class="line"><span class="keywordtype">size_t</span> malloc_usable_size(<span class="keywordtype">void</span> *p);</div><div class="line"></div><div class="line"><span class="comment">// BSD</span></div><div class="line"><span class="keywordtype">void</span>* reallocarray( <span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> count, <span class="keywordtype">size_t</span> size );</div><div class="line"><span class="keywordtype">void</span>* reallocf(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><span class="keywordtype">void</span> cfree(<span class="keywordtype">void</span>* p);</div><div class="line"></div><div class="line"><span class="comment">// Windows</span></div><div class="line"><span class="keywordtype">void</span>* _expand(<span class="keywordtype">void</span>* p, <span class="keywordtype">size_t</span> newsize);</div><div class="line"><span class="keywordtype">size_t</span> _msize(<span class="keywordtype">void</span>* p);</div><div class="line"></div><div class="line"><span class="keywordtype">void</span>* _malloc_dbg(<span class="keyLine truncated
</div><!-- contents -->
</div><!-- doc-content -->
<!-- start footer part -->
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
<ul>
<li class="footer">Generated by
<a href="http://www.doxygen.org/index.html">
<img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.15 </li>
</ul>
</div>
</body>
</html>
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['_5fmi_5foption_5flast',['_mi_option_last',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a',1,'mimalloc-doc.h']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['aligned_20allocation',['Aligned Allocation',['../group__aligned.html',1,'']]]
];
-7
View File
@@ -1,7 +0,0 @@
var searchData=
[
['block_5fsize',['block_size',['../group__analysis.html#a332a6c14d736a99699d5453a1cb04b41',1,'mi_heap_area_t']]],
['blocks',['blocks',['../group__analysis.html#ae0085e6e1cf059a4eb7767e30e9991b8',1,'mi_heap_area_t']]],
['building',['Building',['../build.html',1,'']]],
['basic_20allocation',['Basic Allocation',['../group__malloc.html',1,'']]]
];
-5
View File
@@ -1,5 +0,0 @@
var searchData=
[
['committed',['committed',['../group__analysis.html#ab47526df656d8837ec3e97f11b83f835',1,'mi_heap_area_t']]],
['c_2b_2b_20wrappers',['C++ wrappers',['../group__cpp.html',1,'']]]
];
-5
View File
@@ -1,5 +0,0 @@
var searchData=
[
['environment_20options',['Environment Options',['../environment.html',1,'']]],
['extended_20functions',['Extended Functions',['../group__extended.html',1,'']]]
];
-5
View File
@@ -1,5 +0,0 @@
var searchData=
[
['heap_20introspection',['Heap Introspection',['../group__analysis.html',1,'']]],
['heap_20allocation',['Heap Allocation',['../group__heap.html',1,'']]]
];
-148
View File
@@ -1,148 +0,0 @@
var searchData=
[
['mi_5f_5fposix_5fmemalign',['mi__posix_memalign',['../group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a',1,'mimalloc-doc.h']]],
['mi_5faligned_5falloc',['mi_aligned_alloc',['../group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5',1,'mimalloc-doc.h']]],
['mi_5fblock_5fvisit_5ffun',['mi_block_visit_fun',['../group__analysis.html#gadfa01e2900f0e5d515ad5506b26f6d65',1,'mimalloc-doc.h']]],
['mi_5fcalloc',['mi_calloc',['../group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d',1,'mimalloc-doc.h']]],
['mi_5fcalloc_5faligned',['mi_calloc_aligned',['../group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9',1,'mimalloc-doc.h']]],
['mi_5fcalloc_5faligned_5fat',['mi_calloc_aligned_at',['../group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3',1,'mimalloc-doc.h']]],
['mi_5fcalloc_5ftp',['mi_calloc_tp',['../group__typed.html#gae80c47c9d4cab10961fff1a8ac98fc07',1,'mimalloc-doc.h']]],
['mi_5fcfree',['mi_cfree',['../group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7',1,'mimalloc-doc.h']]],
['mi_5fcheck_5fowned',['mi_check_owned',['../group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5',1,'mimalloc-doc.h']]],
['mi_5fcollect',['mi_collect',['../group__extended.html#ga421430e2226d7d468529cec457396756',1,'mimalloc-doc.h']]],
['mi_5fdeferred_5ffree_5ffun',['mi_deferred_free_fun',['../group__extended.html#ga299dae78d25ce112e384a98b7309c5be',1,'mimalloc-doc.h']]],
['mi_5ferror_5ffun',['mi_error_fun',['../group__extended.html#ga251d369cda3f1c2a955c555486ed90e5',1,'mimalloc-doc.h']]],
['mi_5fexpand',['mi_expand',['../group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4',1,'mimalloc-doc.h']]],
['mi_5ffree',['mi_free',['../group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95',1,'mimalloc-doc.h']]],
['mi_5ffree_5faligned',['mi_free_aligned',['../group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9',1,'mimalloc-doc.h']]],
['mi_5ffree_5fsize',['mi_free_size',['../group__posix.html#gae01389eedab8d67341ff52e2aad80ebb',1,'mimalloc-doc.h']]],
['mi_5ffree_5fsize_5faligned',['mi_free_size_aligned',['../group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc',1,'mimalloc-doc.h']]],
['mi_5fgood_5fsize',['mi_good_size',['../group__extended.html#gac057927cd06c854b45fe7847e921bd47',1,'mimalloc-doc.h']]],
['mi_5fheap_5farea_5ft',['mi_heap_area_t',['../group__analysis.html#structmi__heap__area__t',1,'']]],
['mi_5fheap_5fcalloc',['mi_heap_calloc',['../group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcalloc_5faligned',['mi_heap_calloc_aligned',['../group__heap.html#ga4af03a6e2b93fae77424d93f889705c3',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcalloc_5faligned_5fat',['mi_heap_calloc_aligned_at',['../group__heap.html#ga08ca6419a5c057a4d965868998eef487',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcalloc_5ftp',['mi_heap_calloc_tp',['../group__typed.html#ga4e5d1f1707c90e5f55e023ac5f45fe74',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcheck_5fowned',['mi_heap_check_owned',['../group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcollect',['mi_heap_collect',['../group__heap.html#ga7922f7495cde30b1984d0e6072419298',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcontains_5fblock',['mi_heap_contains_block',['../group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af',1,'mimalloc-doc.h']]],
['mi_5fheap_5fdelete',['mi_heap_delete',['../group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409',1,'mimalloc-doc.h']]],
['mi_5fheap_5fdestroy',['mi_heap_destroy',['../group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d',1,'mimalloc-doc.h']]],
['mi_5fheap_5fget_5fbacking',['mi_heap_get_backing',['../group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc',1,'mimalloc-doc.h']]],
['mi_5fheap_5fget_5fdefault',['mi_heap_get_default',['../group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc',['mi_heap_malloc',['../group__heap.html#ga9cbed01e42c0647907295de92c3fa296',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc_5faligned',['mi_heap_malloc_aligned',['../group__heap.html#gab5b87e1805306f70df38789fcfcf6653',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc_5faligned_5fat',['mi_heap_malloc_aligned_at',['../group__heap.html#ga23acd7680fb0976dde3783254c6c874b',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc_5fsmall',['mi_heap_malloc_small',['../group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc_5ftp',['mi_heap_malloc_tp',['../group__typed.html#ga653bcb24ac495bc19940ecd6898f9cd7',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmallocn',['mi_heap_mallocn',['../group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmallocn_5ftp',['mi_heap_mallocn_tp',['../group__typed.html#ga6b75cb9c4b9c647661d0924552dc6e83',1,'mimalloc-doc.h']]],
['mi_5fheap_5fnew',['mi_heap_new',['../group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11',1,'mimalloc-doc.h']]],
['mi_5fheap_5frealloc',['mi_heap_realloc',['../group__heap.html#gaaef3395f66be48f37bdc8322509c5d81',1,'mimalloc-doc.h']]],
['mi_5fheap_5frealloc_5faligned',['mi_heap_realloc_aligned',['../group__heap.html#gafc603b696bd14cae6da28658f950d98c',1,'mimalloc-doc.h']]],
['mi_5fheap_5frealloc_5faligned_5fat',['mi_heap_realloc_aligned_at',['../group__heap.html#gaf96c788a1bf553fe2d371de9365e047c',1,'mimalloc-doc.h']]],
['mi_5fheap_5freallocf',['mi_heap_reallocf',['../group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527',1,'mimalloc-doc.h']]],
['mi_5fheap_5freallocn',['mi_heap_reallocn',['../group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8',1,'mimalloc-doc.h']]],
['mi_5fheap_5freallocn_5ftp',['mi_heap_reallocn_tp',['../group__typed.html#gaf213d5422ec35e7f6caad827c79bc948',1,'mimalloc-doc.h']]],
['mi_5fheap_5frealpath',['mi_heap_realpath',['../group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0',1,'mimalloc-doc.h']]],
['mi_5fheap_5frecalloc',['mi_heap_recalloc',['../group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd',1,'mimalloc-doc.h']]],
['mi_5fheap_5frecalloc_5faligned',['mi_heap_recalloc_aligned',['../group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3',1,'mimalloc-doc.h']]],
['mi_5fheap_5frecalloc_5faligned_5fat',['mi_heap_recalloc_aligned_at',['../group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7',1,'mimalloc-doc.h']]],
['mi_5fheap_5frecalloc_5ftp',['mi_heap_recalloc_tp',['../group__typed.html#ga3e50a1600958fcaf1a7f3560c9174f9e',1,'mimalloc-doc.h']]],
['mi_5fheap_5frezalloc',['mi_heap_rezalloc',['../group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76',1,'mimalloc-doc.h']]],
['mi_5fheap_5frezalloc_5faligned',['mi_heap_rezalloc_aligned',['../group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664',1,'mimalloc-doc.h']]],
['mi_5fheap_5frezalloc_5faligned_5fat',['mi_heap_rezalloc_aligned_at',['../group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb',1,'mimalloc-doc.h']]],
['mi_5fheap_5fset_5fdefault',['mi_heap_set_default',['../group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422',1,'mimalloc-doc.h']]],
['mi_5fheap_5fstrdup',['mi_heap_strdup',['../group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5',1,'mimalloc-doc.h']]],
['mi_5fheap_5fstrndup',['mi_heap_strndup',['../group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a',1,'mimalloc-doc.h']]],
['mi_5fheap_5ft',['mi_heap_t',['../group__heap.html#ga34a47cde5a5b38c29f1aa3c5e76943c2',1,'mimalloc-doc.h']]],
['mi_5fheap_5fvisit_5fblocks',['mi_heap_visit_blocks',['../group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed',1,'mimalloc-doc.h']]],
['mi_5fheap_5fzalloc',['mi_heap_zalloc',['../group__heap.html#ga903104592c8ed53417a3762da6241133',1,'mimalloc-doc.h']]],
['mi_5fheap_5fzalloc_5faligned',['mi_heap_zalloc_aligned',['../group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0',1,'mimalloc-doc.h']]],
['mi_5fheap_5fzalloc_5faligned_5fat',['mi_heap_zalloc_aligned_at',['../group__heap.html#ga45fb43a62776fbebbdf1edd99b527954',1,'mimalloc-doc.h']]],
['mi_5fheap_5fzalloc_5ftp',['mi_heap_zalloc_tp',['../group__typed.html#gad6e87e86e994aa14416ae9b5d4c188fe',1,'mimalloc-doc.h']]],
['mi_5fis_5fin_5fheap_5fregion',['mi_is_in_heap_region',['../group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6',1,'mimalloc-doc.h']]],
['mi_5fis_5fredirected',['mi_is_redirected',['../group__extended.html#gaad25050b19f30cd79397b227e0157a3f',1,'mimalloc-doc.h']]],
['mi_5fmalloc',['mi_malloc',['../group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5faligned',['mi_malloc_aligned',['../group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5faligned_5fat',['mi_malloc_aligned_at',['../group__aligned.html#ga5850da130c936bd77db039dcfbc8295d',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5fsize',['mi_malloc_size',['../group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5fsmall',['mi_malloc_small',['../group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5ftp',['mi_malloc_tp',['../group__typed.html#ga0619a62c5fd886f1016030abe91f0557',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5fusable_5fsize',['mi_malloc_usable_size',['../group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17',1,'mimalloc-doc.h']]],
['mi_5fmallocn',['mi_mallocn',['../group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6',1,'mimalloc-doc.h']]],
['mi_5fmallocn_5ftp',['mi_mallocn_tp',['../group__typed.html#gae5cb6e0fafc9f23169c5622e077afe8b',1,'mimalloc-doc.h']]],
['mi_5fmemalign',['mi_memalign',['../group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e',1,'mimalloc-doc.h']]],
['mi_5fnew',['mi_new',['../group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545',1,'mimalloc-doc.h']]],
['mi_5fnew_5faligned',['mi_new_aligned',['../group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3',1,'mimalloc-doc.h']]],
['mi_5fnew_5faligned_5fnothrow',['mi_new_aligned_nothrow',['../group__cpp.html#gab5e29558926d934c3f1cae8c815f942c',1,'mimalloc-doc.h']]],
['mi_5fnew_5fn',['mi_new_n',['../group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81',1,'mimalloc-doc.h']]],
['mi_5fnew_5fnothrow',['mi_new_nothrow',['../group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a',1,'mimalloc-doc.h']]],
['mi_5fnew_5frealloc',['mi_new_realloc',['../group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e',1,'mimalloc-doc.h']]],
['mi_5fnew_5freallocn',['mi_new_reallocn',['../group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907',1,'mimalloc-doc.h']]],
['mi_5foption_5fdisable',['mi_option_disable',['../group__options.html#gaebf6ff707a2e688ebb1a2296ca564054',1,'mimalloc-doc.h']]],
['mi_5foption_5feager_5fcommit',['mi_option_eager_commit',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b',1,'mimalloc-doc.h']]],
['mi_5foption_5feager_5fcommit_5fdelay',['mi_option_eager_commit_delay',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c',1,'mimalloc-doc.h']]],
['mi_5foption_5feager_5fregion_5fcommit',['mi_option_eager_region_commit',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad',1,'mimalloc-doc.h']]],
['mi_5foption_5fenable',['mi_option_enable',['../group__options.html#ga04180ae41b0d601421dd62ced40ca050',1,'mimalloc-doc.h']]],
['mi_5foption_5fget',['mi_option_get',['../group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a',1,'mimalloc-doc.h']]],
['mi_5foption_5fis_5fenabled',['mi_option_is_enabled',['../group__options.html#ga459ad98f18b3fc9275474807fe0ca188',1,'mimalloc-doc.h']]],
['mi_5foption_5flarge_5fos_5fpages',['mi_option_large_os_pages',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e',1,'mimalloc-doc.h']]],
['mi_5foption_5fos_5ftag',['mi_option_os_tag',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf',1,'mimalloc-doc.h']]],
['mi_5foption_5fpage_5freset',['mi_option_page_reset',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968',1,'mimalloc-doc.h']]],
['mi_5foption_5freserve_5fhuge_5fos_5fpages',['mi_option_reserve_huge_os_pages',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2',1,'mimalloc-doc.h']]],
['mi_5foption_5freset_5fdecommits',['mi_option_reset_decommits',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536',1,'mimalloc-doc.h']]],
['mi_5foption_5freset_5fdelay',['mi_option_reset_delay',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5',1,'mimalloc-doc.h']]],
['mi_5foption_5fsegment_5fcache',['mi_option_segment_cache',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1',1,'mimalloc-doc.h']]],
['mi_5foption_5fsegment_5freset',['mi_option_segment_reset',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d',1,'mimalloc-doc.h']]],
['mi_5foption_5fset',['mi_option_set',['../group__options.html#gaf84921c32375e25754dc2ee6a911fa60',1,'mimalloc-doc.h']]],
['mi_5foption_5fset_5fdefault',['mi_option_set_default',['../group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90',1,'mimalloc-doc.h']]],
['mi_5foption_5fset_5fenabled',['mi_option_set_enabled',['../group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed',1,'mimalloc-doc.h']]],
['mi_5foption_5fset_5fenabled_5fdefault',['mi_option_set_enabled_default',['../group__options.html#ga65518b69ec5d32336b50e07f74b3f629',1,'mimalloc-doc.h']]],
['mi_5foption_5fshow_5ferrors',['mi_option_show_errors',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0',1,'mimalloc-doc.h']]],
['mi_5foption_5fshow_5fstats',['mi_option_show_stats',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda',1,'mimalloc-doc.h']]],
['mi_5foption_5ft',['mi_option_t',['../group__options.html#gafebf7ed116adb38ae5218bc3ce06884c',1,'mimalloc-doc.h']]],
['mi_5foption_5fuse_5fnuma_5fnodes',['mi_option_use_numa_nodes',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74',1,'mimalloc-doc.h']]],
['mi_5foption_5fverbose',['mi_option_verbose',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777',1,'mimalloc-doc.h']]],
['mi_5foutput_5ffun',['mi_output_fun',['../group__extended.html#gad823d23444a4b77a40f66bf075a98a0c',1,'mimalloc-doc.h']]],
['mi_5fposix_5fmemalign',['mi_posix_memalign',['../group__posix.html#gacff84f226ba9feb2031b8992e5579447',1,'mimalloc-doc.h']]],
['mi_5fprocess_5finfo',['mi_process_info',['../group__extended.html#ga7d862c2affd5790381da14eb102a364d',1,'mimalloc-doc.h']]],
['mi_5fpvalloc',['mi_pvalloc',['../group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e',1,'mimalloc-doc.h']]],
['mi_5frealloc',['mi_realloc',['../group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6',1,'mimalloc-doc.h']]],
['mi_5frealloc_5faligned',['mi_realloc_aligned',['../group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae',1,'mimalloc-doc.h']]],
['mi_5frealloc_5faligned_5fat',['mi_realloc_aligned_at',['../group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb',1,'mimalloc-doc.h']]],
['mi_5freallocarray',['mi_reallocarray',['../group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088',1,'mimalloc-doc.h']]],
['mi_5freallocf',['mi_reallocf',['../group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0',1,'mimalloc-doc.h']]],
['mi_5freallocn',['mi_reallocn',['../group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853',1,'mimalloc-doc.h']]],
['mi_5freallocn_5ftp',['mi_reallocn_tp',['../group__typed.html#ga1158b49a55dfa81f58a4426a7578f523',1,'mimalloc-doc.h']]],
['mi_5frealpath',['mi_realpath',['../group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe',1,'mimalloc-doc.h']]],
['mi_5frecalloc',['mi_recalloc',['../group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc',1,'mimalloc-doc.h']]],
['mi_5frecalloc_5faligned',['mi_recalloc_aligned',['../group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f',1,'mimalloc-doc.h']]],
['mi_5frecalloc_5faligned_5fat',['mi_recalloc_aligned_at',['../group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9',1,'mimalloc-doc.h']]],
['mi_5fregister_5fdeferred_5ffree',['mi_register_deferred_free',['../group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece',1,'mimalloc-doc.h']]],
['mi_5fregister_5ferror',['mi_register_error',['../group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45',1,'mimalloc-doc.h']]],
['mi_5fregister_5foutput',['mi_register_output',['../group__extended.html#gae5b17ff027cd2150b43a33040250cf3f',1,'mimalloc-doc.h']]],
['mi_5freserve_5fhuge_5fos_5fpages_5fat',['mi_reserve_huge_os_pages_at',['../group__extended.html#ga7795a13d20087447281858d2c771cca1',1,'mimalloc-doc.h']]],
['mi_5freserve_5fhuge_5fos_5fpages_5finterleave',['mi_reserve_huge_os_pages_interleave',['../group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50',1,'mimalloc-doc.h']]],
['mi_5frezalloc',['mi_rezalloc',['../group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6',1,'mimalloc-doc.h']]],
['mi_5frezalloc_5faligned',['mi_rezalloc_aligned',['../group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0',1,'mimalloc-doc.h']]],
['mi_5frezalloc_5faligned_5fat',['mi_rezalloc_aligned_at',['../group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1',1,'mimalloc-doc.h']]],
['mi_5fsmall_5fsize_5fmax',['MI_SMALL_SIZE_MAX',['../group__extended.html#ga1ea64283508718d9d645c38efc2f4305',1,'mimalloc-doc.h']]],
['mi_5fstats_5fmerge',['mi_stats_merge',['../group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1',1,'mimalloc-doc.h']]],
['mi_5fstats_5fprint',['mi_stats_print',['../group__extended.html#ga2d126e5c62d3badc35445e5d84166df2',1,'mimalloc-doc.h']]],
['mi_5fstats_5fprint_5fout',['mi_stats_print_out',['../group__extended.html#ga537f13b299ddf801e49a5a94fde02c79',1,'mimalloc-doc.h']]],
['mi_5fstats_5freset',['mi_stats_reset',['../group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99',1,'mimalloc-doc.h']]],
['mi_5fstl_5fallocator',['mi_stl_allocator',['../group__cpp.html#structmi__stl__allocator',1,'']]],
['mi_5fstrdup',['mi_strdup',['../group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2',1,'mimalloc-doc.h']]],
['mi_5fstrndup',['mi_strndup',['../group__malloc.html#gaaabf971c2571891433477e2d21a35266',1,'mimalloc-doc.h']]],
['mi_5fthread_5fdone',['mi_thread_done',['../group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf',1,'mimalloc-doc.h']]],
['mi_5fthread_5finit',['mi_thread_init',['../group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17',1,'mimalloc-doc.h']]],
['mi_5fthread_5fstats_5fprint_5fout',['mi_thread_stats_print_out',['../group__extended.html#gab1dac8476c46cb9eecab767eb40c1525',1,'mimalloc-doc.h']]],
['mi_5fusable_5fsize',['mi_usable_size',['../group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee',1,'mimalloc-doc.h']]],
['mi_5fvalloc',['mi_valloc',['../group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b',1,'mimalloc-doc.h']]],
['mi_5fzalloc',['mi_zalloc',['../group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000',1,'mimalloc-doc.h']]],
['mi_5fzalloc_5faligned',['mi_zalloc_aligned',['../group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819',1,'mimalloc-doc.h']]],
['mi_5fzalloc_5faligned_5fat',['mi_zalloc_aligned_at',['../group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8',1,'mimalloc-doc.h']]],
['mi_5fzalloc_5fsmall',['mi_zalloc_small',['../group__extended.html#ga220f29f40a44404b0061c15bc1c31152',1,'mimalloc-doc.h']]],
['mi_5fzalloc_5ftp',['mi_zalloc_tp',['../group__typed.html#gac77a61bdaf680a803785fe307820b48c',1,'mimalloc-doc.h']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['overriding_20malloc',['Overriding Malloc',['../overrides.html',1,'']]]
];
-5
View File
@@ -1,5 +0,0 @@
var searchData=
[
['performance',['Performance',['../bench.html',1,'']]],
['posix',['Posix',['../group__posix.html',1,'']]]
];
-5
View File
@@ -1,5 +0,0 @@
var searchData=
[
['runtime_20options',['Runtime Options',['../group__options.html',1,'']]],
['reserved',['reserved',['../group__analysis.html#ae848a3e6840414891035423948ca0383',1,'mi_heap_area_t']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['typed_20macros',['Typed Macros',['../group__typed.html',1,'']]]
];
-5
View File
@@ -1,5 +0,0 @@
var searchData=
[
['used',['used',['../group__analysis.html#ab820302c5cd0df133eb8e51650a008b4',1,'mi_heap_area_t']]],
['using_20the_20library',['Using the library',['../using.html',1,'']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['zero_20initialized_20re_2dallocation',['Zero initialized re-allocation',['../group__zeroinit.html',1,'']]]
];
-5
View File
@@ -1,5 +0,0 @@
var searchData=
[
['mi_5fheap_5farea_5ft',['mi_heap_area_t',['../group__analysis.html#structmi__heap__area__t',1,'']]],
['mi_5fstl_5fallocator',['mi_stl_allocator',['../group__cpp.html#structmi__stl__allocator',1,'']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['mi_5foption_5ft',['mi_option_t',['../group__options.html#gafebf7ed116adb38ae5218bc3ce06884c',1,'mimalloc-doc.h']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['_5fmi_5foption_5flast',['_mi_option_last',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca5b4357b74be0d87568036c32eb1a2e4a',1,'mimalloc-doc.h']]]
];
-18
View File
@@ -1,18 +0,0 @@
var searchData=
[
['mi_5foption_5feager_5fcommit',['mi_option_eager_commit',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca1e8de72c93da7ff22d91e1e27b52ac2b',1,'mimalloc-doc.h']]],
['mi_5foption_5feager_5fcommit_5fdelay',['mi_option_eager_commit_delay',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca17a190c25be381142d87e0468c4c068c',1,'mimalloc-doc.h']]],
['mi_5foption_5feager_5fregion_5fcommit',['mi_option_eager_region_commit',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca32ce97ece29f69e82579679cf8a307ad',1,'mimalloc-doc.h']]],
['mi_5foption_5flarge_5fos_5fpages',['mi_option_large_os_pages',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4192d491200d0055df0554d4cf65054e',1,'mimalloc-doc.h']]],
['mi_5foption_5fos_5ftag',['mi_option_os_tag',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca4b74ae2a69e445de6c2361b73c1d14bf',1,'mimalloc-doc.h']]],
['mi_5foption_5fpage_5freset',['mi_option_page_reset',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cada854dd272c66342f18a93ee254a2968',1,'mimalloc-doc.h']]],
['mi_5foption_5freserve_5fhuge_5fos_5fpages',['mi_option_reserve_huge_os_pages',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884caca7ed041be3b0b9d0b82432c7bf41af2',1,'mimalloc-doc.h']]],
['mi_5foption_5freset_5fdecommits',['mi_option_reset_decommits',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cac81ee965b130fa81238913a3c239d536',1,'mimalloc-doc.h']]],
['mi_5foption_5freset_5fdelay',['mi_option_reset_delay',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca154fe170131d5212cff57e22b99523c5',1,'mimalloc-doc.h']]],
['mi_5foption_5fsegment_5fcache',['mi_option_segment_cache',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca2ecbe7ef32f5c84de3739aa4f0b805a1',1,'mimalloc-doc.h']]],
['mi_5foption_5fsegment_5freset',['mi_option_segment_reset',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafb121d30d87591850d5410ccc3a95c6d',1,'mimalloc-doc.h']]],
['mi_5foption_5fshow_5ferrors',['mi_option_show_errors',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884cafbf4822e5c00732c5984b32a032837f0',1,'mimalloc-doc.h']]],
['mi_5foption_5fshow_5fstats',['mi_option_show_stats',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0957ef73b2550764b4840edf48422fda',1,'mimalloc-doc.h']]],
['mi_5foption_5fuse_5fnuma_5fnodes',['mi_option_use_numa_nodes',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca0ac33a18f6b659fcfaf44efb0bab1b74',1,'mimalloc-doc.h']]],
['mi_5foption_5fverbose',['mi_option_verbose',['../group__options.html#ggafebf7ed116adb38ae5218bc3ce06884ca7c8b7bf5281c581bad64f5daa6442777',1,'mimalloc-doc.h']]]
];
-113
View File
@@ -1,113 +0,0 @@
var searchData=
[
['mi_5f_5fposix_5fmemalign',['mi__posix_memalign',['../group__posix.html#gad5a69c8fea96aa2b7a7c818c2130090a',1,'mimalloc-doc.h']]],
['mi_5faligned_5falloc',['mi_aligned_alloc',['../group__posix.html#ga1326d2e4388630b5f81ca7206318b8e5',1,'mimalloc-doc.h']]],
['mi_5fcalloc',['mi_calloc',['../group__malloc.html#ga97fedb4f7107c592fd7f0f0a8949a57d',1,'mimalloc-doc.h']]],
['mi_5fcalloc_5faligned',['mi_calloc_aligned',['../group__aligned.html#ga53dddb4724042a90315b94bc268fb4c9',1,'mimalloc-doc.h']]],
['mi_5fcalloc_5faligned_5fat',['mi_calloc_aligned_at',['../group__aligned.html#ga08647c4593f3b2eef24a919a73eba3a3',1,'mimalloc-doc.h']]],
['mi_5fcfree',['mi_cfree',['../group__posix.html#ga705dc7a64bffacfeeb0141501a5c35d7',1,'mimalloc-doc.h']]],
['mi_5fcheck_5fowned',['mi_check_owned',['../group__analysis.html#ga628c237489c2679af84a4d0d143b3dd5',1,'mimalloc-doc.h']]],
['mi_5fcollect',['mi_collect',['../group__extended.html#ga421430e2226d7d468529cec457396756',1,'mimalloc-doc.h']]],
['mi_5fexpand',['mi_expand',['../group__malloc.html#gaaee66a1d483c3e28f585525fb96707e4',1,'mimalloc-doc.h']]],
['mi_5ffree',['mi_free',['../group__malloc.html#gaf2c7b89c327d1f60f59e68b9ea644d95',1,'mimalloc-doc.h']]],
['mi_5ffree_5faligned',['mi_free_aligned',['../group__posix.html#ga0d28d5cf61e6bfbb18c63092939fe5c9',1,'mimalloc-doc.h']]],
['mi_5ffree_5fsize',['mi_free_size',['../group__posix.html#gae01389eedab8d67341ff52e2aad80ebb',1,'mimalloc-doc.h']]],
['mi_5ffree_5fsize_5faligned',['mi_free_size_aligned',['../group__posix.html#ga72e9d7ffb5fe94d69bc722c8506e27bc',1,'mimalloc-doc.h']]],
['mi_5fgood_5fsize',['mi_good_size',['../group__extended.html#gac057927cd06c854b45fe7847e921bd47',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcalloc',['mi_heap_calloc',['../group__heap.html#gaa6702b3c48e9e53e50e81b36f5011d55',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcalloc_5faligned',['mi_heap_calloc_aligned',['../group__heap.html#ga4af03a6e2b93fae77424d93f889705c3',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcalloc_5faligned_5fat',['mi_heap_calloc_aligned_at',['../group__heap.html#ga08ca6419a5c057a4d965868998eef487',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcheck_5fowned',['mi_heap_check_owned',['../group__analysis.html#ga0d67c1789faaa15ff366c024fcaf6377',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcollect',['mi_heap_collect',['../group__heap.html#ga7922f7495cde30b1984d0e6072419298',1,'mimalloc-doc.h']]],
['mi_5fheap_5fcontains_5fblock',['mi_heap_contains_block',['../group__analysis.html#gaa862aa8ed8d57d84cae41fc1022d71af',1,'mimalloc-doc.h']]],
['mi_5fheap_5fdelete',['mi_heap_delete',['../group__heap.html#ga2ab1af8d438819b55319c7ef51d1e409',1,'mimalloc-doc.h']]],
['mi_5fheap_5fdestroy',['mi_heap_destroy',['../group__heap.html#ga9f9c0844edb9717f4feacd79116b8e0d',1,'mimalloc-doc.h']]],
['mi_5fheap_5fget_5fbacking',['mi_heap_get_backing',['../group__heap.html#ga5d03fbe062ffcf38f0f417fd968357fc',1,'mimalloc-doc.h']]],
['mi_5fheap_5fget_5fdefault',['mi_heap_get_default',['../group__heap.html#ga8db4cbb87314a989a9a187464d6b5e05',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc',['mi_heap_malloc',['../group__heap.html#ga9cbed01e42c0647907295de92c3fa296',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc_5faligned',['mi_heap_malloc_aligned',['../group__heap.html#gab5b87e1805306f70df38789fcfcf6653',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc_5faligned_5fat',['mi_heap_malloc_aligned_at',['../group__heap.html#ga23acd7680fb0976dde3783254c6c874b',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmalloc_5fsmall',['mi_heap_malloc_small',['../group__heap.html#gaa1a1c7a1f4da6826b5a25b70ef878368',1,'mimalloc-doc.h']]],
['mi_5fheap_5fmallocn',['mi_heap_mallocn',['../group__heap.html#ga851da6c43fe0b71c1376cee8aef90db0',1,'mimalloc-doc.h']]],
['mi_5fheap_5fnew',['mi_heap_new',['../group__heap.html#ga766f672ba56f2fbfeb9d9dbb0b7f6b11',1,'mimalloc-doc.h']]],
['mi_5fheap_5frealloc',['mi_heap_realloc',['../group__heap.html#gaaef3395f66be48f37bdc8322509c5d81',1,'mimalloc-doc.h']]],
['mi_5fheap_5frealloc_5faligned',['mi_heap_realloc_aligned',['../group__heap.html#gafc603b696bd14cae6da28658f950d98c',1,'mimalloc-doc.h']]],
['mi_5fheap_5frealloc_5faligned_5fat',['mi_heap_realloc_aligned_at',['../group__heap.html#gaf96c788a1bf553fe2d371de9365e047c',1,'mimalloc-doc.h']]],
['mi_5fheap_5freallocf',['mi_heap_reallocf',['../group__heap.html#ga4a21070eb4e7cce018133c8d5f4b0527',1,'mimalloc-doc.h']]],
['mi_5fheap_5freallocn',['mi_heap_reallocn',['../group__heap.html#gac74e94ad9b0c9b57c1c4d88b8825b7a8',1,'mimalloc-doc.h']]],
['mi_5fheap_5frealpath',['mi_heap_realpath',['../group__heap.html#ga00e95ba1e01acac3cfd95bb7a357a6f0',1,'mimalloc-doc.h']]],
['mi_5fheap_5frecalloc',['mi_heap_recalloc',['../group__zeroinit.html#ga8648c5fbb22a80f0262859099f06dfbd',1,'mimalloc-doc.h']]],
['mi_5fheap_5frecalloc_5faligned',['mi_heap_recalloc_aligned',['../group__zeroinit.html#ga9f3f999396c8f77ca5e80e7b40ac29e3',1,'mimalloc-doc.h']]],
['mi_5fheap_5frecalloc_5faligned_5fat',['mi_heap_recalloc_aligned_at',['../group__zeroinit.html#ga496452c96f1de8c500be9fddf52edaf7',1,'mimalloc-doc.h']]],
['mi_5fheap_5frezalloc',['mi_heap_rezalloc',['../group__zeroinit.html#gacfad83f14eb5d6a42a497a898e19fc76',1,'mimalloc-doc.h']]],
['mi_5fheap_5frezalloc_5faligned',['mi_heap_rezalloc_aligned',['../group__zeroinit.html#ga375fa8a611c51905e592d5d467c49664',1,'mimalloc-doc.h']]],
['mi_5fheap_5frezalloc_5faligned_5fat',['mi_heap_rezalloc_aligned_at',['../group__zeroinit.html#gac90da54fa7e5d10bdc97ce0b51dce2eb',1,'mimalloc-doc.h']]],
['mi_5fheap_5fset_5fdefault',['mi_heap_set_default',['../group__heap.html#gab8631ec88c8d26641b68b5d25dcd4422',1,'mimalloc-doc.h']]],
['mi_5fheap_5fstrdup',['mi_heap_strdup',['../group__heap.html#ga139d6b09dbf50c3c2523d0f4d1cfdeb5',1,'mimalloc-doc.h']]],
['mi_5fheap_5fstrndup',['mi_heap_strndup',['../group__heap.html#ga8e3dbd46650dd26573cf307a2c8f1f5a',1,'mimalloc-doc.h']]],
['mi_5fheap_5fvisit_5fblocks',['mi_heap_visit_blocks',['../group__analysis.html#ga70c46687dc6e9dc98b232b02646f8bed',1,'mimalloc-doc.h']]],
['mi_5fheap_5fzalloc',['mi_heap_zalloc',['../group__heap.html#ga903104592c8ed53417a3762da6241133',1,'mimalloc-doc.h']]],
['mi_5fheap_5fzalloc_5faligned',['mi_heap_zalloc_aligned',['../group__heap.html#gaa450a59c6c7ae5fdbd1c2b80a8329ef0',1,'mimalloc-doc.h']]],
['mi_5fheap_5fzalloc_5faligned_5fat',['mi_heap_zalloc_aligned_at',['../group__heap.html#ga45fb43a62776fbebbdf1edd99b527954',1,'mimalloc-doc.h']]],
['mi_5fis_5fin_5fheap_5fregion',['mi_is_in_heap_region',['../group__extended.html#ga5f071b10d4df1c3658e04e7fd67a94e6',1,'mimalloc-doc.h']]],
['mi_5fis_5fredirected',['mi_is_redirected',['../group__extended.html#gaad25050b19f30cd79397b227e0157a3f',1,'mimalloc-doc.h']]],
['mi_5fmalloc',['mi_malloc',['../group__malloc.html#ga3406e8b168bc74c8637b11571a6da83a',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5faligned',['mi_malloc_aligned',['../group__aligned.html#ga68930196751fa2cca9e1fd0d71bade56',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5faligned_5fat',['mi_malloc_aligned_at',['../group__aligned.html#ga5850da130c936bd77db039dcfbc8295d',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5fsize',['mi_malloc_size',['../group__posix.html#ga4531c9e775bb3ae12db57c1ba8a5d7de',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5fsmall',['mi_malloc_small',['../group__extended.html#ga7136c2e55cb22c98ecf95d08d6debb99',1,'mimalloc-doc.h']]],
['mi_5fmalloc_5fusable_5fsize',['mi_malloc_usable_size',['../group__posix.html#ga06d07cf357bbac5c73ba5d0c0c421e17',1,'mimalloc-doc.h']]],
['mi_5fmallocn',['mi_mallocn',['../group__malloc.html#ga0b05e2bf0f73e7401ae08597ff782ac6',1,'mimalloc-doc.h']]],
['mi_5fmemalign',['mi_memalign',['../group__posix.html#gaab7fa71ea93b96873f5d9883db57d40e',1,'mimalloc-doc.h']]],
['mi_5fnew',['mi_new',['../group__cpp.html#gaad048a9fce3d02c5909cd05c6ec24545',1,'mimalloc-doc.h']]],
['mi_5fnew_5faligned',['mi_new_aligned',['../group__cpp.html#gaef2c2bdb4f70857902d3c8903ac095f3',1,'mimalloc-doc.h']]],
['mi_5fnew_5faligned_5fnothrow',['mi_new_aligned_nothrow',['../group__cpp.html#gab5e29558926d934c3f1cae8c815f942c',1,'mimalloc-doc.h']]],
['mi_5fnew_5fn',['mi_new_n',['../group__cpp.html#gae7bc4f56cd57ed3359060ff4f38bda81',1,'mimalloc-doc.h']]],
['mi_5fnew_5fnothrow',['mi_new_nothrow',['../group__cpp.html#gaeaded64eda71ed6b1d569d3e723abc4a',1,'mimalloc-doc.h']]],
['mi_5fnew_5frealloc',['mi_new_realloc',['../group__cpp.html#gaab78a32f55149e9fbf432d5288e38e1e',1,'mimalloc-doc.h']]],
['mi_5fnew_5freallocn',['mi_new_reallocn',['../group__cpp.html#ga756f4b2bc6a7ecd0a90baea8e90c7907',1,'mimalloc-doc.h']]],
['mi_5foption_5fdisable',['mi_option_disable',['../group__options.html#gaebf6ff707a2e688ebb1a2296ca564054',1,'mimalloc-doc.h']]],
['mi_5foption_5fenable',['mi_option_enable',['../group__options.html#ga04180ae41b0d601421dd62ced40ca050',1,'mimalloc-doc.h']]],
['mi_5foption_5fget',['mi_option_get',['../group__options.html#ga7e8af195cc81d3fa64ccf2662caa565a',1,'mimalloc-doc.h']]],
['mi_5foption_5fis_5fenabled',['mi_option_is_enabled',['../group__options.html#ga459ad98f18b3fc9275474807fe0ca188',1,'mimalloc-doc.h']]],
['mi_5foption_5fset',['mi_option_set',['../group__options.html#gaf84921c32375e25754dc2ee6a911fa60',1,'mimalloc-doc.h']]],
['mi_5foption_5fset_5fdefault',['mi_option_set_default',['../group__options.html#ga7ef623e440e6e5545cb08c94e71e4b90',1,'mimalloc-doc.h']]],
['mi_5foption_5fset_5fenabled',['mi_option_set_enabled',['../group__options.html#ga9a13d05fcb77489cb06d4d017ebd8bed',1,'mimalloc-doc.h']]],
['mi_5foption_5fset_5fenabled_5fdefault',['mi_option_set_enabled_default',['../group__options.html#ga65518b69ec5d32336b50e07f74b3f629',1,'mimalloc-doc.h']]],
['mi_5fposix_5fmemalign',['mi_posix_memalign',['../group__posix.html#gacff84f226ba9feb2031b8992e5579447',1,'mimalloc-doc.h']]],
['mi_5fprocess_5finfo',['mi_process_info',['../group__extended.html#ga7d862c2affd5790381da14eb102a364d',1,'mimalloc-doc.h']]],
['mi_5fpvalloc',['mi_pvalloc',['../group__posix.html#gaeb325c39b887d3b90d85d1eb1712fb1e',1,'mimalloc-doc.h']]],
['mi_5frealloc',['mi_realloc',['../group__malloc.html#gaf11eb497da57bdfb2de65eb191c69db6',1,'mimalloc-doc.h']]],
['mi_5frealloc_5faligned',['mi_realloc_aligned',['../group__aligned.html#ga4028d1cf4aa4c87c880747044a8322ae',1,'mimalloc-doc.h']]],
['mi_5frealloc_5faligned_5fat',['mi_realloc_aligned_at',['../group__aligned.html#gaf66a9ae6c6f08bd6be6fb6ea771faffb',1,'mimalloc-doc.h']]],
['mi_5freallocarray',['mi_reallocarray',['../group__posix.html#ga48fad8648a2f1dab9c87ea9448a52088',1,'mimalloc-doc.h']]],
['mi_5freallocf',['mi_reallocf',['../group__malloc.html#gafe68ac7c5e24a65cd55c9d6b152211a0',1,'mimalloc-doc.h']]],
['mi_5freallocn',['mi_reallocn',['../group__malloc.html#ga61d57b4144ba24fba5c1e9b956d13853',1,'mimalloc-doc.h']]],
['mi_5frealpath',['mi_realpath',['../group__malloc.html#ga08cec32dd5bbe7da91c78d19f1b5bebe',1,'mimalloc-doc.h']]],
['mi_5frecalloc',['mi_recalloc',['../group__malloc.html#ga23a0fbb452b5dce8e31fab1a1958cacc',1,'mimalloc-doc.h']]],
['mi_5frecalloc_5faligned',['mi_recalloc_aligned',['../group__zeroinit.html#ga3e7e5c291acf1c7fd7ffd9914a9f945f',1,'mimalloc-doc.h']]],
['mi_5frecalloc_5faligned_5fat',['mi_recalloc_aligned_at',['../group__zeroinit.html#ga4ff5e92ad73585418a072c9d059e5cf9',1,'mimalloc-doc.h']]],
['mi_5fregister_5fdeferred_5ffree',['mi_register_deferred_free',['../group__extended.html#ga3460a6ca91af97be4058f523d3cb8ece',1,'mimalloc-doc.h']]],
['mi_5fregister_5ferror',['mi_register_error',['../group__extended.html#gaa1d55e0e894be240827e5d87ec3a1f45',1,'mimalloc-doc.h']]],
['mi_5fregister_5foutput',['mi_register_output',['../group__extended.html#gae5b17ff027cd2150b43a33040250cf3f',1,'mimalloc-doc.h']]],
['mi_5freserve_5fhuge_5fos_5fpages_5fat',['mi_reserve_huge_os_pages_at',['../group__extended.html#ga7795a13d20087447281858d2c771cca1',1,'mimalloc-doc.h']]],
['mi_5freserve_5fhuge_5fos_5fpages_5finterleave',['mi_reserve_huge_os_pages_interleave',['../group__extended.html#ga3132f521fb756fc0e8ec0b74fb58df50',1,'mimalloc-doc.h']]],
['mi_5frezalloc',['mi_rezalloc',['../group__zeroinit.html#ga8c292e142110229a2980b37ab036dbc6',1,'mimalloc-doc.h']]],
['mi_5frezalloc_5faligned',['mi_rezalloc_aligned',['../group__zeroinit.html#gacd71a7bce96aab38ae6de17af2eb2cf0',1,'mimalloc-doc.h']]],
['mi_5frezalloc_5faligned_5fat',['mi_rezalloc_aligned_at',['../group__zeroinit.html#gae8b358c417e61d5307da002702b0a8e1',1,'mimalloc-doc.h']]],
['mi_5fstats_5fmerge',['mi_stats_merge',['../group__extended.html#ga854b1de8cb067c7316286c28b2fcd3d1',1,'mimalloc-doc.h']]],
['mi_5fstats_5fprint',['mi_stats_print',['../group__extended.html#ga2d126e5c62d3badc35445e5d84166df2',1,'mimalloc-doc.h']]],
['mi_5fstats_5fprint_5fout',['mi_stats_print_out',['../group__extended.html#ga537f13b299ddf801e49a5a94fde02c79',1,'mimalloc-doc.h']]],
['mi_5fstats_5freset',['mi_stats_reset',['../group__extended.html#ga3bb8468b8cfcc6e2a61d98aee85c5f99',1,'mimalloc-doc.h']]],
['mi_5fstrdup',['mi_strdup',['../group__malloc.html#gac7cffe13f1f458ed16789488bf92b9b2',1,'mimalloc-doc.h']]],
['mi_5fstrndup',['mi_strndup',['../group__malloc.html#gaaabf971c2571891433477e2d21a35266',1,'mimalloc-doc.h']]],
['mi_5fthread_5fdone',['mi_thread_done',['../group__extended.html#ga0ae4581e85453456a0d658b2b98bf7bf',1,'mimalloc-doc.h']]],
['mi_5fthread_5finit',['mi_thread_init',['../group__extended.html#gaf8e73efc2cbca9ebfdfb166983a04c17',1,'mimalloc-doc.h']]],
['mi_5fthread_5fstats_5fprint_5fout',['mi_thread_stats_print_out',['../group__extended.html#gab1dac8476c46cb9eecab767eb40c1525',1,'mimalloc-doc.h']]],
['mi_5fusable_5fsize',['mi_usable_size',['../group__extended.html#ga089c859d9eddc5f9b4bd946cd53cebee',1,'mimalloc-doc.h']]],
['mi_5fvalloc',['mi_valloc',['../group__posix.html#ga73baaf5951f5165ba0763d0c06b6a93b',1,'mimalloc-doc.h']]],
['mi_5fzalloc',['mi_zalloc',['../group__malloc.html#gafdd9d8bb2986e668ba9884f28af38000',1,'mimalloc-doc.h']]],
['mi_5fzalloc_5faligned',['mi_zalloc_aligned',['../group__aligned.html#ga0cadbcf5b89a7b6fb171bc8df8734819',1,'mimalloc-doc.h']]],
['mi_5fzalloc_5faligned_5fat',['mi_zalloc_aligned_at',['../group__aligned.html#ga5f8c2353766db522565e642fafd8a3f8',1,'mimalloc-doc.h']]],
['mi_5fzalloc_5fsmall',['mi_zalloc_small',['../group__extended.html#ga220f29f40a44404b0061c15bc1c31152',1,'mimalloc-doc.h']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['aligned_20allocation',['Aligned Allocation',['../group__aligned.html',1,'']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['basic_20allocation',['Basic Allocation',['../group__malloc.html',1,'']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['c_2b_2b_20wrappers',['C++ wrappers',['../group__cpp.html',1,'']]]
];
-4
View File
@@ -1,4 +0,0 @@
var searchData=
[
['extended_20functions',['Extended Functions',['../group__extended.html',1,'']]]
];
-5
View File
@@ -1,5 +0,0 @@
var searchData=
[
['heap_20introspection',['Heap Introspection',['../group__analysis.html',1,'']]],
['heap_20allocation',['Heap Allocation',['../group__heap.html',1,'']]]
];
Loaded 100 of 1815 files, more files were not shown because too many files have changed in this diff. Show more