Compare commits

...
476 Commits
Author SHA1 Message Date
Rui Ueyama a9a8205be2 Bump mold version to 1.3.0 2022-06-18 11:29:18 +08:00
Rui Ueyama 0bbc1808cc Merge pull request #544 from lancethepants/main
Also copy mold-wrapper.so from docker build.
2022-06-17 22:51:42 +08:00
lancethepants cd3d82c89e Also copy mold-wrapper.so from docker build.
Signed-off-by: Lance Fredrickson <[email protected]>
2022-06-17 08:32:31 -06:00
Rui Ueyama 52c8e09ddb [ELF] Explain --icf=safe and --ignore-data-address-equality 2022-06-17 16:57:56 +08:00
Rui Ueyama c214bfc96b [Mach-O] Remove -dump
I added this option for debugging. Since mold is now mature enough,
we no longer need this option.
2022-06-16 20:53:27 +08:00
Rui Ueyama 7b8d7a6417 [Mach-O] Add -order_file 2022-06-16 16:52:25 +08:00
Rui Ueyama cfb7c268f5 [Mach-O] Do not strip __eh_frame 2022-06-16 15:26:06 +08:00
Rui Ueyama 285ea7ba26 [Mach-O] Add -u 2022-06-16 14:24:59 +08:00
Rui Ueyama 603a831f02 [Mach-O] Improve performance of __cstring merging 2022-06-16 13:17:54 +08:00
Rui Ueyama edfe9f30f2 [Mach-O] Do not enable -adhoc_codesign by default on Intel Mac 2022-06-16 12:23:03 +08:00
Rui Ueyama 5228fa2d78 Revert "[Mach-O] Add a hack to speed up code verification"
This reverts commit f3e1f728d3.
I'm not 100% sure why, but now the code signature verification works
way faster than before. Maybe it has improved in macOS 12.4 which was
released last month?
2022-06-16 12:05:22 +08:00
Rui Ueyama e4e267cc8b Simplify 2022-06-16 11:25:30 +08:00
Rui Ueyama 75ac73809b Remove dead code 2022-06-16 10:35:02 +08:00
Rui Ueyama 1b89d52504 [Mach-O] Reduce the number of personality functions 2022-06-15 20:34:00 +08:00
Rui Ueyama b05a671789 [Mach-O] Do not split into subsections if MH_SUBSECTIONS_VIA_SYMBOLS is not set 2022-06-15 17:45:25 +08:00
Rui Ueyama 30222dfb3f Revert "simplify opt_demangle setup"
This reverts commit f6b661a9051f5657ac701ea9c94dd54a85499365.
We want to make it possible to run two or more mold's `main` function
simultaneously in a single process, so we don't want to make `opt_demangle`
a global variable. That's why it was a thread-local variable.
2022-06-15 13:16:50 +08:00
Rui Ueyama 91aca3b7cf Attempt to fix CI 2022-06-15 13:12:18 +08:00
Rui Ueyama 610e839e9e Fix ASAN issue 2022-06-15 13:12:09 +08:00
Rui Ueyama dcbc85b412 Attempt to fix CI 2022-06-15 11:52:45 +08:00
Rui Ueyama 7133f8a547 Attempt to fix CI 2022-06-15 11:41:45 +08:00
Rui Ueyama 792773be20 Simplify 2022-06-15 10:40:38 +08:00
Rui Ueyama 13816a14e1 Merge pull request #538 from llunak/better-undefined
Better reporting of undefined symbols
2022-06-15 10:28:56 +08:00
Rui Ueyama 8f02509ba6 Print out an error message if mold -run is used on macOS 2022-06-09 10:36:02 -07:00
Luboš Luňák 87163c667a fix tests to work with new undefined references reporting
Signed-off-by: Luboš Luňák <[email protected]>
2022-06-09 13:32:23 +02:00
Luboš Luňák e45bebd1c7 group undefined symbols when reporting them (#508)
The output now roughly matches the lld report format, with
each symbol followed by a limited list of places that reference it.
Lld can additionally read debuginfo to report the exact location,
but that would require writing an interpreter for .debug_line.

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

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

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

  bar.foo = 0xf000;

sets `bar.foo` to 1 while

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

  bar.foo = 0xf000;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

So, we should not write to argv.

This commit is based on @ishitatsuyuki's patch.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

which make the test stuck.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
# These are supported funding model platforms
github: [rui314] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
+24 -17
View File
@@ -4,22 +4,28 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
env:
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
MOLD_SUPPRESS_MACHO_WARNING: 1
jobs:
build-clang:
strategy:
matrix:
target:
- test
- test-asan
- test-tsan
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- uses: rui314/setup-mold@v1
with:
make-default: false
- uses: rui314/setup-mold@staging
- name: apt-get
run: sudo dpkg --add-architecture i386 && sudo apt-get update && sudo apt-get install git build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libxxhash-dev libtbb-dev git bsdmainutils dwarfdump pkg-config
run: sudo apt-get update && sudo apt-get install git build-essential libstdc++-10-dev cmake clang libssl-dev zlib1g-dev libtbb-dev git bsdmainutils dwarfdump
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=clang CXX=clang++ LDFLAGS=-fuse-ld=mold -j$(nproc)
- name: make test
run: make -j$(nproc) test
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(nproc) CC=clang CXX=clang++ ${{ matrix.target }}
build-gcc:
runs-on: ubuntu-20.04
container: gcc:11.1.0
@@ -27,7 +33,7 @@ jobs:
- 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
dpkg --add-architecture i386 && apt-get update && apt-get install -y sudo libssl-dev zlib1g-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
@@ -38,19 +44,20 @@ jobs:
ln -sf /usr/bin/riscv64-linux-gnu-g++-10 /usr/bin/riscv64-linux-gnu-g++
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make CC=gcc CXX=g++ -j$(nproc)
- name: make test
run: make -j$(nproc) test-all
- name: make test-all
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(nproc) CC=gcc CXX=g++ test-all
build-macos:
strategy:
matrix:
target:
- test
- test-asan
runs-on: macos-11
steps:
- uses: actions/checkout@v2
- name: brew
run: brew install xxhash
- uses: rui314/setup-mold@staging
- name: ccache
uses: hendrikmuhs/ccache-action@v1
- name: make
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu)
- name: make test
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu) test
run: PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" make -j$(sysctl -n hw.logicalcpu) ${{ matrix.target }}
+1
View File
@@ -8,6 +8,7 @@ compile_commands.json
options.inc
core
ld
ld64
ld64.mold
gentoo
out
+1 -1
View File
@@ -9,7 +9,7 @@ RUN apt-get update && \
apt-get install -y --no-install-recommends software-properties-common && \
add-apt-repository -y ppa:ubuntu-toolchain-r/test && \
apt-get install -y --no-install-recommends build-essential git \
wget pkg-config cmake libstdc++-11-dev zlib1g-dev gpg gpg-agent && \
wget 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 && \
+43 -48
View File
@@ -1,8 +1,4 @@
# If you want to enable ASAN, run `make` with the following options:
#
# make CXXFLAGS='-fsanitize=address -g' LDFLAGS=-fsanitize=address USE_MIMALLOC=0
VERSION = 1.2.0
VERSION = 1.3.0
PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
@@ -22,9 +18,6 @@ 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
@@ -51,7 +44,11 @@ MOLD_CXXFLAGS := -std=c++20 -fno-exceptions -fno-unwind-tables \
MOLD_LDFLAGS := -pthread -lz -lm -ldl
GIT_HASH := $(shell [ -d .git ] && git rev-parse HEAD)
# Get a hash of the current git head. We don't want to use the git
# command because the command prints out a warning if running under
# sudo.
GIT_HASH := $(shell [ -f .git/HEAD ] && if grep -q '^ref:' .git/HEAD; then cat .git/`sed 's/^ref: //' .git/HEAD`; else cat .git/HEAD; fi)
ifneq ($(GIT_HASH),)
MOLD_CXXFLAGS += -DGIT_HASH=\"$(GIT_HASH)\"
endif
@@ -97,19 +94,17 @@ ifeq ($(OS), Linux)
endif
endif
NEEDS_LIBCRYPTO = 0
ifneq ($(OS), Darwin)
NEEDS_LIBCRYPTO = 1
NEEDS_LIBCRYPTO = 1
ifeq ($(OS), Darwin)
NEEDS_LIBCRYPTO = 0
endif
ifeq ($(NEEDS_LIBCRYPTO), 1)
MOLD_CXXFLAGS += $(shell $(PKG_CONFIG) --cflags-only-I openssl)
MOLD_LDFLAGS += $(shell $(PKG_CONFIG) --libs-only-L openssl) -lcrypto
MOLD_LDFLAGS += -lcrypto
endif
# '-latomic' flag is needed building on riscv64 system
# RV32 system doesn't tested yet
# seems like '-atomic' would be better but not working.
# '-latomic' flag is needed building on riscv64 system.
# Seems like '-atomic' would be better but not working.
ifeq ($(ARCH), riscv64)
MOLD_LDFLAGS += -latomic
endif
@@ -132,7 +127,7 @@ all: mold mold-wrapper.so
mold: $(OBJS) $(MIMALLOC_LIB) $(TBB_LIB)
$(CXX) $(OBJS) -o $@ $(MOLD_LDFLAGS) $(LDFLAGS)
ln -sf mold ld
ln -sf mold ld64.mold
ln -sf mold ld64
mold-wrapper.so: elf/mold-wrapper.c
$(CC) $(DEPFLAGS) $(CFLAGS) -fPIC -shared -o $@ $< $(MOLD_WRAPPER_LDFLAGS) $(LDFLAGS)
@@ -167,33 +162,22 @@ endif
echo 'Passed all tests'; \
fi
test-x86-64: all
@echo x86_64
CC=x86_64-linux-gnu-gcc CXX=x86_64-linux-gnu-g++ GCC=x86_64-linux-gnu-gcc GXX=x86_64-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=x86_64 QEMU='qemu-x86_64 -L /usr/x86_64-linux-gnu' $(MAKE) test
test-arch:
TEST_CC=${TRIPLE}-gcc \
TEST_CXX=${TRIPLE}-g++ \
TEST_GCC=${TRIPLE}-gcc \
TEST_GXX=${TRIPLE}-g++ \
OBJDUMP=${TRIPLE}-objdump \
MACHINE=${MACHINE} \
QEMU="qemu-${MACHINE} -L /usr/${TRIPLE}" \
$(MAKE) test
test-i386: all
@echo i386
CC=i686-linux-gnu-gcc CXX=i686-linux-gnu-g++ GCC=i686-linux-gnu-gcc GXX=i686-linux-gnu-g++ OBJDUMP=x86_64-linux-gnu-objdump MACHINE=i386 QEMU='qemu-i386 -L /usr/i686-linux-gnu' $(MAKE) test
test-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
test-all: all
$(MAKE) test-arch TRIPLE=x86_64-linux-gnu MACHINE=x86_64
$(MAKE) test-arch TRIPLE=i686-linux-gnu MACHINE=i386
$(MAKE) test-arch TRIPLE=aarch64-linux-gnu MACHINE=aarch64
$(MAKE) test-arch TRIPLE=arm-linux-gnueabihf MACHINE=arm
$(MAKE) test-arch TRIPLE=riscv64-linux-gnu MACHINE=riscv64
install: all
$(INSTALL) -d $D$(BINDIR)
@@ -205,7 +189,12 @@ install: all
$(STRIP) $D$(LIBDIR)/mold/mold-wrapper.so
$(INSTALL) -d $D$(LIBEXECDIR)/mold
ln -sf $(BINDIR)/mold $D$(LIBEXECDIR)/mold/ld
# We want to make a symblink with a relative path, so that users can
# move the entire directory to other place without breaking the reference.
# GNU ln supports `--relative` to do that, but that's not supported by
# non-GNU systems. So we use Python to compute a relative path.
ln -sf `python3 -c "import os.path; print(os.path.relpath('$(BINDIR)/mold', '$(LIBEXECDIR)/mold'))"` $D$(LIBEXECDIR)/mold/ld
$(INSTALL) -d $D$(MANDIR)/man1
$(INSTALL_DATA) docs/mold.1 $D$(MANDIR)/man1
@@ -218,7 +207,13 @@ uninstall:
rm -f $D$(MANDIR)/man1/mold.1
rm -rf $D$(LIBDIR)/mold
clean:
rm -rf *~ mold mold-wrapper.so out ld ld64.mold mold-*-linux.tar.gz
test-asan test-ubsan:
$(MAKE) USE_MIMALLOC=0 CXXFLAGS='-fsanitize=address -fsanitize=undefined -O0 -g' LDFLAGS='-fsanitize=address -fsanitize=undefined' test
.PHONY: all test tests check clean test-x86-64 test-i386 test-arm64 test-arm32 test-riscv64 test-all
test-tsan:
$(MAKE) USE_MIMALLOC=0 CXXFLAGS='-fsanitize=thread -O0 -g' LDFLAGS=-fsanitize=thread test
clean:
rm -rf *~ mold mold-wrapper.so out ld ld64 mold-*-linux.tar.gz
.PHONY: all test tests check clean test-arch test-all test-asan test-ubsan test-tsan
+41 -44
View File
@@ -27,8 +27,8 @@ mold currently supports x86-64, i386, ARM32, ARM64 and 64-bit RISC-V.
## Why does the speed of linking matter?
If you are using a compiled language such as C, C++ or Rust, a build
consists of two phases. In the first phase, a compiler compiles a
source file into an object file (`.o` files). In the second phase,
consists of two phases. In the first phase, a compiler compiles
source files into object files (`.o` files). In the second phase,
a linker takes all object files to combine them into a single executable
or a shared library file.
@@ -39,7 +39,7 @@ most noticeable when you are in rapid debug-edit-rebuild cycles.
## Install
Binary packages for the following distros are currently available.
Binary packages for the following systems are currently available.
[![Packaging status](https://repology.org/badge/vertical-allrepos/mold.svg)](https://repology.org/project/mold/versions)
@@ -47,32 +47,21 @@ Binary packages for the following distros are currently available.
mold is written in C++20, so if you build mold yourself, you need a
recent version of a C++ compiler and a C++ standard library. GCC 10.2
or Clang 12.0.0 as well as libstdc++ 10 or libc++ 7 are recommended.
I'm using Ubuntu 20.04 as a development platform. In that environment,
you can build mold by the following commands.
or Clang 12.0.0 (or later) as well as libstdc++ 10 or libc++ 7 (or
later) are recommended.
### Install dependencies
#### Ubuntu 20.04 and later / Debian 11 and later
```shell
sudo apt-get update
sudo apt-get install -y build-essential git clang cmake libstdc++-10-dev libssl-dev libxxhash-dev zlib1g-dev pkg-config
```
#### Fedora 34 and later
```shell
sudo dnf install -y git clang cmake openssl-devel xxhash-devel zlib-devel libstdc++-devel
```
To install build dependencies, run `./install-build-deps.sh` in this
directory. It recognizes your Linux distribution and tries to install
necessary packages. You may want to run it as root.
### Compile mold
```shell
git clone https://github.com/rui314/mold.git
cd mold
git checkout v1.2.0
git checkout v1.3.0
make -j$(nproc) CXX=clang++
sudo make install
```
@@ -86,19 +75,10 @@ By default, `mold` is installed to `/usr/local/bin`.
If you don't use a recent enough Linux distribution, or if for any reason
`make` in the above commands doesn't work for you, you can use Docker to
build it in a Docker environment. To do so, just run `./dist.sh` in this
directory instead of running `make -j$(nproc)`. The shell script creates a
Ubuntu 18.04 Docker image, installs necessary tools and libraries to it,
builds mold and auxiliary files, and packs them into a single tar file
`mold-$version-$arch-linux.tar.gz`. You can extract the tar file anywhere
and use `mold` executable in it.
`make test` depends on a few more packages. To install, run the following commands:
```shell
sudo dpkg --add-architecture i386
sudo apt update
sudo apt-get install bsdmainutils dwarfdump libc6-dev:i386 lib32gcc-10-dev libstdc++-10-dev-arm64-cross gcc-10-aarch64-linux-gnu g++-10-aarch64-linux-gnu
```
directory instead of running `make -j$(nproc)`. The shell script pulls a
Docker image, builds mold and auxiliary files inside it, and packs
them into a single tar file `mold-$version-$arch-linux.tar.gz`.
You can extract the tar file anywhere and use `mold` executable in it.
## How to use
@@ -115,7 +95,7 @@ following flags to use `mold` instead of `/usr/bin/ld`:
- Clang: pass `-fuse-ld=mold`
- GCC 12.1.0 (upcoming version) or later: pass `-fuse-ld=mold`
- GCC 12.1.0 or later: pass `-fuse-ld=mold`
- GCC before 12.1.0: `-fuse-ld` does not accept `mold` as a valid
argument, so you need to use `-B` option instead. `-B` is an option
@@ -123,7 +103,7 @@ following flags to use `mold` instead of `/usr/bin/ld`:
If you have installed mold with `make install`, there should be a
directory named `/usr/libexec/mold` (or `/usr/local/libexec/mold`,
depending on your $PREFIX), and `ld` command should be there. The
depending on your `$PREFIX`), and `ld` command should be there. The
`ld` is actually a symlink to `mold`. So, all you need is to pass
`-B/usr/libexec/mold` (or `-B/usr/local/libexec/mold`) to GCC.
@@ -170,12 +150,14 @@ replace `argv[0]` with `mold` if it is `ld`, `ld.gold` or `ld.lld`.
</details>
<details><summary>GitHub Action</summary>
If you want to use mold in your GitHub-hosted CI to speed up continuous
build, you can use <a href=https://github.com/rui314/setup-mold>setup-mold</a>
GitHub Action. GitHub runs a CI on a two-core machine, but mold is
still significantly faster than the default GNU linker there
especially when a program being linked is large.
<details><summary>GitHub Actions</summary>
You can use our <a href=https://github.com/rui314/setup-mold>setup-mold</a>
GitHub Action to speed up GitHub-hosted continuous build. GitHub Actions
runs on a two-core machine, but mold is still significantly faster than
the default GNU linker there especially when a program being linked is
large.
</details>
<details><summary>Verify that you are using mold</summary>
@@ -184,7 +166,7 @@ mold leaves its identification string in `.comment` section in an output
file. You can print it out to verify that you are actually using mold.
```shell
readelf -p .comment <executable-file>
$ readelf -p .comment <executable-file>
String dump of section '.comment':
[ 0] GCC: (Ubuntu 10.2.0-5ubuntu1~20.04) 10.2.0
@@ -214,6 +196,21 @@ artificially capped to 16 so that the bars fit in the GIF.
For details, please read [design notes](docs/design.md).
# Logo
## License
-![mold image](docs/mold.jpg)
mold is available under AGPL. Note that that does not mean that you
have to license your program under AGPL if you use mold to link your
program. An output of the mold linker is a derived work of the object
files and libraries you pass to the linker but not a derived work of
the mold linker itself.
Besides that, you can also buy a commercial, non-AGPL license with
technical support from our company, Blue Whale Systems PTE LTD. If you
are a big company, please consider obtaining it before making hundreds
or thousands of developers of your company to depend on mold. mold is
mostly a single-person open-source project, and just like other
open-source projects, we are not legally obligated to keep maintaining
it. A legally-binding commercial license contract addresses the
concern. By purchasing a license, you are guaranteed that mold will be
maintained for you. Please [contact us](mailto:[email protected])
for a commercial license inquiry.
+26
View File
@@ -1,3 +1,28 @@
// This file contains functions to read an archive file (.a file).
// An archive file is just a bundle of object files. It's similar to
// tar or zip, but the contents are not compressed.
//
// An archive file is either "regular" or "thin". A regular archive
// contains object files directly, while a thin archive contains only
// pathnames. In the latter case, actual file contents have to be read
// from given pathnames. A regular archive is sometimes called "fat"
// archive as opposed to "thin".
//
// If an archive file is given to the linker, the linker pulls out
// object files that are needed to resolve undefined symbols. So,
// bunding object files as an archive and giving that archive to the
// linker has a different meaning than directly giving the same set of
// object files to the linker. The former links only needed object
// files, while the latter links all the given object files.
//
// Therefore, if you link libc.a for example, not all the libc
// functions are linked to your binary. Instead, only object files
// that provides functions and variables used in your program get
// linked. To make this efficient, static library functions are
// usually separated to each object file in an archive file. You can
// see the contents of libc.a by runnning `ar t
// /usr/lib/x86_64-linux-gnu/libc.a`.
#pragma once
#include "mold.h"
@@ -93,6 +118,7 @@ read_thin_archive_members(C &ctx, MappedFile<C> *mf) {
std::string path = name.starts_with('/') ?
name : (filepath(mf->name).parent_path() / name).string();
vec.push_back(MappedFile<C>::must_open(ctx, path));
vec.back()->thin_parent = mf;
data = body;
}
return vec;
-81
View File
@@ -1,81 +0,0 @@
#pragma once
#include <cstdint>
#include <cstring>
#ifdef __BIG_ENDIAN__
#error "mold does not support big-endian hosts"
#endif
namespace mold {
template <typename T>
class BigEndian {
public:
BigEndian() = delete;
operator T() const {
T x;
memcpy(&x, val, sizeof(T));
return bswap(x);
}
BigEndian &operator=(T x) {
x = bswap(x);
memcpy(&val, &x, sizeof(T));
return *this;
}
BigEndian &operator++() {
return *this = *this + 1;
}
BigEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
BigEndian &operator--() {
return *this = *this - 1;
}
BigEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
BigEndian &operator+=(T x) {
return *this = *this + x;
}
BigEndian &operator&=(T x) {
return *this = *this & x;
}
BigEndian &operator|=(T x) {
return *this = *this | x;
}
private:
uint8_t val[sizeof(T)];
static T bswap(T x) {
if constexpr (sizeof(T) == 2)
return __builtin_bswap16(x);
else if constexpr (sizeof(T) == 4)
return __builtin_bswap32(x);
else
return __builtin_bswap64(x);
}
};
using ibig16 = BigEndian<int16_t>;
using ibig32 = BigEndian<int32_t>;
using ibig64 = BigEndian<int64_t>;
using ubig16 = BigEndian<uint16_t>;
using ubig32 = BigEndian<uint32_t>;
using ubig64 = BigEndian<uint64_t>;
} // namespace mold
+13
View File
@@ -48,6 +48,7 @@ read_response_file(C &ctx, std::string_view path) {
return vec;
}
// Replace "@path/to/some/text/file" with its file contents.
template <typename C>
std::vector<std::string_view> expand_response_files(C &ctx, char **argv) {
std::vector<std::string_view> vec;
@@ -60,4 +61,16 @@ std::vector<std::string_view> expand_response_files(C &ctx, char **argv) {
return vec;
}
static std::string_view string_trim(std::string_view str) {
size_t pos = str.find_first_not_of(" \t");
if (pos == str.npos)
return "";
str = str.substr(pos);
pos = str.find_last_not_of(" \t");
if (pos == str.npos)
return str;
return str.substr(0, pos + 1);
}
} // namespace mold
+6 -5
View File
@@ -89,7 +89,8 @@ static std::vector<u8> do_compress(std::string_view input) {
return buf;
}
ZlibCompressor::ZlibCompressor(std::string_view input) {
ZlibCompressor::ZlibCompressor(u8 *buf, i64 size) {
std::string_view input{(char *)buf, (size_t)size};
std::vector<std::string_view> inputs = split(input);
std::vector<u64> adlers(inputs.size());
shards.resize(inputs.size());
@@ -134,7 +135,7 @@ void ZlibCompressor::write_to(u8 *buf) {
end[-5] = 0;
// Write a checksum
*(ubig32 *)(end - 4) = checksum;
*(ub32 *)(end - 4) = checksum;
}
GzipCompressor::GzipCompressor(std::string_view input) {
@@ -183,10 +184,10 @@ void GzipCompressor::write_to(u8 *buf) {
// Write a trailer
u8 *end = buf + size();
end[-10] = 0x3; // two-byte zlib stream terminator
end[-10] = 3; // two-byte zlib stream terminator
end[-9] = 0;
*(u32 *)(end - 8) = checksum;
*(u32 *)(end - 4) = uncompressed_size;
*(ul32 *)(end - 8) = checksum;
*(ul32 *)(end - 4) = uncompressed_size;
}
} // namespace mold
+9 -9
View File
@@ -9,19 +9,19 @@
set -e
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
# Unlike Linux, macOS's uname returns arm64 for aarch64.
arch=$(uname -m)
dest=mold-$version-$arch-linux
[ $arch = arm64 ] && arch=aarch64
if [ $arch = x86_64 ]; then
image=rui314/mold-builder:v1-x86_64
elif [ $arch = aarch64 -o $arch = arm64 ]; then
image=rui314/mold-builder:v1-aarch64
else
if [ $arch != x86_64 -a $arch != aarch64 ]; then
echo "Error: no docker image for $arch"
exit 1
fi
version=$(grep '^VERSION =' $(dirname $0)/Makefile | sed 's/.* = //')
dest=mold-$version-$arch-linux
image=rui314/mold-builder:v1-$arch
docker images -q $image 2> /dev/null || docker pull $image
docker run -it --rm -v "$(pwd):/mold:Z" -u "$(id -u):$(id -g)" $image \
@@ -30,6 +30,6 @@ 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"
cp mold /mold &&
cp mold-wrapper.so /mold"
+83 -7
View File
@@ -473,6 +473,20 @@ Set
.Ar dir
to root directory.
.Pp
.It Fl -color-diagnostics Ns = Ns Op Sy auto | always | never
.It Fl -color-diagnostics
.It Fl -no-color-diagnostics
.Pp
Show diagnostics messages in color using ANSI escape sequences.
.Ar auto
means that
.Nm
prints out messages in color only if the standard output is connected to a TTY. \
Default is
.Ar auto .
.Pp
.It Fl -defsym Ns = Ns Ar symbol Ns = Ns Ar value
.Pp
.It Fl -compress-debug-sections Ns = Ns Op Sy none | zlib | zlib-gabi | zlib-gnu
Compress DWARF debug info
.Pf ( Sy .debug_*
@@ -555,6 +569,10 @@ Mark all symbols in the given
.Ar libraries
hidden.
.Pp
.It Fl -fatal-warnings
.It Fl -no-fatal-warnings
Treat warnings as errors.
.Pp
.It Fl -fini Ns = Ns Ar symbol
Call
.Ar symbol
@@ -574,12 +592,67 @@ hides that latency.
.It Fl -no-gc-sections
Remove unreferenced sections.
.Pp
.It Fl -gdb-index
Create a
.Li .gdb_index
section to speed up GNU debugger. To use this, you need to compile source files \
with the
.Fl -ggnu-pubnames
compiler flag.
.Pp
.It Fl -hash-style Ns = Ns Op Sy sysv | gnu | both
Set hash style.
.Pp
.It Fl -icf Ns = Ns Sy all
.It Fl -icf Ns = Ns Op Sy none | safe | all
.It Fl -no-icf
Fold identical code.
It is not uncommon for a program to contain many identical functions that differ \
only in name. For example, a C++ template
.Sy std::vector
is very likely to be instantiated to the identical code for
.Sy std::vector<int>
and
.Sy std::vector<unsigned>
because the container cares only about the size of the parameter type. \
Identical Code Folding (ICF) is a size optimization to identify and merge \
such identical functions.
.Pp
If
.Fl -icf=all
is given,
.Nm
tries to merge all identical functions. This reduces the size of the output \
most, but it is not
.Dq safe
optimization. It is guaranteed in C and C++ that two pointeres pointing two \
different functions will never be equal, but
.Fl -icf=all
breaks that assumption as two functions have the same address after merging. \
So a care must be taken when you use that flag that your program does not \
depend on the function pointer uniqueness.
.Pp
.Fl -icf=safe
is a flag to merge functions only when it is safe to do so. That is, if a \
program does not take an address of a function, it is safe to merge that \
function with other function, as you cannot compare a function pointer \
with something else without taking an address of a function.
.FL -icf=safe
needs to be used with a compiler that supports
.Sy .llvm_addrsig
section which contains the information as to what symbols are address-taken. \
LLVM/Clang supports that section by default. Since GCC does not suppor it yet, \
you cannot use
.Fl -icf=safe
with GCC (it doesn't do any harm but can't optimize at all.)
.Pp
.Fl -icf=none
and
.Fl -no-icf
disables ICF.
.Pp
.It Fl -ignore-data-address-equality
Make ICF to merge not only functions but also data. This option should be \
used in combination with
.Fl -icf=all .
.Pp
.It Fl -image-base Ns = Ns Ar addr
Set the base address to
@@ -620,6 +693,14 @@ to run executables or shared libraries linked with
.Fl -pack-dyn-relocs=relr ,
and only ChromeOS, Android and Fuchsia support it as of now in 2022.
.Pp
.It -package-metadata Ns = Ns string
Embed
.Ar string
to a .note.package section. This option in intended to be used by a \
package management command such as
.Cm rpm
to embed metadata regarding a package to each executable file.
.Pp
.It Fl -perf
Print performance statistics.
.Pp
@@ -1044,20 +1125,15 @@ Mark object to interpose all DSOs but executable.
.It Fl EL
.It Fl O Ns Ar number
.It Fl -allow-shlib-undefined
.It Fl -color-diagnostics
.It Fl -dc
.It Fl -dp
.It Fl -end-group
.It Fl -fatal-warnings
.It Fl -gdb-index
.It Fl -no-add-needed
.It Fl -no-allow-shlib-undefined
.It Fl -no-copy-dt-needed-entries
.It Fl -no-fatal-warnings
.It Fl -no-undefined-version
.It Fl -nostdlib
.It Fl -plugin
.It Fl -plugin-opt
.It Fl -rpath-link Ns = Ns Ar dir
.It Fl -sort-common
.It Fl -sort-section
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

+64 -69
View File
@@ -35,23 +35,10 @@ namespace mold::elf {
using E = ARM32;
static u32 bit(u32 val, i64 pos) {
return (val >> pos) & 1;
}
// Returns [hi:lo] bits of val.
static u32 bits(u32 val, i64 hi, i64 lo) {
return (val >> lo) & ((1LL << (hi - lo + 1)) - 1);
}
static i32 sign_extend(u32 val, u32 size) {
return (i64)(val << (31 - size)) >> (31 - size);
};
static void write_mov_imm(u8 *loc, u32 val) {
u32 imm12 = bits(val, 11, 0);
u32 imm4 = bits(val, 15, 12);
*(u32 *)loc = (*(u32 *)loc & 0xfff0f000) | (imm4 << 16) | imm12;
*(ul32 *)loc = (*(ul32 *)loc & 0xfff0f000) | (imm4 << 16) | imm12;
}
static void write_thm_b_imm(u8 *loc, u32 val) {
@@ -64,9 +51,9 @@ static void write_thm_b_imm(u8 *loc, u32 val) {
u32 imm10 = bits(val, 21, 12);
u32 imm11 = bits(val, 11, 1);
*(u16 *)loc = (*(u16 *)loc & 0xf800) | (sign << 10) | imm10;
*(u16 *)(loc + 2) =
(*(u16 *)(loc + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | imm11;
*(ul16 *)loc = (*(ul16 *)loc & 0xf800) | (sign << 10) | imm10;
*(ul16 *)(loc + 2) =
(*(ul16 *)(loc + 2) & 0xd000) | (J1 << 13) | (J2 << 11) | imm11;
}
static void write_thm_mov_imm(u8 *loc, u32 val) {
@@ -75,9 +62,9 @@ static void write_thm_mov_imm(u8 *loc, u32 val) {
u32 i = bit(val, 11);
u32 imm3 = bits(val, 10, 8);
u32 imm8 = bits(val, 7, 0);
*(u16 *)loc = (*(u16 *)loc & 0b1111'1011'1111'0000) | (i << 10) | imm4;
*(u16 *)(loc + 2) =
((*(u16 *)(loc + 2)) & 0b1000'1111'0000'0000) | (imm3 << 12) | imm8;
*(ul16 *)loc = (*(ul16 *)loc & 0b1111'1011'1111'0000) | (i << 10) | imm4;
*(ul16 *)(loc + 2) =
((*(ul16 *)(loc + 2)) & 0b1000'1111'0000'0000) | (imm3 << 12) | imm8;
}
template <>
@@ -85,15 +72,18 @@ void PltSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->shdr.sh_offset;
static const u32 plt0[] = {
0xe52de004, // 1: push {lr}
0xe59fe004, // ldr lr, [pc, #4]
0xe08fe00e, // add lr, pc, lr
0xe5bef008, // ldr pc, [lr, #8]!
0x00000000, // .word .got.plt - 1b - 16
0xe52de004, // push {lr}
0xe59fe004, // ldr lr, 2f
0xe08fe00e, // 1: add lr, pc, lr
0xe5bef008, // ldr pc, [lr, #8]!
0x00000000, // 2: .word .got.plt - 1b - 8
0xe320f000, // nop
0xe320f000, // nop
0xe320f000, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 16;
*(ul32 *)(buf + 16) = ctx.gotplt->shdr.sh_addr - this->shdr.sh_addr - 16;
for (Symbol<E> *sym : symbols) {
static const u32 plt[] = {
@@ -105,7 +95,7 @@ void PltSection<E>::copy_buf(Context<E> &ctx) {
u8 *ent = buf + sizeof(plt0) + sym->get_plt_idx(ctx) * sizeof(plt);
memcpy(ent, plt, sizeof(plt));
*(u32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 12;
*(ul32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 12;
}
}
@@ -123,20 +113,20 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u8 *ent = buf + sym->get_pltgot_idx(ctx) * sizeof(plt);
memcpy(ent, plt, sizeof(plt));
*(u32 *)(ent + 12) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 12;
*(ul32 *)(ent + 12) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 12;
}
}
// ARM does not use .eh_frame for exception handling. Instead, it uses
// .ARM.exidx and .ARM.extab. So this function is empty.
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {}
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
@@ -167,17 +157,17 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
case R_ARM_ABS32:
case R_ARM_TARGET1:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u32 *)loc = S + A;
*(ul32 *)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;
*(ul32 *)loc = S + A;
}
continue;
case R_ARM_REL32:
*(u32 *)loc = S + A - P;
*(ul32 *)loc = S + A - P;
continue;
case R_ARM_THM_CALL:
// THM_CALL relocation refers either BL or BLX instruction.
@@ -187,26 +177,26 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
// 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
*(ul16 *)(loc + 2) |= (1 << 12); // rewrite with BL
} else if (T) {
write_thm_b_imm(loc, S + A - P);
*(u16 *)(loc + 2) |= (1 << 12); // rewrite with BL
*(ul16 *)(loc + 2) |= (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
*(ul16 *)(loc + 2) &= ~(1 << 12); // rewrite with BLX
}
continue;
case R_ARM_BASE_PREL:
*(u32 *)loc = GOT + A - P;
*(ul32 *)loc = GOT + A - P;
continue;
case R_ARM_GOT_PREL:
*(u32 *)loc = G + A - P;
*(ul32 *)loc = G + A - P;
continue;
case R_ARM_GOT_BREL:
*(u32 *)loc = G + A;
*(ul32 *)loc = G + A;
continue;
case R_ARM_TARGET2:
*(u32 *)loc = GOT + G + A - P;
*(ul32 *)loc = GOT + G + A - P;
continue;
case R_ARM_CALL:
case R_ARM_JUMP24: {
@@ -220,13 +210,13 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
val = S + A - P;
}
*(u32 *)loc = (*(u32 *)loc & 0xff00'0000) | ((val >> 2) & 0x00ff'ffff);
*(ul32 *)loc = (*(ul32 *)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);
*(ul16 *)loc = (*(ul16 *)loc & 0xf800) | (val & 0x07ff);
continue;
}
case R_ARM_THM_JUMP24:
@@ -251,7 +241,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
continue;
case R_ARM_PREL31: {
u32 val = S + A - P;
*(u32 *)loc = (*(u32 *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
*(ul32 *)loc = (*(ul32 *)loc & 0x8000'0000) | (val & 0x7fff'ffff);
continue;
}
case R_ARM_THM_MOVW_ABS_NC:
@@ -270,34 +260,34 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
write_thm_mov_imm(loc, (S + A) >> 16);
continue;
case R_ARM_TLS_GD32:
*(u32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
*(ul32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LDM32:
*(u32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
*(ul32 *)loc = ctx.got->get_tlsld_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LDO32:
*(u32 *)loc = S + A - ctx.tls_begin;
*(ul32 *)loc = S + A - ctx.tls_begin;
continue;
case R_ARM_TLS_IE32:
*(u32 *)loc = sym.get_gottp_addr(ctx) + A - P;
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
continue;
case R_ARM_TLS_LE32:
*(u32 *)loc = S + A - ctx.tls_begin + 8;
*(ul32 *)loc = S + A - ctx.tls_begin + 8;
continue;
case R_ARM_TLS_GOTDESC:
if (sym.get_tlsdesc_idx(ctx) == -1)
*(u32 *)loc = S - ctx.tls_begin + 8;
*(ul32 *)loc = S - ctx.tls_begin + 8;
else
*(u32 *)loc = sym.get_tlsdesc_addr(ctx) + A - P - 6;
*(ul32 *)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;
*(ul32 *)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
*(ul16 *)(loc + 2) &= ~(1 << 12); // rewrite BL with BLX
}
continue;
default:
@@ -315,7 +305,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -326,7 +316,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -339,16 +329,19 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_ARM_ABS32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul32 *)loc = *val;
break;
}
}
*(ul32 *)loc = S + A;
break;
case R_ARM_TLS_LDO32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
*(ul32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
*(ul32 *)loc = S + A - ctx.tls_begin;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
@@ -366,7 +359,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -377,7 +370,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -493,10 +486,11 @@ 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 u8 insn[] = {
0x40, 0xf2, 0x00, 0x0c, // movw ip, $0
0xc0, 0xf2, 0x00, 0x0c, // movt ip, $0
0xfc, 0x44, // add ip, pc
0x60, 0x47, // bx ip
};
static_assert(sizeof(insn) == ENTRY_SIZE);
@@ -505,7 +499,8 @@ void ThumbToArmSection::copy_buf(Context<E> &ctx) {
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));
write_thm_mov_imm(buf + offset, val);
write_thm_mov_imm(buf + offset + 4, val >> 16);
offset += sizeof(insn);
}
}
@@ -556,8 +551,8 @@ void sort_arm_exidx(Context<E> &ctx) {
// 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;
ul32 addr;
ul32 val;
};
if (osec->shdr.sh_size % sizeof(Entry))
+112 -218
View File
@@ -7,15 +7,18 @@ namespace mold::elf {
using E = ARM64;
static void write_adr(u8 *buf, u64 val) {
u32 hi = (val & 0x1ffffc) << 3;
u32 lo = (val & 3) << 29;
*(u32 *)buf = (*(u32 *)buf & 0x9f00001f) | hi | lo;
static void write_adrp(u8 *buf, u64 val) {
u32 hi = bits(val, 32, 14);
u32 lo = bits(val, 13, 12);
u32 op = *(ul32 *)buf & 0b1001'1111'0000'0000'0000'0000'0001'1111;
*(ul32 *)buf = (lo << 29) | (hi << 5) | op;
}
// Returns [hi:lo] bits of val.
static u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
static void write_adr(u8 *buf, u64 val) {
u32 hi = bits(val, 20, 2);
u32 lo = bits(val, 1, 0);
u32 op = *(ul32 *)buf & 0b1001'1111'0000'0000'0000'0000'0001'1111;
*(ul32 *)buf = (lo << 29) | (hi << 5) | op;
}
static u64 page(u64 val) {
@@ -39,9 +42,9 @@ static void write_plt_header(Context<E> &ctx, u8 *buf) {
u64 plt = ctx.plt->shdr.sh_addr;
memcpy(buf, plt0, sizeof(plt0));
write_adr(buf + 4, bits(page(gotplt) - page(plt + 4), 32, 12));
*(u32 *)(buf + 8) |= bits(gotplt, 11, 3) << 10;
*(u32 *)(buf + 12) |= ((gotplt) & 0xfff) << 10;
write_adrp(buf + 4, page(gotplt) - page(plt + 4));
*(ul32 *)(buf + 8) |= bits(gotplt, 11, 3) << 10;
*(ul32 *)(buf + 12) |= (gotplt & 0xfff) << 10;
}
static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
@@ -58,9 +61,9 @@ static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym) {
u64 plt = sym.get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_adr(ent, bits(page(gotplt) - page(plt), 32, 12));
*(u32 *)(ent + 4) |= bits(gotplt, 11, 3) << 10;
*(u32 *)(ent + 8) |= (gotplt & 0xfff) << 10;
write_adrp(ent, page(gotplt) - page(plt));
*(ul32 *)(ent + 4) |= bits(gotplt, 11, 3) << 10;
*(ul32 *)(ent + 8) |= (gotplt & 0xfff) << 10;
}
template <>
@@ -89,25 +92,25 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u64 plt = sym->get_plt_addr(ctx);
memcpy(ent, data, sizeof(data));
write_adr(ent, bits(page(got) - page(plt), 32, 12));
*(u32 *)(ent + 4) |= bits(got, 11, 3) << 10;
write_adrp(ent, page(got) - page(plt));
*(ul32 *)(ent + 4) |= bits(got, 11, 3) << 10;
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_AARCH64_ABS64:
*(u64 *)loc = val;
*(ul64 *)loc = val;
return;
case R_AARCH64_PREL32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
return;
case R_AARCH64_PREL64:
*(u64 *)loc = val - this->shdr.sh_addr - offset;
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
return;
}
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
@@ -116,8 +119,7 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<RangeExtensionRef> range_extn = get_range_extn();
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
@@ -145,7 +147,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define A (frag_ref ? (u64)frag_ref->addend : (u64)rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.got->shdr.sh_addr)
#define GOT ctx.got->shdr.sh_addr
@@ -153,59 +155,59 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
switch (rel.r_type) {
case R_AARCH64_ABS64:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u64 *)loc = S + A;
*(ul64 *)loc = S + A;
} else if (sym.is_imported) {
*dynrel++ = {P, R_AARCH64_ABS64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
*(ul64 *)loc = A;
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_AARCH64_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
*(ul64 *)loc = S + A;
}
continue;
case R_AARCH64_LDST8_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 0) << 10;
*(ul32 *)loc |= bits(S + A, 11, 0) << 10;
continue;
case R_AARCH64_LDST16_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 1) << 10;
*(ul32 *)loc |= bits(S + A, 11, 1) << 10;
continue;
case R_AARCH64_LDST32_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 2) << 10;
*(ul32 *)loc |= bits(S + A, 11, 2) << 10;
continue;
case R_AARCH64_LDST64_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 3) << 10;
*(ul32 *)loc |= bits(S + A, 11, 3) << 10;
continue;
case R_AARCH64_LDST128_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 4) << 10;
*(ul32 *)loc |= bits(S + A, 11, 4) << 10;
continue;
case R_AARCH64_ADD_ABS_LO12_NC:
*(u32 *)loc |= bits(S + A, 11, 0) << 10;
*(ul32 *)loc |= bits(S + A, 11, 0) << 10;
continue;
case R_AARCH64_MOVW_UABS_G0:
case R_AARCH64_MOVW_UABS_G0_NC:
*(u32 *)loc |= bits(S + A, 15, 0) << 5;
*(ul32 *)loc |= bits(S + A, 15, 0) << 5;
continue;
case R_AARCH64_MOVW_UABS_G1:
case R_AARCH64_MOVW_UABS_G1_NC:
*(u32 *)loc |= bits(S + A, 31, 16) << 5;
*(ul32 *)loc |= bits(S + A, 31, 16) << 5;
continue;
case R_AARCH64_MOVW_UABS_G2:
case R_AARCH64_MOVW_UABS_G2_NC:
*(u32 *)loc |= bits(S + A, 47, 32) << 5;
*(ul32 *)loc |= bits(S + A, 47, 32) << 5;
continue;
case R_AARCH64_MOVW_UABS_G3:
*(u32 *)loc |= bits(S + A, 63, 48) << 5;
*(ul32 *)loc |= bits(S + A, 63, 48) << 5;
continue;
case R_AARCH64_ADR_GOT_PAGE: {
i64 val = page(G + GOT + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
continue;
}
case R_AARCH64_ADR_PREL_PG_HI21: {
i64 val = page(S + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
continue;
}
case R_AARCH64_ADR_PREL_LO21: {
@@ -219,7 +221,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
if (sym.esym().is_undef_weak()) {
// On ARM, calling an weak undefined symbol jumps to the
// next instruction.
*(u32 *)loc |= 1;
*(ul32 *)loc |= 1;
continue;
}
@@ -228,83 +230,83 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
i64 val = S + A - P;
if (val < lo || hi <= val) {
RangeExtensionRef ref = range_extn[i];
RangeExtensionRef ref = extra.range_extn[i];
val = output_section->thunks[ref.thunk_idx]->get_addr(ref.sym_idx) + A - P;
assert(lo <= val && val < hi);
}
*(u32 *)loc |= (val >> 2) & 0x3ffffff;
*(ul32 *)loc |= (val >> 2) & 0x3ffffff;
continue;
}
case R_AARCH64_CONDBR19:
case R_AARCH64_LD_PREL_LO19: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 20), (i64)1 << 20);
*(u32 *)loc |= bits(val, 20, 2) << 5;
*(ul32 *)loc |= bits(val, 20, 2) << 5;
continue;
}
case R_AARCH64_PREL16: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 15), (i64)1 << 15);
*(u16 *)loc = val;
*(ul16 *)loc = val;
continue;
}
case R_AARCH64_PREL32: {
i64 val = S + A - P;
overflow_check(val, -((i64)1 << 31), (i64)1 << 32);
*(u32 *)loc = val;
*(ul32 *)loc = val;
continue;
}
case R_AARCH64_PREL64:
*(u64 *)loc = S + A - P;
*(ul64 *)loc = S + A - P;
continue;
case R_AARCH64_LD64_GOT_LO12_NC:
*(u32 *)loc |= bits(G + GOT + A, 11, 3) << 10;
*(ul32 *)loc |= bits(G + GOT + A, 11, 3) << 10;
continue;
case R_AARCH64_LD64_GOTPAGE_LO15: {
i64 val = G + GOT + A - page(GOT);
overflow_check(val, 0, 1 << 15);
*(u32 *)loc |= bits(val, 14, 3) << 10;
*(ul32 *)loc |= bits(val, 14, 3) << 10;
continue;
}
case R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: {
i64 val = page(sym.get_gottp_addr(ctx) + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
continue;
}
case R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC:
*(u32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
*(ul32 *)loc |= bits(sym.get_gottp_addr(ctx) + A, 11, 3) << 10;
continue;
case R_AARCH64_TLSLE_ADD_TPREL_HI12: {
i64 val = S + A - ctx.tls_begin + 16;
overflow_check(val, 0, (i64)1 << 24);
*(u32 *)loc |= bits(val, 23, 12) << 10;
*(ul32 *)loc |= bits(val, 23, 12) << 10;
continue;
}
case R_AARCH64_TLSLE_ADD_TPREL_LO12:
case R_AARCH64_TLSLE_ADD_TPREL_LO12_NC:
*(u32 *)loc |= bits(S + A - ctx.tls_begin + 16, 11, 0) << 10;
*(ul32 *)loc |= bits(S + A - ctx.tls_begin + 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));
write_adrp(loc, val);
continue;
}
case R_AARCH64_TLSGD_ADD_LO12_NC:
*(u32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A, 11, 0) << 10;
*(ul32 *)loc |= bits(sym.get_tlsgd_addr(ctx) + A, 11, 0) << 10;
continue;
case R_AARCH64_TLSDESC_ADR_PAGE21: {
if (ctx.relax_tlsdesc && !sym.is_imported) {
// adrp x0, 0 -> movz x0, #tls_ofset_hi, lsl #16
i64 val = (S + A - ctx.tls_begin + 16);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
*(u32 *)loc = 0xd2a00000 | (bits(val, 32, 16) << 5);
*(ul32 *)loc = 0xd2a00000 | (bits(val, 32, 16) << 5);
} else {
i64 val = page(sym.get_tlsdesc_addr(ctx) + A) - page(P);
overflow_check(val, -((i64)1 << 32), (i64)1 << 32);
write_adr(loc, bits(val, 32, 12));
write_adrp(loc, val);
}
continue;
}
@@ -312,23 +314,23 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
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);
*(ul32 *)loc = 0xf2800000 | (offset_lo << 5);
} else {
*(u32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 3) << 10;
*(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 3) << 10;
}
continue;
case R_AARCH64_TLSDESC_ADD_LO12:
if (ctx.relax_tlsdesc && !sym.is_imported) {
// add x0, x0, #0 -> nop
*(u32 *)loc = 0xd503201f;
*(ul32 *)loc = 0xd503201f;
} else {
*(u32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 0) << 10;
*(ul32 *)loc |= bits(sym.get_tlsdesc_addr(ctx) + A, 11, 0) << 10;
}
continue;
case R_AARCH64_TLSDESC_CALL:
if (ctx.relax_tlsdesc && !sym.is_imported) {
// blr x2 -> nop
*(u32 *)loc = 0xd503201f;
*(ul32 *)loc = 0xd503201f;
}
continue;
default:
@@ -345,7 +347,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -356,7 +358,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -365,17 +367,20 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::tie(frag, addend) = get_fragment(ctx, rel);
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
#define A (frag ? (u64)addend : (u64)rel.r_addend)
switch (rel.r_type) {
case R_AARCH64_ABS64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul64 *)loc = *val;
break;
}
}
*(ul64 *)loc = S + A;
continue;
case R_AARCH64_ABS32:
*(u32 *)loc = S + A;
*(ul32 *)loc = S + A;
continue;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
@@ -393,7 +398,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -404,7 +409,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -498,11 +503,17 @@ static void reset_thunk(RangeExtensionThunk<E> &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())
// We pessimistically assume that PLT entries are unreacahble.
if (sym.has_plt(ctx))
return false;
// We create thunks with a pessimistic assumption that all
// out-of-section relocations would be out-of-range.
InputSection<E> *isec2 = sym.get_input_section();
if (!isec2 || isec.output_section != isec2->output_section)
return false;
if (isec2->offset == -1)
return false;
// Compute a distance between the relocated place and the symbol
@@ -520,15 +531,23 @@ 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) {
// ARM64's call/jump instructions take 27 bits displacement, so they
// can refer only up to ±128 MiB. If a branch target is further than
// that, we need to let it branch to a linker-synthesized code
// sequence that construct a full 32 bit address in a register and
// jump there. That linker-synthesized code is called "thunk".
void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
std::span<InputSection<E> *> members = osec.members;
if (members.empty())
return;
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.
// Initialize input sections with a dummy offset so that we can
// distinguish sections that have got an address with the one who
// haven't.
tbb::parallel_for((i64)1, (i64)members.size(), [&](i64 i) {
members[i]->offset = 1 << 31;
members[i]->offset = -1;
});
// We create thunks from the beginning of the section to the end.
@@ -572,8 +591,8 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// 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();
std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
std::vector<RangeExtensionRef> &range_extn = isec->extra.range_extn;
range_extn.resize(rels.size());
for (i64 i = 0; i < rels.size(); i++) {
@@ -583,6 +602,10 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
// Skip if the symbol is undefined. apply_reloc() will report an error.
if (!sym.file)
continue;
// Skip if the destination is within reach.
if (is_reachable(ctx, sym, *isec, rel))
continue;
@@ -609,7 +632,10 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
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; });
sort(thunk.symbols, [](Symbol<E> *a, Symbol<E> *b) {
return std::tuple{a->file->priority, a->sym_idx} <
std::tuple{b->file->priority, b->sym_idx};
});
// Assign offsets within the thunk to the symbols.
for (i64 i = 0; Symbol<E> *sym : thunk.symbols) {
@@ -620,10 +646,10 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
// 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();
std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
std::vector<RangeExtensionRef> &range_extn = isec->extra.range_extn;
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;
@@ -641,126 +667,6 @@ static void create_thunks(Context<E> &ctx, OutputSection<E> &osec) {
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;
@@ -778,21 +684,9 @@ void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
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;
write_adrp(loc, page(S) - page(P));
*(ul32 *)(loc + 4) |= bits(S, 11, 0) << 10;
}
}
void write_thunks(Context<E> &ctx) {
Timer t(ctx, "write_thunks");
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
tbb::parallel_for_each(osec->thunks,
[&](std::unique_ptr<RangeExtensionThunk<E>> &thunk) {
thunk->copy_buf(ctx);
});
});
}
} // namespace mold::elf
+38 -35
View File
@@ -12,8 +12,8 @@ static void write_plt_header(Context<E> &ctx, u8 *buf) {
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 8;
*(ul32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 4;
*(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.got->shdr.sh_addr + 8;
} else {
static const u8 plt0[] = {
0xff, 0x35, 0, 0, 0, 0, // pushl GOTPLT+4
@@ -21,8 +21,8 @@ static void write_plt_header(Context<E> &ctx, u8 *buf) {
0x90, 0x90, 0x90, 0x90, // nop
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr + 4;
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr + 8;
*(ul32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr + 4;
*(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr + 8;
}
}
@@ -37,7 +37,7 @@ static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
*(ul32 *)(ent + 2) = sym.get_gotplt_addr(ctx) - ctx.got->shdr.sh_addr;
} else {
static const u8 data[] = {
0xff, 0x25, 0, 0, 0, 0, // jmp *foo@GOT
@@ -45,11 +45,11 @@ static void write_plt_entry(Context<E> &ctx, u8 *buf, Symbol<E> &sym,
0xe9, 0, 0, 0, 0, // jmp .PLT0@PC
};
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym.get_gotplt_addr(ctx);
*(ul32 *)(ent + 2) = sym.get_gotplt_addr(ctx);
}
*(u32 *)(ent + 7) = idx * sizeof(ElfRel<E>);
*(u32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx) - 16;
*(ul32 *)(ent + 7) = idx * sizeof(ElfRel<E>);
*(ul32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym.get_plt_addr(ctx) - 16;
}
template <>
@@ -74,7 +74,7 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
for (i64 i = 0; i < symbols.size(); i++) {
u8 *ent = buf + i * sizeof(data);
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.got->shdr.sh_addr;
*(ul32 *)(ent + 2) = symbols[i]->get_got_addr(ctx) - ctx.got->shdr.sh_addr;
}
} else {
static const u8 data[] = {
@@ -85,13 +85,13 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
for (i64 i = 0; i < symbols.size(); i++) {
u8 *ent = buf + i * sizeof(data);
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = symbols[i]->get_got_addr(ctx);
*(ul32 *)(ent + 2) = symbols[i]->get_got_addr(ctx);
}
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
@@ -99,10 +99,10 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
case R_386_NONE:
return;
case R_386_32:
*(u32 *)loc = val;
*(ul32 *)loc = val;
return;
case R_386_PC32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
return;
}
unreachable();
@@ -111,7 +111,7 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
if (ctx.reldyn)
@@ -149,16 +149,16 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write32 = [&](u64 val) {
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
@@ -232,7 +232,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 3, insn, sizeof(insn));
*(u32 *)(loc + 5) = ctx.tls_end - S - A;
*(ul32 *)(loc + 5) = ctx.tls_end - S - A;
break;
}
case R_386_GOT32: {
@@ -241,7 +241,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x81, 0xe8, 0, 0, 0, 0, // add $0, %rax
};
memcpy(loc - 2, insn, sizeof(insn));
*(u32 *)(loc + 6) = ctx.tls_end - S - A;
*(ul32 *)(loc + 6) = ctx.tls_end - S - A;
break;
}
default:
@@ -325,7 +325,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -336,7 +336,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -363,12 +363,12 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
@@ -384,10 +384,13 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
write16(S + A);
continue;
case R_386_32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
else
*(u32 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul32 *)loc = *val;
continue;
}
}
*(ul32 *)loc = S + A;
continue;
case R_386_PC8:
write8s(S + A);
@@ -396,22 +399,22 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
write16s(S + A);
continue;
case R_386_PC32:
*(u32 *)loc = S + A;
*(ul32 *)loc = S + A;
continue;
case R_386_GOTPC:
*(u32 *)loc = GOT + A;
*(ul32 *)loc = GOT + A;
continue;
case R_386_GOTOFF:
*(u32 *)loc = S + A - GOT;
*(ul32 *)loc = S + A - GOT;
continue;
case R_386_TLS_LDO_32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
*(ul32 *)loc = *val;
else
*(u32 *)loc = S + A - ctx.tls_begin;
*(ul32 *)loc = S + A - ctx.tls_begin;
continue;
case R_386_SIZE32:
*(u32 *)loc = sym.esym().st_size + A;
*(ul32 *)loc = sym.esym().st_size + A;
continue;
default:
unreachable();
@@ -428,7 +431,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -439,7 +442,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
+109 -125
View File
@@ -7,15 +7,6 @@ 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;
}
@@ -56,43 +47,43 @@ static u32 cjtype(u32 val) {
bit(val, 1) << 3 | bit(val, 5) << 2;
}
static void write_itype(u32 *loc, u32 val) {
static void write_itype(u8 *loc, u32 val) {
u32 mask = 0b000000'00000'11111'111'11111'1111111;
*loc = (*loc & mask) | itype(val);
*(ul32 *)loc = (*(ul32 *)loc & mask) | itype(val);
}
static void write_stype(u32 *loc, u32 val) {
static void write_stype(u8 *loc, u32 val) {
u32 mask = 0b000000'11111'11111'111'00000'1111111;
*loc = (*loc & mask) | stype(val);
*(ul32 *)loc = (*(ul32 *)loc & mask) | stype(val);
}
static void write_btype(u32 *loc, u32 val) {
static void write_btype(u8 *loc, u32 val) {
u32 mask = 0b000000'11111'11111'111'00000'1111111;
*loc = (*loc & mask) | btype(val);
*(ul32 *)loc = (*(ul32 *)loc & mask) | btype(val);
}
static void write_utype(u32 *loc, u32 val) {
static void write_utype(u8 *loc, u32 val) {
u32 mask = 0b000000'00000'00000'000'11111'1111111;
*loc = (*loc & mask) | utype(val);
*(ul32 *)loc = (*(ul32 *)loc & mask) | utype(val);
}
static void write_jtype(u32 *loc, u32 val) {
static void write_jtype(u8 *loc, u32 val) {
u32 mask = 0b000000'00000'00000'000'11111'1111111;
*loc = (*loc & mask) | jtype(val);
*(ul32 *)loc = (*(ul32 *)loc & mask) | jtype(val);
}
static void write_cbtype(u16 *loc, u32 val) {
static void write_cbtype(u8 *loc, u32 val) {
u32 mask = 0b1110001110000011;
*loc = (*loc & mask) | cbtype(val);
*(ul16 *)loc = (*(ul16 *)loc & mask) | cbtype(val);
}
static void write_cjtype(u16 *loc, u32 val) {
static void write_cjtype(u8 *loc, u32 val) {
u32 mask = 0b1110000000000011;
*loc = (*loc & mask) | cjtype(val);
*(ul16 *)loc = (*(ul16 *)loc & mask) | cjtype(val);
}
static void write_plt_header(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + ctx.plt->shdr.sh_offset);
u8 *buf = ctx.buf + ctx.plt->shdr.sh_offset;
static const u32 plt0[] = {
0x00000397, // auipc t2, %pcrel_hi(.got.plt)
@@ -110,13 +101,13 @@ static void write_plt_header(Context<E> &ctx) {
memcpy(buf, plt0, sizeof(plt0));
write_utype(buf, gotplt - plt);
write_itype(buf + 2, gotplt - plt);
write_itype(buf + 4, gotplt - plt);
write_itype(buf + 8, gotplt - plt);
write_itype(buf + 16, 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);
u8 *ent = 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])
@@ -130,7 +121,7 @@ static void write_plt_entry(Context<E> &ctx, Symbol<E> &sym) {
memcpy(ent, data, sizeof(data));
write_utype(ent, gotplt - plt);
write_itype(ent + 1, gotplt - plt);
write_itype(ent + 4, gotplt - plt);
}
template <>
@@ -142,7 +133,7 @@ void PltSection<E>::copy_buf(Context<E> &ctx) {
template <>
void PltGotSection<E>::copy_buf(Context<E> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->shdr.sh_offset);
u8 *buf = ctx.buf + this->shdr.sh_offset;
static const u32 data[] = {
0x00000e17, // auipc t3, %pcrel_hi([email protected])
@@ -152,33 +143,33 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
};
for (Symbol<E> *sym : symbols) {
u32 *ent = buf + sym->get_pltgot_idx(ctx) * 4;
u8 *ent = buf + sym->get_pltgot_idx(ctx) * 16;
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);
write_itype(ent + 4, got - plt);
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
switch (rel.r_type) {
case R_RISCV_ADD32:
*(u32 *)loc += val;
*(ul32 *)loc += val;
return;
case R_RISCV_SUB8:
*loc -= val;
return;
case R_RISCV_SUB16:
*(u16 *)loc -= val;
*(ul16 *)loc -= val;
return;
case R_RISCV_SUB32:
*(u32 *)loc -= val;
*(ul32 *)loc -= val;
return;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - val) & 0b0011'1111);
@@ -190,10 +181,10 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
*loc = val;
return;
case R_RISCV_SET16:
*(u16 *)loc = val;
*(ul16 *)loc = val;
return;
case R_RISCV_32_PCREL:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
return;
}
Fatal(ctx) << "unsupported relocation in .eh_frame: " << rel;
@@ -202,8 +193,7 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<i32> r_deltas = get_r_deltas();
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
@@ -218,7 +208,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
continue;
Symbol<E> &sym = *file.symbols[rel.r_sym];
i64 r_offset = rel.r_offset + r_deltas[i];
i64 r_offset = rel.r_offset + extra.r_deltas[i];
u8 *loc = base + r_offset;
const SectionFragmentRef<E> *frag_ref = nullptr;
@@ -226,25 +216,25 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
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 A (frag_ref ? (u64)frag_ref->addend : (u64)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;
*(ul32 *)loc = S + A;
break;
case R_RISCV_64:
if (sym.is_absolute() || !ctx.arg.pic) {
*(u64 *)loc = S + A;
*(ul64 *)loc = S + A;
} else if (sym.is_imported) {
*dynrel++ = {P, R_RISCV_64, (u32)sym.get_dynsym_idx(ctx), A};
*(u64 *)loc = A;
*(ul64 *)loc = A;
} else {
if (!is_relr_reloc(ctx, rel))
*dynrel++ = {P, R_RISCV_RELATIVE, 0, (i64)(S + A)};
*(u64 *)loc = S + A;
*(ul64 *)loc = S + A;
}
break;
case R_RISCV_TLS_DTPMOD32:
@@ -256,68 +246,68 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
Error(ctx) << *this << ": unsupported relocation: " << rel;
break;
case R_RISCV_BRANCH:
write_btype((u32 *)loc, S + A - P);
write_btype(loc, S + A - P);
break;
case R_RISCV_JAL:
write_jtype((u32 *)loc, S + A - P);
write_jtype(loc, S + A - P);
break;
case R_RISCV_CALL:
case R_RISCV_CALL_PLT: {
if (r_deltas[i + 1] - r_deltas[i] != 0) {
if (extra.r_deltas[i + 1] - extra.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);
assert(extra.r_deltas[i + 1] - extra.r_deltas[i] == -4);
u32 jalr = *(ul32 *)&contents[rels[i].r_offset + 4];
*(ul32 *)loc = (0b11111'000000 & jalr) | 0b101111;
write_jtype(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);
write_utype(loc, val);
write_itype(loc + 4, val);
}
break;
}
case R_RISCV_GOT_HI20:
*(u32 *)loc = G + GOT + A - P;
*(ul32 *)loc = G + GOT + A - P;
break;
case R_RISCV_TLS_GOT_HI20:
*(u32 *)loc = sym.get_gottp_addr(ctx) + A - P;
*(ul32 *)loc = sym.get_gottp_addr(ctx) + A - P;
break;
case R_RISCV_TLS_GD_HI20:
*(u32 *)loc = sym.get_tlsgd_addr(ctx) + A - P;
*(ul32 *)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;
*(ul32 *)loc = P;
} else {
*(u32 *)loc = S + A - P;
*(ul32 *)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));
write_itype(loc, *(ul32 *)(base + sym.value));
break;
case R_RISCV_LO12_I:
case R_RISCV_TPREL_LO12_I:
write_itype((u32 *)loc, S + A);
write_itype(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));
write_stype(loc, *(ul32 *)(base + sym.value));
break;
case R_RISCV_LO12_S:
case R_RISCV_TPREL_LO12_S:
write_stype((u32 *)loc, S + A);
write_stype(loc, S + A);
break;
case R_RISCV_HI20:
write_utype((u32 *)loc, S + A);
write_utype(loc, S + A);
break;
case R_RISCV_TPREL_HI20:
write_utype((u32 *)loc, S + A - ctx.tls_begin);
write_utype(loc, S + A - ctx.tls_begin);
break;
case R_RISCV_TPREL_ADD:
break;
@@ -325,31 +315,31 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
loc += S + A;
break;
case R_RISCV_ADD16:
*(u16 *)loc += S + A;
*(ul16 *)loc += S + A;
break;
case R_RISCV_ADD32:
*(u32 *)loc += S + A;
*(ul32 *)loc += S + A;
break;
case R_RISCV_ADD64:
*(u64 *)loc += S + A;
*(ul64 *)loc += S + A;
break;
case R_RISCV_SUB8:
loc -= S + A;
break;
case R_RISCV_SUB16:
*(u16 *)loc -= S + A;
*(ul16 *)loc -= S + A;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= S + A;
*(ul32 *)loc -= S + A;
break;
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
*(ul64 *)loc -= S + A;
break;
case R_RISCV_RVC_BRANCH:
write_cbtype((u16 *)loc, S + A - P);
write_cbtype(loc, S + A - P);
break;
case R_RISCV_RVC_JUMP:
write_cjtype((u16 *)loc, S + A - P);
write_cjtype(loc, S + A - P);
break;
case R_RISCV_RVC_LUI:
Error(ctx) << *this << ": unsupported relocation: " << rel;
@@ -364,13 +354,13 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
*loc = S + A;
break;
case R_RISCV_SET16:
*(u16 *)loc = S + A;
*(ul16 *)loc = S + A;
break;
case R_RISCV_SET32:
*(u32 *)loc = S + A;
*(ul32 *)loc = S + A;
break;
case R_RISCV_32_PCREL:
*(u32 *)loc = S + A - P;
*(ul32 *)loc = S + A - P;
break;
default:
Error(ctx) << *this << ": unknown relocation: " << rel;
@@ -393,9 +383,9 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
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];
u8 *loc = base + rels[i].r_offset + extra.r_deltas[i];
u32 val = *(ul32 *)loc;
*(ul32 *)loc = *(ul32 *)&contents[rels[i].r_offset];
write_utype(loc, val);
}
}
@@ -404,7 +394,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -415,7 +405,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -424,41 +414,44 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::tie(frag, addend) = get_fragment(ctx, rel);
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
#define A (frag ? (u64)addend : (u64)rel.r_addend)
switch (rel.r_type) {
case R_RISCV_32:
*(u32 *)loc = S + A;
*(ul32 *)loc = S + A;
break;
case R_RISCV_64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul64 *)loc = *val;
break;
}
}
*(ul64 *)loc = S + A;
break;
case R_RISCV_ADD8:
*loc += S + A;
break;
case R_RISCV_ADD16:
*(u16 *)loc += S + A;
*(ul16 *)loc += S + A;
break;
case R_RISCV_ADD32:
*(u32 *)loc += S + A;
*(ul32 *)loc += S + A;
break;
case R_RISCV_ADD64:
*(u64 *)loc += S + A;
*(ul64 *)loc += S + A;
break;
case R_RISCV_SUB8:
*loc -= S + A;
break;
case R_RISCV_SUB16:
*(u16 *)loc -= S + A;
*(ul16 *)loc -= S + A;
break;
case R_RISCV_SUB32:
*(u32 *)loc -= S + A;
*(ul32 *)loc -= S + A;
break;
case R_RISCV_SUB64:
*(u64 *)loc -= S + A;
*(ul64 *)loc -= S + A;
break;
case R_RISCV_SUB6:
*loc = (*loc & 0b1100'0000) | ((*loc - (S + A)) & 0b0011'1111);
@@ -470,10 +463,10 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
*loc = S + A;
break;
case R_RISCV_SET16:
*(u16 *)loc = S + A;
*(ul16 *)loc = S + A;
break;
case R_RISCV_SET32:
*(u32 *)loc = S + A;
*(ul32 *)loc = S + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
@@ -490,8 +483,8 @@ 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);
if (compressed)
uncompress_to(ctx, buf);
else
memcpy(buf, contents.data(), contents.size());
return;
@@ -499,12 +492,11 @@ void InputSection<E>::copy_contents_riscv(Context<E> &ctx, u8 *buf) {
// 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();
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 pos = 0;
for (i64 i = 0; i < rels.size(); i++) {
i64 delta = r_deltas[i + 1] - r_deltas[i];
i64 delta = extra.r_deltas[i + 1] - extra.r_deltas[i];
if (delta == 0)
continue;
assert(delta < 0);
@@ -523,7 +515,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -534,7 +526,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
Symbol<E> &sym = *file.symbols[rel.r_sym];
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -643,23 +635,18 @@ 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) {
// Initializes sorted_symbols.
static void sort_symbols(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);
isec->extra.sorted_symbols.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; });
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (isec)
sort(isec->extra.sorted_symbols,
[](Symbol<E> *a, Symbol<E> *b) { return a->value < b->value; });
});
}
@@ -686,17 +673,17 @@ static i64 compute_distance(Context<E> &ctx, Symbol<E> &sym,
// 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();
std::span<Symbol<E> *> syms = isec.extra.sorted_symbols;
i64 delta = 0;
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
isec.extra.r_deltas.resize(rels.size() + 1);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &r = rels[i];
i64 delta2 = 0;
r_deltas[i] += delta;
isec.extra.r_deltas[i] += delta;
switch (r.r_type) {
case R_RISCV_ALIGN: {
@@ -709,10 +696,7 @@ static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
// 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);
u64 alignment = bit_ceil(r.r_addend);
if (next_loc % alignment)
delta2 = align_to(loc, alignment) - next_loc;
break;
@@ -745,7 +729,7 @@ static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
for (Symbol<E> *sym : syms)
sym->value += delta;
r_deltas[rels.size()] += delta;
isec.extra.r_deltas[rels.size()] += delta;
isec.sh_size += delta;
}
@@ -795,7 +779,7 @@ static void relax_section(Context<E> &ctx, InputSection<E> &isec) {
// relocations.
i64 riscv_resize_sections(Context<E> &ctx) {
Timer t(ctx, "riscv_resize_sections");
initialize_storage(ctx);
sort_symbols(ctx);
// Find R_RISCV_CALL AND R_RISCV_CALL_PLT that can be relaxed.
// This step should only shrink sections.
+45 -42
View File
@@ -18,7 +18,7 @@ static void write_compact_plt(Context<E> &ctx) {
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;
*(ul32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
}
}
@@ -50,8 +50,8 @@ static void write_ibtplt(Context<E> &ctx) {
};
memcpy(buf, plt0, sizeof(plt0));
*(u32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 4;
*(u32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 2;
*(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 4;
*(ul32 *)(buf + 14) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr - 2;
// Write PLT entries
i64 relplt_idx = 0;
@@ -65,8 +65,8 @@ static void write_ibtplt(Context<E> &ctx) {
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;
*(ul32 *)(ent + 6) = relplt_idx++;
*(ul32 *)(ent + 12) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 16;
}
}
@@ -82,8 +82,8 @@ static void write_plt(Context<E> &ctx) {
};
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;
*(ul32 *)(buf + 2) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 2;
*(ul32 *)(buf + 8) = ctx.gotplt->shdr.sh_addr - ctx.plt->shdr.sh_addr + 4;
// Write PLT entries
i64 relplt_idx = 0;
@@ -97,9 +97,9 @@ static void write_plt(Context<E> &ctx) {
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;
*(ul32 *)(ent + 2) = sym->get_gotplt_addr(ctx) - sym->get_plt_addr(ctx) - 6;
*(ul32 *)(ent + 7) = relplt_idx++;
*(ul32 *)(ent + 12) = ctx.plt->shdr.sh_addr - sym->get_plt_addr(ctx) - 16;
}
}
@@ -123,14 +123,14 @@ void PltGotSection<E>::copy_buf(Context<E> &ctx) {
};
for (Symbol<E> *sym : symbols) {
u8 *ent = buf + sym->get_pltgot_idx(ctx) * X86_64::pltgot_size;
u8 *ent = buf + sym->get_pltgot_idx(ctx) * E::pltgot_size;
memcpy(ent, data, sizeof(data));
*(u32 *)(ent + 2) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 6;
*(ul32 *)(ent + 2) = sym->get_got_addr(ctx) - sym->get_plt_addr(ctx) - 6;
}
}
template <>
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
void EhFrameSection<E>::apply_reloc(Context<E> &ctx, const ElfRel<E> &rel,
u64 offset, u64 val) {
u8 *loc = ctx.buf + this->shdr.sh_offset + offset;
@@ -138,16 +138,16 @@ void EhFrameSection<E>::apply_reloc(Context<E> &ctx, ElfRel<E> &rel,
case R_X86_64_NONE:
return;
case R_X86_64_32:
*(u32 *)loc = val;
*(ul32 *)loc = val;
return;
case R_X86_64_64:
*(u64 *)loc = val;
*(ul64 *)loc = val;
return;
case R_X86_64_PC32:
*(u32 *)loc = val - this->shdr.sh_addr - offset;
*(ul32 *)loc = val - this->shdr.sh_addr - offset;
return;
case R_X86_64_PC64:
*(u64 *)loc = val - this->shdr.sh_addr - offset;
*(ul64 *)loc = val - this->shdr.sh_addr - offset;
return;
}
unreachable();
@@ -233,7 +233,7 @@ static u32 relax_gotpc32_tlsdesc(u8 *loc) {
template <>
void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
ElfRel<E> *dynrel = nullptr;
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
i64 frag_idx = 0;
if (ctx.reldyn)
@@ -271,30 +271,30 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write16s = [&](u64 val) {
overflow_check(val, -(1 << 15), 1 << 15);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write32 = [&](u64 val) {
overflow_check(val, 0, (i64)1 << 32);
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
auto write32s = [&](u64 val) {
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
auto write64 = [&](u64 val) {
*(u64 *)loc = val;
*(ul64 *)loc = val;
};
#define S (frag_ref ? frag_ref->frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag_ref ? frag_ref->addend : rel.r_addend)
#define A (frag_ref ? (u64)frag_ref->addend : (u64)rel.r_addend)
#define P (output_section->shdr.sh_addr + offset + rel.r_offset)
#define G (sym.get_got_addr(ctx) - ctx.gotplt->shdr.sh_addr)
#define GOT ctx.gotplt->shdr.sh_addr
@@ -404,7 +404,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x48, 0x8d, 0x80, 0, 0, 0, 0, // lea 0(%rax), %rax
};
memcpy(loc - 4, insn, sizeof(insn));
*(u32 *)(loc + 8) = val;
*(ul32 *)(loc + 8) = val;
break;
}
case R_X86_64_PLTOFF64: {
@@ -414,7 +414,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
0x66, 0x0f, 0x1f, 0x44, 0x00, 0x00, // nop
};
memcpy(loc - 3, insn, sizeof(insn));
*(u32 *)(loc + 9) = val;
*(ul32 *)(loc + 9) = val;
break;
}
default:
@@ -545,7 +545,7 @@ void InputSection<E>::apply_reloc_alloc(Context<E> &ctx, u8 *base) {
// scan_relocations.
template <>
void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
for (i64 i = 0; i < rels.size(); i++) {
const ElfRel<E> &rel = rels[i];
@@ -556,7 +556,7 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
u8 *loc = base + rel.r_offset;
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
@@ -578,21 +578,21 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
auto write16 = [&](u64 val) {
overflow_check(val, 0, 1 << 16);
*(u16 *)loc = val;
*(ul16 *)loc = val;
};
auto write32 = [&](u64 val) {
overflow_check(val, 0, (i64)1 << 32);
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
auto write32s = [&](u64 val) {
overflow_check(val, -((i64)1 << 31), (i64)1 << 31);
*(u32 *)loc = val;
*(ul32 *)loc = val;
};
#define S (frag ? frag->get_addr(ctx) : sym.get_addr(ctx))
#define A (frag ? addend : rel.r_addend)
#define A (frag ? (u64)addend : (u64)rel.r_addend)
switch (rel.r_type) {
case R_X86_64_8:
@@ -608,28 +608,31 @@ void InputSection<E>::apply_reloc_nonalloc(Context<E> &ctx, u8 *base) {
write32s(S + A);
break;
case R_X86_64_64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
else
*(u64 *)loc = S + A;
if (!frag) {
if (std::optional<u64> val = get_tombstone(sym)) {
*(ul64 *)loc = *val;
break;
}
}
*(ul64 *)loc = S + A;
break;
case R_X86_64_DTPOFF32:
if (std::optional<u64> val = get_tombstone(sym))
*(u32 *)loc = *val;
*(ul32 *)loc = *val;
else
write32s(S + A - ctx.tls_begin);
break;
case R_X86_64_DTPOFF64:
if (std::optional<u64> val = get_tombstone(sym))
*(u64 *)loc = *val;
*(ul64 *)loc = *val;
else
*(u64 *)loc = S + A - ctx.tls_begin;
*(ul64 *)loc = S + A - ctx.tls_begin;
break;
case R_X86_64_SIZE32:
write32(sym.esym().st_size + A);
break;
case R_X86_64_SIZE64:
*(u64 *)loc = sym.esym().st_size + A;
*(ul64 *)loc = sym.esym().st_size + A;
break;
default:
Fatal(ctx) << *this << ": invalid relocation for non-allocated sections: "
@@ -652,7 +655,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
assert(shdr().sh_flags & SHF_ALLOC);
this->reldyn_offset = file.num_dynrel * sizeof(ElfRel<E>);
std::span<ElfRel<E>> rels = get_rels(ctx);
std::span<const ElfRel<E>> rels = get_rels(ctx);
// Scan relocations
for (i64 i = 0; i < rels.size(); i++) {
@@ -664,7 +667,7 @@ void InputSection<E>::scan_relocations(Context<E> &ctx) {
u8 *loc = (u8 *)(contents.data() + rel.r_offset);
if (!sym.file) {
report_undef(ctx, file, sym);
record_undef_error(ctx, rel);
continue;
}
+375 -407
View File
File diff suppressed because it is too large. Load diff
+510
View File
@@ -0,0 +1,510 @@
#include "mold.h"
namespace mold::elf {
// The hash function for .gdb_index.
static u32 gdb_hash(std::string_view name) {
u32 h = 0;
for (u8 c : name) {
if ('A' <= c && c <= 'Z')
c = 'a' + c - 'A';
h = h * 67 + c - 113;
}
return h;
}
// Split .debug_info into so-called "compilation units". A .debug_info
// section usually contains one compunit unless it was created by `ld -r`.
// This is for --gdb-index.
template <typename E>
std::vector<std::string_view>
read_compunits(Context<E> &ctx, ObjectFile<E> &file) {
file.debug_info->uncompress(ctx);
std::string_view data = file.debug_info->contents;
std::vector<std::string_view> vec;
while (!data.empty()) {
if (data.size() < 4)
Fatal(ctx) << *file.debug_info << ": corrupted .debug_info";
if (*(ul32 *)data.data() == 0xffffffff)
Fatal(ctx) << *file.debug_info << ": --gdb-index: DWARF64 not supported";
i64 len = *(ul32 *)data.data() + 4;
vec.push_back(data.substr(0, len));
data = data.substr(len);
}
return vec;
}
// Parses .debug_gnu_pubnames and .debug_gnu_pubtypes. These sections
// start with a 14 bytes header followed by (4-byte offset, 1-byte type,
// null-terminated string) tuples.
//
// The 4-byte offset is an offset into .debug_info that contains details
// about the name. The 1-byte type is a type of the corresponding name
// (e.g. function, variable or datatype). The string is a name of a
// function, a variable or a type.
template <typename E>
std::vector<GdbIndexName> read_pubnames(Context<E> &ctx, ObjectFile<E> &file) {
std::vector<GdbIndexName> vec;
auto get_cu_idx = [&](InputSection<E> &isec, i64 offset) {
i64 off = 0;
for (i64 i = 0; i < file.compunits.size(); i++) {
if (offset == off)
return file.compunits_idx + i;
off += file.compunits[i].size();
}
Fatal(ctx) << isec << ": corrupted debug_info_offset";
};
auto read = [&](InputSection<E> &isec) {
isec.uncompress(ctx);
std::string_view contents = isec.contents;
while (!contents.empty()) {
if (contents.size() < 14)
Fatal(ctx) << isec << ": corrupted header";
u32 len = *(ul32 *)contents.data() + 4;
u32 debug_info_offset = *(ul32 *)(contents.data() + 6);
u32 cu_idx = get_cu_idx(isec, debug_info_offset);
std::string_view data = contents.substr(14, len - 14);
contents = contents.substr(len);
while (!data.empty()) {
u32 offset = *(ul32 *)data.data();
data = data.substr(4);
if (offset == 0)
break;
u8 type = data[0];
data = data.substr(1);
std::string_view name = data.data();
data = data.substr(name.size() + 1);
vec.push_back({name, gdb_hash(name), (type << 24) | cu_idx});
}
}
};
if (file.debug_pubnames)
read(*file.debug_pubnames);
if (file.debug_pubtypes)
read(*file.debug_pubtypes);
// Uniquify elements because GCC 11 seems to emit one record for each
// comdat group which results in having a lot of duplicate records.
auto less = [](const GdbIndexName &a, const GdbIndexName &b) {
return std::tuple{a.hash, a.attr, a.name} <
std::tuple{b.hash, b.attr, b.name};
};
auto equal = [](const GdbIndexName &a, const GdbIndexName &b) {
return std::tuple{a.hash, a.attr, a.name} ==
std::tuple{b.hash, b.attr, b.name};
};
std::sort(vec.begin(), vec.end(), less);
vec.erase(std::unique(vec.begin(), vec.end(), equal), vec.end());
return vec;
}
template <typename E>
static u8 *get_buffer(Context<E> &ctx, Chunk<E> *chunk) {
if (u8 *buf = chunk->get_uncompressed_data())
return buf;
return ctx.buf + chunk->shdr.sh_offset;
}
// Try to find a compilation unit from .debug_info and its
// corresponding record from .debug_abbrev and returns them.
template <typename E>
static std::tuple<u8 *, u8 *, u32>
find_compunit(Context<E> &ctx, ObjectFile<E> &file, i64 offset) {
// Read .debug_info to find the record at a given offset.
u8 *cu = get_buffer(ctx, ctx.debug_info) + offset;
u32 dwarf_version = *(ul16 *)(cu + 4);
u32 abbrev_offset;
// Skip a header.
switch (dwarf_version) {
case 2:
case 3:
case 4:
abbrev_offset = *(ul32 *)(cu + 6);
if (u32 address_size = cu[10]; address_size != E::word_size)
Fatal(ctx) << file << ": --gdb-index: unsupported address size "
<< address_size;
cu += 11;
break;
case 5: {
abbrev_offset = *(ul32 *)(cu + 8);
if (u32 address_size = cu[7]; address_size != E::word_size)
Fatal(ctx) << file << ": --gdb-index: unsupported address size "
<< address_size;
switch (u32 unit_type = cu[6]; unit_type) {
case DW_UT_compile:
case DW_UT_partial:
cu += 12;
break;
case DW_UT_skeleton:
case DW_UT_split_compile:
cu += 20;
break;
default:
Fatal(ctx) << file << ": --gdb-index: unknown DW_UT_* value: 0x"
<< std::hex << unit_type;
}
break;
}
default:
Fatal(ctx) << file << ": --gdb-index: unknown DWARF version: "
<< dwarf_version;
}
u32 abbrev_code = read_uleb(cu);
// Find a .debug_abbrev record corresponding to the .debug_info record.
// We assume the .debug_info record at a given offset is of
// DW_TAG_compile_unit which describes a compunit.
u8 *abbrev = get_buffer(ctx, ctx.debug_abbrev) + abbrev_offset;
for (;;) {
u32 code = read_uleb(abbrev);
if (code == 0)
Fatal(ctx) << file << ": --gdb-index: .debug_abbrev does not contain"
<< " a record for the first .debug_info record";
if (code == abbrev_code) {
// Found a record
u64 abbrev_tag = read_uleb(abbrev);
if (abbrev_tag != DW_TAG_compile_unit && abbrev_tag != DW_TAG_skeleton_unit)
Fatal(ctx) << file << ": --gdb-index: the first entry's tag is not"
<< " DW_TAG_compile_unit/DW_TAG_skeleton_unit but 0x"
<< std::hex << abbrev_tag;
break;
}
// Skip an uninteresting record
read_uleb(abbrev); // tag
abbrev++; // has_children byte
for (;;) {
u64 name = read_uleb(abbrev);
u64 form = read_uleb(abbrev);
if (name == 0 && form == 0)
break;
if (form == DW_FORM_implicit_const)
read_uleb(abbrev);
}
}
abbrev++; // skip has_children byte
return {cu, abbrev, dwarf_version};
}
// Estimate the number of address ranges contained in a given file.
// It may over-estimate but never under-estimate.
template <typename E>
i64 estimate_address_areas(Context<E> &ctx, ObjectFile<E> &file) {
// Each CU contains zero or one address area.
i64 ret = file.compunits.size();
// In DWARF 4, a CU can refer address ranges in .debug_ranges.
// .debug_ranges contains a vector of [begin, end) address pairs.
// The last entry must be a null terminator, so we do -1.
if (file.debug_ranges)
ret += file.debug_ranges->sh_size / E::word_size / 2 - 1;
// In DWARF 5, a CU can refer address ranges in .debug_rnglists, which
// contains variable-length entries. The smallest possible range entry
// is one byte for the code and two ULEB128 values (each can be as
// small as one byte), so 3 bytes.
if (file.debug_rnglists)
ret += file.debug_rnglists->sh_size / 3;
return ret;
}
// .debug_info contains variable-length fields. This class reads them.
template <typename E>
class DebugInfoReader {
public:
DebugInfoReader(Context<E> &ctx, ObjectFile<E> &file, u8 *cu)
: ctx(ctx), file(file), cu(cu) {}
u64 read(u64 form);
Context<E> &ctx;
ObjectFile<E> &file;
u8 *cu;
};
// Read value of the given DW_FORM_* form. If a value is not scalar,
// returns a dummy value 0.
template <typename E>
inline u64 DebugInfoReader<E>::read(u64 form) {
switch (form) {
case DW_FORM_flag_present:
return 0;
case DW_FORM_data1:
case DW_FORM_flag:
case DW_FORM_strx1:
case DW_FORM_addrx1:
case DW_FORM_ref1:
return *cu++;
case DW_FORM_data2:
case DW_FORM_strx2:
case DW_FORM_addrx2:
case DW_FORM_ref2: {
u64 val = *(ul16 *)cu;
cu += 2;
return val;
}
case DW_FORM_strx3:
case DW_FORM_addrx3: {
u64 val = *(ul24 *)cu;
cu += 3;
return val;
}
case DW_FORM_data4:
case DW_FORM_strp:
case DW_FORM_sec_offset:
case DW_FORM_line_strp:
case DW_FORM_strx4:
case DW_FORM_addrx4:
case DW_FORM_ref4: {
u64 val = *(ul32 *)cu;
cu += 4;
return val;
}
case DW_FORM_data8:
case DW_FORM_ref8: {
u64 val = *(ul64 *)cu;
cu += 8;
return val;
}
case DW_FORM_addr:
case DW_FORM_ref_addr: {
u64 val = *(typename E::WordTy *)cu;
cu += E::word_size;
return val;
}
case DW_FORM_strx:
case DW_FORM_addrx:
case DW_FORM_udata:
case DW_FORM_ref_udata:
case DW_FORM_loclistx:
case DW_FORM_rnglistx:
return read_uleb(cu);
case DW_FORM_string:
cu += strlen((char *)cu) + 1;
return 0;
default:
Fatal(ctx) << file << ": --gdb-index: unhandled debug info form: 0x"
<< std::hex << form;
return 0;
}
}
// Read a range list from .debug_ranges starting at the given offset
// (until an end of list entry).
template <typename E>
static std::vector<u64>
read_debug_range(Context<E> &ctx, ObjectFile<E> &file, typename E::WordTy *range) {
std::vector<u64> vec;
u64 base = 0;
for (i64 i = 0; range[i] || range[i + 1]; i += 2) {
if (range[i] + 1 == 0) {
// base address selection entry
base = range[i + 1];
} else {
vec.push_back(range[i] + base);
vec.push_back(range[i + 1] + base);
}
}
return vec;
}
// Read a range list from .debug_rnglists starting at the given offset
// (until an end of list entry).
template <typename E>
static std::vector<u64>
read_rnglist_range(Context<E> &ctx, ObjectFile<E> &file, u8 *rnglist,
typename E::WordTy *addrx) {
std::vector<u64> vec;
u64 base = 0;
for (;;) {
switch (*rnglist++) {
case DW_RLE_end_of_list:
return vec;
case DW_RLE_base_addressx:
base = addrx[read_uleb(rnglist)];
break;
case DW_RLE_startx_endx:
vec.push_back(addrx[read_uleb(rnglist)]);
vec.push_back(addrx[read_uleb(rnglist)]);
break;
case DW_RLE_startx_length:
vec.push_back(addrx[read_uleb(rnglist)]);
vec.push_back(vec.back() + read_uleb(rnglist));
break;
case DW_RLE_offset_pair:
vec.push_back(base + read_uleb(rnglist));
vec.push_back(base + read_uleb(rnglist));
break;
case DW_RLE_base_address:
base = *(typename E::WordTy *)rnglist;
rnglist += E::word_size;
break;
case DW_RLE_start_end:
vec.push_back(*(typename E::WordTy *)rnglist);
rnglist += E::word_size;
vec.push_back(*(typename E::WordTy *)rnglist);
rnglist += E::word_size;
break;
case DW_RLE_start_length:
vec.push_back(*(typename E::WordTy *)rnglist);
rnglist += E::word_size;
vec.push_back(vec.back() + read_uleb(rnglist));
break;
}
}
}
// Returns a list of address ranges explained by a compunit at the
// `offset` in an output .debug_info section.
//
// .debug_info contains DWARF debug info records, so this function
// parses DWARF. If a designated compunit contains multiple ranges, the
// ranges are read from .debug_ranges (or .debug_rnglists for DWARF5).
// Otherwise, a range is read directly from .debug_info (or possibly
// from .debug_addr for DWARF5).
template <typename E>
std::vector<u64>
read_address_areas(Context<E> &ctx, ObjectFile<E> &file, i64 offset) {
u8 *cu;
u8 *abbrev;
u32 dwarf_version;
std::tie(cu, abbrev, dwarf_version) = find_compunit(ctx, file, offset);
DebugInfoReader<E> reader{ctx, file, cu};
struct Record {
u64 form = 0;
u64 value = 0;
};
Record low_pc;
Record high_pc;
Record ranges;
std::optional<u64> rnglists_base;
typename E::WordTy *addrx = nullptr;
// Read all interesting debug records.
for (;;) {
u64 name = read_uleb(abbrev);
u64 form = read_uleb(abbrev);
if (name == 0 && form == 0)
break;
u64 val = reader.read(form);
switch (name) {
case DW_AT_low_pc:
low_pc = {form, val};
break;
case DW_AT_high_pc:
high_pc = {form, val};
break;
case DW_AT_rnglists_base:
rnglists_base = val;
break;
case DW_AT_addr_base:
addrx = (typename E::WordTy *)(get_buffer(ctx, ctx.debug_addr) + val);
break;
case DW_AT_ranges:
ranges = {form, val};
break;
}
}
// Handle non-contiguous address ranges.
if (ranges.form) {
if (dwarf_version <= 4) {
typename E::WordTy *range_begin =
(typename E::WordTy *)(get_buffer(ctx, ctx.debug_ranges) + ranges.value);
return read_debug_range(ctx, file, range_begin);
}
assert(dwarf_version == 5);
u8 *buf = get_buffer(ctx, ctx.debug_rnglists);
if (ranges.form == DW_FORM_sec_offset)
return read_rnglist_range(ctx, file, buf + ranges.value, addrx);
if (!rnglists_base)
Fatal(ctx) << file << ": --gdb-index: missing DW_AT_rnglists_base";
u8 *base = buf + *rnglists_base;
return read_rnglist_range(ctx, file, base + *(ul32 *)base, addrx);
}
// Handle a contiguous address range.
if (low_pc.form && high_pc.form) {
u64 lo;
switch (low_pc.form) {
case DW_FORM_addr:
lo = low_pc.value;
break;
case DW_FORM_addrx:
case DW_FORM_addrx1:
case DW_FORM_addrx2:
case DW_FORM_addrx4:
lo = addrx[low_pc.value];
break;
default:
Fatal(ctx) << file << ": --gdb-index: unhandled form for DW_AT_low_pc: 0x"
<< std::hex << high_pc.form;
}
switch (high_pc.form) {
case DW_FORM_addr:
return {lo, high_pc.value};
case DW_FORM_addrx:
case DW_FORM_addrx1:
case DW_FORM_addrx2:
case DW_FORM_addrx4:
return {lo, addrx[high_pc.value]};
case DW_FORM_udata:
case DW_FORM_data1:
case DW_FORM_data2:
case DW_FORM_data4:
case DW_FORM_data8:
return {lo, lo + high_pc.value};
default:
Fatal(ctx) << file << ": --gdb-index: unhandled form for DW_AT_high_pc: 0x"
<< std::hex << high_pc.form;
}
}
return {};
}
#define INSTANTIATE(E) \
template std::vector<std::string_view> \
read_compunits(Context<E> &, ObjectFile<E> &); \
template std::vector<GdbIndexName> \
read_pubnames(Context<E> &, ObjectFile<E> &); \
template i64 \
estimate_address_areas(Context<E> &, ObjectFile<E> &); \
template std::vector<u64> \
read_address_areas(Context<E> &, ObjectFile<E> &, i64)
INSTANTIATE_ALL;
} // namespace mold::elf
+163 -127
View File
@@ -1,5 +1,7 @@
#pragma once
#include "../inttypes.h"
#include <cstdint>
#include <ostream>
#include <string>
@@ -62,6 +64,7 @@ static constexpr u32 SHT_PREINIT_ARRAY = 16;
static constexpr u32 SHT_GROUP = 17;
static constexpr u32 SHT_SYMTAB_SHNDX = 18;
static constexpr u32 SHT_RELR = 19;
static constexpr u32 SHT_LLVM_ADDRSIG = 0x6fff4c03;
static constexpr u32 SHT_GNU_HASH = 0x6ffffff6;
static constexpr u32 SHT_GNU_VERDEF = 0x6ffffffd;
static constexpr u32 SHT_GNU_VERNEED = 0x6ffffffe;
@@ -80,6 +83,7 @@ static constexpr u32 SHF_LINK_ORDER = 0x80;
static constexpr u32 SHF_GROUP = 0x200;
static constexpr u32 SHF_TLS = 0x400;
static constexpr u32 SHF_COMPRESSED = 0x800;
static constexpr u32 SHF_GNU_RETAIN = 0x200000;
static constexpr u32 SHF_EXCLUDE = 0x80000000;
static constexpr u32 GRP_COMDAT = 1;
@@ -225,6 +229,7 @@ static constexpr u32 NT_GNU_HWCAP = 2;
static constexpr u32 NT_GNU_BUILD_ID = 3;
static constexpr u32 NT_GNU_GOLD_VERSION = 4;
static constexpr u32 NT_GNU_PROPERTY_TYPE_0 = 5;
static constexpr u32 NT_FDO_PACKAGING_METADATA = 0xcafe1a7e;
static constexpr u32 GNU_PROPERTY_AARCH64_FEATURE_1_AND = 0xc0000000;
static constexpr u32 GNU_PROPERTY_X86_FEATURE_1_AND = 0xc0000002;
@@ -1054,8 +1059,16 @@ static constexpr u32 DW_AT_low_pc = 0x11;
static constexpr u32 DW_AT_high_pc = 0x12;
static constexpr u32 DW_AT_producer = 0x25;
static constexpr u32 DW_AT_ranges = 0x55;
static constexpr u32 DW_AT_addr_base = 0x73;
static constexpr u32 DW_AT_rnglists_base = 0x74;
static constexpr u32 DW_TAG_compile_unit = 0x11;
static constexpr u32 DW_TAG_skeleton_unit = 0x4a;
static constexpr u32 DW_UT_compile = 0x01;
static constexpr u32 DW_UT_partial = 0x03;
static constexpr u32 DW_UT_skeleton = 0x04;
static constexpr u32 DW_UT_split_compile = 0x05;
static constexpr u32 DW_FORM_addr = 0x01;
static constexpr u32 DW_FORM_block2 = 0x03;
@@ -1081,6 +1094,34 @@ static constexpr u32 DW_FORM_indirect = 0x16;
static constexpr u32 DW_FORM_sec_offset = 0x17;
static constexpr u32 DW_FORM_exprloc = 0x18;
static constexpr u32 DW_FORM_flag_present = 0x19;
static constexpr u32 DW_FORM_strx = 0x1a;
static constexpr u32 DW_FORM_addrx = 0x1b;
static constexpr u32 DW_FORM_ref_sup4 = 0x1c;
static constexpr u32 DW_FORM_strp_sup = 0x1d;
static constexpr u32 DW_FORM_data16 = 0x1e;
static constexpr u32 DW_FORM_line_strp = 0x1f;
static constexpr u32 DW_FORM_ref_sig8 = 0x20;
static constexpr u32 DW_FORM_implicit_const = 0x21;
static constexpr u32 DW_FORM_loclistx = 0x22;
static constexpr u32 DW_FORM_rnglistx = 0x23;
static constexpr u32 DW_FORM_ref_sup8 = 0x24;
static constexpr u32 DW_FORM_strx1 = 0x25;
static constexpr u32 DW_FORM_strx2 = 0x26;
static constexpr u32 DW_FORM_strx3 = 0x27;
static constexpr u32 DW_FORM_strx4 = 0x28;
static constexpr u32 DW_FORM_addrx1 = 0x29;
static constexpr u32 DW_FORM_addrx2 = 0x2a;
static constexpr u32 DW_FORM_addrx3 = 0x2b;
static constexpr u32 DW_FORM_addrx4 = 0x2c;
static constexpr u32 DW_RLE_end_of_list = 0x00;
static constexpr u32 DW_RLE_base_addressx = 0x01;
static constexpr u32 DW_RLE_startx_endx = 0x02;
static constexpr u32 DW_RLE_startx_length = 0x03;
static constexpr u32 DW_RLE_offset_pair = 0x04;
static constexpr u32 DW_RLE_base_address = 0x05;
static constexpr u32 DW_RLE_start_end = 0x06;
static constexpr u32 DW_RLE_start_length = 0x07;
struct Elf64Sym {
bool is_defined() const { return !is_undef(); }
@@ -1097,13 +1138,13 @@ struct Elf64Sym {
return st_shndx == SHN_UNDEF && st_bind == STB_WEAK;
}
u32 st_name;
ul32 st_name;
u8 st_type : 4;
u8 st_bind : 4;
u8 st_visibility : 2;
u16 st_shndx;
u64 st_value;
u64 st_size;
ul16 st_shndx;
ul64 st_value;
ul64 st_size;
};
struct Elf32Sym {
@@ -1121,185 +1162,185 @@ struct Elf32Sym {
return st_shndx == SHN_UNDEF && st_bind == STB_WEAK;
}
u32 st_name;
u32 st_value;
u32 st_size;
ul32 st_name;
ul32 st_value;
ul32 st_size;
u8 st_type : 4;
u8 st_bind : 4;
u8 st_visibility : 2;
u16 st_shndx;
ul16 st_shndx;
};
struct Elf64Shdr {
u32 sh_name;
u32 sh_type;
u64 sh_flags;
u64 sh_addr;
u64 sh_offset;
u64 sh_size;
u32 sh_link;
u32 sh_info;
u64 sh_addralign;
u64 sh_entsize;
ul32 sh_name;
ul32 sh_type;
ul64 sh_flags;
ul64 sh_addr;
ul64 sh_offset;
ul64 sh_size;
ul32 sh_link;
ul32 sh_info;
ul64 sh_addralign;
ul64 sh_entsize;
};
struct Elf32Shdr {
u32 sh_name;
u32 sh_type;
u32 sh_flags;
u32 sh_addr;
u32 sh_offset;
u32 sh_size;
u32 sh_link;
u32 sh_info;
u32 sh_addralign;
u32 sh_entsize;
ul32 sh_name;
ul32 sh_type;
ul32 sh_flags;
ul32 sh_addr;
ul32 sh_offset;
ul32 sh_size;
ul32 sh_link;
ul32 sh_info;
ul32 sh_addralign;
ul32 sh_entsize;
};
struct Elf64Ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u64 e_entry;
u64 e_phoff;
u64 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
ul16 e_type;
ul16 e_machine;
ul32 e_version;
ul64 e_entry;
ul64 e_phoff;
ul64 e_shoff;
ul32 e_flags;
ul16 e_ehsize;
ul16 e_phentsize;
ul16 e_phnum;
ul16 e_shentsize;
ul16 e_shnum;
ul16 e_shstrndx;
};
struct Elf32Ehdr {
u8 e_ident[16];
u16 e_type;
u16 e_machine;
u32 e_version;
u32 e_entry;
u32 e_phoff;
u32 e_shoff;
u32 e_flags;
u16 e_ehsize;
u16 e_phentsize;
u16 e_phnum;
u16 e_shentsize;
u16 e_shnum;
u16 e_shstrndx;
ul16 e_type;
ul16 e_machine;
ul32 e_version;
ul32 e_entry;
ul32 e_phoff;
ul32 e_shoff;
ul32 e_flags;
ul16 e_ehsize;
ul16 e_phentsize;
ul16 e_phnum;
ul16 e_shentsize;
ul16 e_shnum;
ul16 e_shstrndx;
};
struct Elf64Phdr {
u32 p_type;
u32 p_flags;
u64 p_offset;
u64 p_vaddr;
u64 p_paddr;
u64 p_filesz;
u64 p_memsz;
u64 p_align;
ul32 p_type;
ul32 p_flags;
ul64 p_offset;
ul64 p_vaddr;
ul64 p_paddr;
ul64 p_filesz;
ul64 p_memsz;
ul64 p_align;
};
struct Elf32Phdr {
u32 p_type;
u32 p_offset;
u32 p_vaddr;
u32 p_paddr;
u32 p_filesz;
u32 p_memsz;
u32 p_flags;
u32 p_align;
ul32 p_type;
ul32 p_offset;
ul32 p_vaddr;
ul32 p_paddr;
ul32 p_filesz;
ul32 p_memsz;
ul32 p_flags;
ul32 p_align;
};
struct Elf64Rel {
u64 r_offset;
u32 r_type;
u32 r_sym;
ul64 r_offset;
ul32 r_type;
ul32 r_sym;
};
struct Elf32Rel {
u32 r_offset;
u32 r_type : 8;
u32 r_sym : 24;
ul32 r_offset;
u8 r_type;
ul24 r_sym;
};
struct Elf64Rela {
u64 r_offset;
u32 r_type;
u32 r_sym;
i64 r_addend;
ul64 r_offset;
ul32 r_type;
ul32 r_sym;
il64 r_addend;
};
struct Elf32Rela {
u32 r_offset;
u32 r_type : 8;
u32 r_sym : 24;
i32 r_addend;
ul32 r_offset;
u8 r_type;
ul24 r_sym;
il32 r_addend;
};
struct Elf64Dyn {
u64 d_tag;
u64 d_val;
ul64 d_tag;
ul64 d_val;
};
struct Elf32Dyn {
u32 d_tag;
u32 d_val;
ul32 d_tag;
ul32 d_val;
};
struct ElfVerneed {
u16 vn_version;
u16 vn_cnt;
u32 vn_file;
u32 vn_aux;
u32 vn_next;
ul16 vn_version;
ul16 vn_cnt;
ul32 vn_file;
ul32 vn_aux;
ul32 vn_next;
};
struct ElfVernaux {
u32 vna_hash;
u16 vna_flags;
u16 vna_other;
u32 vna_name;
u32 vna_next;
ul32 vna_hash;
ul16 vna_flags;
ul16 vna_other;
ul32 vna_name;
ul32 vna_next;
};
struct ElfVerdef {
u16 vd_version;
u16 vd_flags;
u16 vd_ndx;
u16 vd_cnt;
u32 vd_hash;
u32 vd_aux;
u32 vd_next;
ul16 vd_version;
ul16 vd_flags;
ul16 vd_ndx;
ul16 vd_cnt;
ul32 vd_hash;
ul32 vd_aux;
ul32 vd_next;
};
struct ElfVerdaux {
u32 vda_name;
u32 vda_next;
ul32 vda_name;
ul32 vda_next;
};
struct Elf64Chdr {
u32 ch_type;
u32 ch_reserved;
u64 ch_size;
u64 ch_addralign;
ul32 ch_type;
ul32 ch_reserved;
ul64 ch_size;
ul64 ch_addralign;
};
struct Elf32Chdr {
u32 ch_type;
u32 ch_size;
u32 ch_addralign;
ul32 ch_type;
ul32 ch_size;
ul32 ch_addralign;
};
struct ElfNhdr {
u32 n_namesz;
u32 n_descsz;
u32 n_type;
ul32 n_namesz;
ul32 n_descsz;
ul32 n_type;
};
struct X86_64 {
using WordTy = u64;
using WordTy = ul64;
static constexpr u32 R_NONE = R_X86_64_NONE;
static constexpr u32 R_COPY = R_X86_64_COPY;
@@ -1318,7 +1359,6 @@ struct X86_64 {
static constexpr u32 e_machine = EM_X86_64;
static constexpr u32 pltgot_size = 8;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
};
@@ -1331,7 +1371,7 @@ template <> struct ElfDyn<X86_64> : public Elf64Dyn {};
template <> struct ElfChdr<X86_64> : public Elf64Chdr {};
struct I386 {
using WordTy = u32;
using WordTy = ul32;
static constexpr u32 R_NONE = R_386_NONE;
static constexpr u32 R_COPY = R_386_COPY;
@@ -1350,7 +1390,6 @@ struct I386 {
static constexpr u32 e_machine = EM_386;
static constexpr u32 pltgot_size = 8;
static constexpr bool is_rel = true;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
};
@@ -1363,7 +1402,7 @@ template <> struct ElfDyn<I386> : public Elf32Dyn {};
template <> struct ElfChdr<I386> : public Elf32Chdr {};
struct ARM64 {
using WordTy = u64;
using WordTy = ul64;
static constexpr u32 R_NONE = R_AARCH64_NONE;
static constexpr u32 R_COPY = R_AARCH64_COPY;
@@ -1382,7 +1421,6 @@ struct ARM64 {
static constexpr u32 e_machine = EM_AARCH64;
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
};
@@ -1395,7 +1433,7 @@ template <> struct ElfDyn<ARM64> : public Elf64Dyn {};
template <> struct ElfChdr<ARM64> : public Elf64Chdr {};
struct ARM32 {
using WordTy = u32;
using WordTy = ul32;
static constexpr u32 R_NONE = R_ARM_NONE;
static constexpr u32 R_COPY = R_ARM_COPY;
@@ -1414,7 +1452,6 @@ struct ARM32 {
static constexpr u32 e_machine = EM_ARM;
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = true;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = true;
};
@@ -1427,7 +1464,7 @@ template <> struct ElfDyn<ARM32> : public Elf32Dyn {};
template <> struct ElfChdr<ARM32> : public Elf32Chdr {};
struct RISCV64 {
using WordTy = u64;
using WordTy = ul64;
static constexpr u32 R_NONE = R_RISCV_NONE;
static constexpr u32 R_COPY = R_RISCV_COPY;
@@ -1445,7 +1482,6 @@ struct RISCV64 {
static constexpr u32 e_machine = EM_RISCV;
static constexpr u32 pltgot_size = 16;
static constexpr bool is_rel = false;
static constexpr bool is_le = true;
static constexpr bool supports_tlsdesc = false;
};
+11 -17
View File
@@ -26,13 +26,13 @@ static bool is_init_fini(const InputSection<E> &isec) {
template <typename E>
static bool mark_section(InputSection<E> *isec) {
return isec && isec->is_alive && !isec->extra().is_visited.exchange(true);
return isec && isec->is_alive && !isec->is_visited.exchange(true);
}
template <typename E>
static void visit(Context<E> &ctx, InputSection<E> *isec,
tbb::feeder<InputSection<E> *> &feeder, i64 depth) {
assert(isec->extra().is_visited);
assert(isec->is_visited);
// A relocation can refer either a section fragment (i.e. a piece of
// string in a mergeable string section) or a symbol. Mark all
@@ -45,12 +45,12 @@ static void visit(Context<E> &ctx, InputSection<E> *isec,
// describing how to handle exceptions for that function.
// We want to keep associated .eh_frame records.
for (FdeRecord<E> &fde : isec->get_fdes())
for (ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
for (const ElfRel<E> &rel : fde.get_rels(isec->file).subspan(1))
if (Symbol<E> *sym = isec->file.symbols[rel.r_sym])
if (mark_section(sym->get_input_section()))
feeder.add(sym->get_input_section());
for (ElfRel<E> &rel : isec->get_rels(ctx)) {
for (const ElfRel<E> &rel : isec->get_rels(ctx)) {
Symbol<E> &sym = *isec->file.symbols[rel.r_sym];
// Symbol can refer either a section fragment or an input section.
@@ -102,11 +102,12 @@ collect_root_set(Context<E> &ctx) {
// reduce the amount of non-memory-mapped segments, you should
// use `strip` command, compile without debug info or use
// -strip-all linker option.
if (!(isec->shdr().sh_flags & SHF_ALLOC))
isec->extra().is_visited = true;
u32 flags = isec->shdr().sh_flags;
if (!(flags & SHF_ALLOC))
isec->is_visited = true;
if (is_init_fini(*isec) || is_c_identifier(isec->name()) ||
isec->shdr().sh_type == SHT_NOTE)
(flags & SHF_GNU_RETAIN) || isec->shdr().sh_type == SHT_NOTE)
enqueue_section(isec.get());
}
});
@@ -132,7 +133,7 @@ collect_root_set(Context<E> &ctx) {
// We just keep all CIEs and everything that are referenced by them.
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (CieRecord<E> &cie : file->cies)
for (ElfRel<E> &rel : cie.get_rels())
for (const ElfRel<E> &rel : cie.get_rels())
enqueue_symbol(file->symbols[rel.r_sym]);
});
@@ -159,7 +160,7 @@ static void sweep(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && !isec->extra().is_visited) {
if (isec && isec->is_alive && !isec->is_visited) {
if (ctx.arg.print_gc_sections)
SyncOut(ctx) << "removing unused section " << *isec;
isec->kill();
@@ -188,9 +189,6 @@ template <typename E>
void gc_sections(Context<E> &ctx) {
Timer t(ctx, "gc");
for (ObjectFile<E> *file : ctx.objs)
file->extras.resize(file->sections.size());
mark_nonalloc_fragments(ctx);
tbb::concurrent_vector<InputSection<E> *> rootset = collect_root_set(ctx);
@@ -201,10 +199,6 @@ void gc_sections(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void gc_sections(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+89 -46
View File
@@ -40,22 +40,35 @@
// regular trees are equal. Given this formulation, we want to find as
// many identical vertices as possible.
//
// Solving such problem is computationally intensive, but mold is quite fast.
// For Chromium, mold's ICF finishes in less than 1 second with 20 threads.
// This is contrary to lld and gold, which take about 5 and 50 seconds to
// run ICF under the same condition, respectively.
// Just like a lot of problems with graph, this problem doesn't have a
// straightforward "optimal" solution, and we need to resort to heuristics.
//
// mold's ICF is faster because we are using a better algorithm.
// It's actually me who developed and implemented the lld's ICF algorithm,
// and I can say that mold's algorithm is better than that in all aspects.
// It scales better for number of available cores, require less overall
// computation, and has a smaller working set. So, it's better with a single
// thread and even better with multiple threads.
// mold approaches this problem by hashing program trees with increasing depth
// on each iteration.
// For example, when we start, we only hash individual functions with
// their call into other functions omitted. From the second iteration, we
// put the function they call into the hash by appending the hash of those
// functions from the previous iteration. This means that the nth iteration
// hashes call chain up to (n-1) levels deep.
// We use a cryptographic hash function, so the unique number of hashes will
// only monotonically increase as we take into account of deeper trees with
// iterations (otherwise, that means we have found a hash collision). We stop
// when the unique number of hashes stop increasing; this is based on the fact
// that once we observe an iteration with the same amount of unique hashes as
// the previous iteration, it will remain unchanged for further iterations.
// This is provable, but here we omit the proof for brevity.
//
// When compared to other approaches, mold's approach has a relatively cheaper
// cost per iteration, and as a bonus, is highly parallelizable.
// For Chromium, mold's ICF finishes in less than 1 second with 20 threads,
// whereas lld takes 5 seconds and gold takes 50 seconds under the same
// conditions.
#include "mold.h"
#include "../sha.h"
#include <array>
#include <cstdio>
#include <tbb/concurrent_unordered_map.h>
#include <tbb/concurrent_vector.h>
#include <tbb/enumerable_thread_specific.h>
@@ -73,6 +86,13 @@ template<> struct hash<Digest> {
return *(int64_t *)&k[0];
}
};
std::string to_string(Digest digest) {
char buf[HASH_SIZE * 2];
for (int i = 0; i < HASH_SIZE; i++)
sprintf(buf + i * 2, "%02x", digest[i]);
return {buf, sizeof(buf)};
}
}
namespace mold::elf {
@@ -98,12 +118,13 @@ static void uniquify_cies(Context<E> &ctx) {
}
template <typename E>
static bool is_eligible(InputSection<E> &isec) {
static bool is_eligible(Context<E> &ctx, InputSection<E> &isec) {
const ElfShdr<E> &shdr = isec.shdr();
std::string_view name = isec.name();
bool is_alloc = (shdr.sh_flags & SHF_ALLOC);
bool is_executable = (shdr.sh_flags & SHF_EXECINSTR);
bool is_exec = (shdr.sh_flags & SHF_EXECINSTR) ||
ctx.arg.ignore_data_address_equality;
bool is_relro = (name == ".data.rel.ro" ||
name.starts_with(".data.rel.ro."));
bool is_readonly = !(shdr.sh_flags & SHF_WRITE) || is_relro;
@@ -112,9 +133,10 @@ static bool is_eligible(InputSection<E> &isec) {
bool is_init = (shdr.sh_type == SHT_INIT_ARRAY || name == ".init");
bool is_fini = (shdr.sh_type == SHT_FINI_ARRAY || name == ".fini");
bool is_enumerable = is_c_identifier(name);
bool is_addr_taken = !ctx.arg.icf_all && isec.address_significant;
return is_alloc && is_executable && is_readonly && !is_bss &&
!is_empty && !is_init && !is_fini && !is_enumerable;
return is_alloc && is_exec && is_readonly && !is_bss && !is_empty &&
!is_init && !is_fini && !is_enumerable && !is_addr_taken;
}
static Digest digest_final(SHA256_CTX &sha) {
@@ -175,6 +197,8 @@ struct LeafEq {
}
};
// Early merge of leaf nodes, which can be processed without constructing the
// entire graph. This reduces the vertex count and improves memory efficiency.
template <typename E>
static void merge_leaf_nodes(Context<E> &ctx) {
Timer t(ctx, "merge_leaf_nodes");
@@ -191,30 +215,30 @@ static void merge_leaf_nodes(Context<E> &ctx) {
if (!isec || !isec->is_alive)
continue;
if (!is_eligible(*isec)) {
if (!is_eligible(ctx, *isec)) {
non_eligible++;
continue;
}
if (is_leaf(ctx, *isec)) {
leaf++;
isec->extra().icf_leaf = true;
isec->icf_leaf = true;
auto [it, inserted] = map.insert({isec.get(), isec.get()});
if (!inserted && isec->get_priority() < it->second->get_priority())
it->second = isec.get();
} else {
eligible++;
isec->extra().icf_eligible = true;
isec->icf_eligible = true;
}
}
});
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections) {
if (isec && isec->is_alive && isec->extra().icf_leaf) {
if (isec && isec->is_alive && isec->icf_leaf) {
auto it = map.find(isec.get());
assert(it != map.end());
isec->extra().leader = it->second;
isec->leader = it->second;
}
}
});
@@ -245,10 +269,10 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
hash((u64)frag);
} else if (!isec) {
hash('3');
} else if (isec->extra().leader) {
} else if (isec->leader) {
hash('4');
hash((u64)isec->extra().leader);
} else if (isec->extra().icf_eligible) {
hash((u64)isec->leader);
} else if (isec->icf_eligible) {
hash('5');
} else {
hash('6');
@@ -271,7 +295,7 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
hash(fde.get_rels(isec.file).size());
for (ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
for (const ElfRel<E> &rel : fde.get_rels(isec.file).subspan(1)) {
hash_symbol(*isec.file.symbols[rel.r_sym]);
hash(rel.r_type);
hash(rel.r_offset - fde.input_offset);
@@ -282,7 +306,7 @@ static Digest compute_digest(Context<E> &ctx, InputSection<E> &isec) {
i64 frag_idx = 0;
for (i64 i = 0; i < isec.get_rels(ctx).size(); i++) {
ElfRel<E> &rel = isec.get_rels(ctx)[i];
const ElfRel<E> &rel = isec.get_rels(ctx)[i];
hash(rel.r_offset);
hash(rel.r_type);
hash(isec.get_addend(rel));
@@ -309,7 +333,7 @@ static std::vector<InputSection<E> *> gather_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
if (isec && isec->is_alive && isec->extra().icf_eligible)
if (isec && isec->is_alive && isec->icf_eligible)
num_sections[i]++;
});
@@ -324,12 +348,12 @@ static std::vector<InputSection<E> *> gather_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
i64 idx = section_indices[i];
for (std::unique_ptr<InputSection<E>> &isec : ctx.objs[i]->sections)
if (isec && isec->is_alive && isec->extra().icf_eligible)
if (isec && isec->is_alive && isec->icf_eligible)
sections[idx++] = isec.get();
});
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
sections[i]->extra().icf_idx = i;
sections[i]->icf_idx = i;
});
return sections;
@@ -347,6 +371,9 @@ compute_digests(Context<E> &ctx, std::span<InputSection<E> *> sections) {
return digests;
}
// Build a graph, treating every function as a vertex and every function call
// as an edge. See the description at the top for a more detailed formulation.
// We use u32 indices here to improve cache locality.
template <typename E>
static void gather_edges(Context<E> &ctx,
std::span<InputSection<E> *> sections,
@@ -362,18 +389,18 @@ static void gather_edges(Context<E> &ctx,
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
InputSection<E> &isec = *sections[i];
assert(isec.extra().icf_eligible);
assert(isec.icf_eligible);
i64 frag_idx = 0;
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
if (isec.rel_fragments && isec.rel_fragments[frag_idx].idx == j) {
frag_idx++;
} else {
ElfRel<E> &rel = isec.get_rels(ctx)[j];
const ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->extra().icf_eligible)
if (isec->icf_eligible)
num_edges[i]++;
}
}
@@ -392,12 +419,13 @@ static void gather_edges(Context<E> &ctx,
for (i64 j = 0; j < isec.get_rels(ctx).size(); j++) {
if (isec.rel_fragments && isec.rel_fragments[frag_idx].idx == j) {
frag_idx++;
ElfRel<E> &rel = isec.get_rels(ctx)[j];
} else {
const ElfRel<E> &rel = isec.get_rels(ctx)[j];
Symbol<E> &sym = *isec.file.symbols[rel.r_sym];
if (!sym.get_frag())
if (InputSection<E> *isec = sym.get_input_section())
if (isec->extra().icf_eligible)
edges[idx++] = isec->extra().icf_idx;
if (isec->icf_eligible)
edges[idx++] = isec->icf_idx;
}
}
});
@@ -458,11 +486,11 @@ static void print_icf_sections(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections) {
if (isec && isec->is_alive && isec->extra().leader) {
if (isec.get() == isec->extra().leader)
if (isec && isec->is_alive && isec->leader) {
if (isec.get() == isec->leader)
leaders.push_back(isec.get());
else
map.insert({isec->extra().leader, isec.get()});
map.insert({isec->leader, isec.get()});
}
}
});
@@ -495,9 +523,8 @@ static void print_icf_sections(Context<E> &ctx) {
template <typename E>
void icf_sections(Context<E> &ctx) {
Timer t(ctx, "icf");
for (ObjectFile<E> *file : ctx.objs)
file->extras.resize(file->sections.size());
if (ctx.objs.empty())
return;
uniquify_cies(ctx);
merge_leaf_nodes(ctx);
@@ -505,6 +532,11 @@ void icf_sections(Context<E> &ctx) {
// Prepare for the propagation rounds.
std::vector<InputSection<E> *> sections = gather_sections(ctx);
// We allocate 3 arrays to store hashes for each vertex.
// Index 0 and 1 are used for tree hashes from the previous iteration and the current iteration.
// They switch roles every iteration --- see `slot` below.
// Index 2 stores the initial, single-vertex hash --- this is combined with hashes from the connected vertices to
// form the tree hash described above.
std::vector<std::vector<Digest>> digests(3);
digests[0] = compute_digests<E>(ctx, sections);
digests[1].resize(digests[0].size());
@@ -521,6 +553,17 @@ void icf_sections(Context<E> &ctx) {
Timer t(ctx, "propagate");
tbb::affinity_partitioner ap;
// A cheap test that the graph hasn't converged yet.
// The loop after this one uses a strict condition, but it's expensive
// as it requires sorting the entire hash collection.
//
// For nodes that have a cycle in downstream (i.e. recursive
// functions and functions that calls recursive functions) will always
// change with the iterations. Nodes that doesn't (i.e. non-recursive
// functions) will stop changing as soon as the propagation depth reaches
// the call tree depth.
// Here, we test whether we have reached sufficient depth for the latter,
// which is a necessary (but not sufficient) condition for convergence.
i64 num_changed = -1;
for (;;) {
i64 n = propagate<E>(digests, edges, edge_indices, slot, ap);
@@ -529,8 +572,12 @@ void icf_sections(Context<E> &ctx) {
num_changed = n;
}
// Run the pass until the unique number of hashes stop increasing, at which
// point we have achieved convergence (proof omitted for brevity).
i64 num_classes = -1;
for (;;) {
// count_num_classes requires sorting which is O(n log n), so do a little
// more work beforehand to amortize that log factor.
for (i64 i = 0; i < 10; i++)
propagate<E>(digests, edges, edge_indices, slot, ap);
@@ -558,7 +605,7 @@ void icf_sections(Context<E> &ctx) {
tbb::parallel_for((i64)0, (i64)sections.size(), [&](i64 i) {
auto it = map->find(digest[i]);
assert(it != map->end());
sections[i]->extra().leader = it->second;
sections[i]->leader = it->second;
});
// Since free'ing the map is slow, postpone it.
@@ -575,7 +622,7 @@ void icf_sections(Context<E> &ctx) {
for (Symbol<E> *sym : file->symbols) {
if (sym->file == file) {
InputSection<E> *isec = sym->get_input_section();
if (isec && isec->extra().leader && isec->extra().leader != isec) {
if (isec && isec->leader && isec->leader != isec) {
isec->kill();
isec->killed_by_icf = true;
}
@@ -588,10 +635,6 @@ void icf_sections(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void icf_sections(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+140 -67
View File
@@ -63,6 +63,15 @@ void InputFile<E>::clear_symbols() {
}
}
// Find the source filename. It should be listed in symtab as STT_FILE.
template <typename E>
std::string_view InputFile<E>::get_source_name() const {
for (i64 i = 0; i < first_global; i++)
if (Symbol<E> *sym = symbols[i]; sym->get_type() == STT_FILE)
return sym->name();
return "";
}
template <typename E>
ObjectFile<E>::ObjectFile(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name, bool is_in_lib)
@@ -105,11 +114,11 @@ u32 ObjectFile<E>::read_note_gnu_property(Context<E> &ctx,
continue;
while (!desc.empty()) {
u32 type = *(u32 *)desc.data();
u32 size = *(u32 *)(desc.data() + 4);
u32 type = *(ul32 *)desc.data();
u32 size = *(ul32 *)(desc.data() + 4);
desc = desc.substr(8);
if (type == GNU_PROPERTY_X86_FEATURE_1_AND)
ret |= *(u32 *)desc.data();
ret |= *(ul32 *)desc.data();
desc = desc.substr(align_to(size, E::word_size));
}
}
@@ -122,7 +131,8 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
for (i64 i = 0; i < this->elf_sections.size(); i++) {
const ElfShdr<E> &shdr = this->elf_sections[i];
if ((shdr.sh_flags & SHF_EXCLUDE) && !(shdr.sh_flags & SHF_ALLOC))
if ((shdr.sh_flags & SHF_EXCLUDE) && !(shdr.sh_flags & SHF_ALLOC) &&
shdr.sh_type != SHT_LLVM_ADDRSIG)
continue;
switch (shdr.sh_type) {
@@ -131,10 +141,15 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
if (shdr.sh_info >= this->elf_syms.size())
Fatal(ctx) << *this << ": invalid symbol index";
const ElfSym<E> &sym = this->elf_syms[shdr.sh_info];
std::string_view signature = symbol_strtab.data() + sym.st_name;
std::string_view signature = this->symbol_strtab.data() + sym.st_name;
// Ignore a broken comdat group GCC emits for .debug_macros.
// https://github.com/rui314/mold/issues/438
if (signature.starts_with("wm4."))
continue;
// Get comdat group members.
std::span<u32> entries = this->template get_data<u32>(ctx, shdr);
std::span<ul32> entries = this->template get_data<ul32>(ctx, shdr);
if (entries.empty())
Fatal(ctx) << *this << ": empty SHT_GROUP";
@@ -167,11 +182,14 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
// making the stack area executable is too dangerous. Tell our
// users about the difference if that matters.
if (name == ".note.GNU-stack") {
if (shdr.sh_flags & SHF_EXECINSTR)
Warn(ctx) << *this << ": this file may cause a segmentation"
" fault because it requires an executable stack. See"
" https://github.com/rui314/mold/tree/main/docs/execstack.md"
" for more info.";
if (shdr.sh_flags & SHF_EXECINSTR) {
if (!ctx.arg.z_execstack && !ctx.arg.z_execstack_if_needed)
Warn(ctx) << *this << ": this file may cause a segmentation"
" fault because it requires an executable stack. See"
" https://github.com/rui314/mold/tree/main/docs/execstack.md"
" for more info.";
needs_executable_stack = true;
}
continue;
}
@@ -199,16 +217,22 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
this->sections[i] = std::make_unique<InputSection<E>>(ctx, *this, name, i);
// Save .llvm_addrsig for --icf=safe.
if (shdr.sh_type == SHT_LLVM_ADDRSIG) {
if (ctx.arg.icf && !ctx.arg.icf_all)
llvm_addrsig = this->sections[i].get();
}
// Save debug sections for --gdb-index.
if (ctx.arg.gdb_index) {
InputSection<E> *isec = this->sections[i].get();
if (name == ".debug_info")
debug_info = isec;
if (name == ".debug_abbrev")
debug_abbrev = isec;
if (name == ".debug_ranges")
debug_ranges = isec;
if (name == ".debug_rnglists")
debug_rnglists = isec;
// If --gdb-index is given, contents of .debug_gnu_pubnames and
// .debug_gnu_pubtypes are copied to .gdb_index, so keeping them
@@ -222,6 +246,16 @@ void ObjectFile<E>::initialize_sections(Context<E> &ctx) {
debug_pubtypes = isec;
isec->is_alive = false;
}
// .debug_types is similar to .debug_info but contains type info
// only. It exists only in DWARF 4, has been removed in DWARF 5 and
// neither GCC nor Clang generate it by default
// (-fdebug-types-section is needed). As such there is probably
// little need to support it.
if (name == ".debug_types")
Fatal(ctx) << *this << ": mold's --gdb-index is not compatible"
" with .debug_types; to fix this error, remove"
" -fdebug-types-section and recompile";
}
static Counter counter("regular_sections");
@@ -290,7 +324,7 @@ void ObjectFile<E>::initialize_ehframe_sections(Context<E> &ctx) {
// This function parses an input .eh_frame section.
template <typename E>
void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
std::span<ElfRel<E>> rels = isec.get_rels(ctx);
std::span<const ElfRel<E>> rels = isec.get_rels(ctx);
i64 cies_begin = cies.size();
i64 fdes_begin = fdes.size();
@@ -299,13 +333,13 @@ void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
i64 rel_idx = 0;
for (std::string_view data = contents; !data.empty();) {
i64 size = *(u32 *)data.data();
i64 size = *(ul32 *)data.data();
if (size == 0)
break;
i64 begin_offset = data.data() - contents.data();
i64 end_offset = begin_offset + size + 4;
i64 id = *(u32 *)(data.data() + 4);
i64 id = *(ul32 *)(data.data() + 4);
data = data.substr(size + 4);
i64 rel_begin = rel_idx;
@@ -341,7 +375,7 @@ void ObjectFile<E>::read_ehframe(Context<E> &ctx, InputSection<E> &isec) {
};
for (i64 i = fdes_begin; i < fdes.size(); i++) {
i64 cie_offset = *(i32 *)(contents.data() + fdes[i].input_offset + 4);
i64 cie_offset = *(il32 *)(contents.data() + fdes[i].input_offset + 4);
fdes[i].cie_idx = find_cie(fdes[i].input_offset + 4 - cie_offset);
}
@@ -408,7 +442,7 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
if (esym.is_common())
Fatal(ctx) << *this << ": common local symbol?";
std::string_view name = symbol_strtab.data() + esym.st_name;
std::string_view name = this->symbol_strtab.data() + esym.st_name;
if (name.empty() && esym.st_type == STT_SECTION)
if (InputSection<E> *sec = get_section(esym))
name = sec->name();
@@ -437,7 +471,7 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
const ElfSym<E> &esym = this->elf_syms[i];
// Get a symbol name
std::string_view key = symbol_strtab.data() + esym.st_name;
std::string_view key = this->symbol_strtab.data() + esym.st_name;
std::string_view name = key;
// Parse symbol version after atsign
@@ -464,27 +498,24 @@ void ObjectFile<E>::initialize_symbols(Context<E> &ctx) {
// We expect them to be sorted, so sort them if necessary.
template <typename E>
void ObjectFile<E>::sort_relocations(Context<E> &ctx) {
if (!std::is_same_v<E, RISCV64>)
return;
if constexpr (std::is_same_v<E, RISCV64>) {
auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
return a.r_type != E::R_NONE && b.r_type != E::R_NONE &&
a.r_offset < b.r_offset;
};
auto less = [&](const ElfRel<E> &a, const ElfRel<E> &b) {
return a.r_type != E::R_NONE && b.r_type != E::R_NONE &&
a.r_offset < b.r_offset;
};
for (i64 i = 1; i < sections.size(); i++) {
std::unique_ptr<InputSection<E>> &isec = sections[i];
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
continue;
sorted_rels.resize(sections.size());
std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
if (std::is_sorted(rels.begin(), rels.end(), less))
continue;
for (i64 i = 1; i < sections.size(); i++) {
std::unique_ptr<InputSection<E>> &isec = sections[i];;
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
continue;
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
if (std::is_sorted(rels.begin(), rels.end(), less))
continue;
sorted_rels[isec->shndx] = {rels.begin(), rels.end()};
sort(sorted_rels[isec->shndx], less);
isec->extra.sorted_rels = {rels.begin(), rels.end()};
sort(isec->extra.sorted_rels, less);
}
}
}
@@ -524,16 +555,10 @@ split_section(Context<E> &ctx, InputSection<E> &sec) {
sec.shdr().sh_flags);
rec->p2align = sec.p2align;
std::string_view data = sec.contents;
// If thes section contents are compressed, uncompress them.
if (sec.is_compressed()) {
u8 *buf = new u8[sec.sh_size];
sec.uncompress(ctx, buf);
data = {(char *)buf, sec.sh_size};
ctx.string_pool.emplace_back(buf);
}
sec.uncompress(ctx);
std::string_view data = sec.contents;
const char *begin = data.data();
u64 entsize = sec.shdr().sh_entsize;
HyperLogLog estimator;
@@ -656,7 +681,7 @@ void ObjectFile<E>::register_section_pieces(Context<E> &ctx) {
if (!isec || !isec->is_alive || !(isec->shdr().sh_flags & SHF_ALLOC))
continue;
std::span<ElfRel<E>> rels = isec->get_rels(ctx);
std::span<const ElfRel<E>> rels = isec->get_rels(ctx);
if (rels.empty())
continue;
@@ -728,15 +753,46 @@ void ObjectFile<E>::register_section_pieces(Context<E> &ctx) {
}
}
template <typename E>
void ObjectFile<E>::fill_addrsig(Context<E> &ctx) {
if (llvm_addrsig) {
const u8 *start = reinterpret_cast<const u8 *>(llvm_addrsig->contents.data());
const u8 *end = start + llvm_addrsig->contents.size();
const u8 *cur = start;
while (cur != end) {
u64 symIndex = read_uleb(cur);
if (this->symbols[symIndex]->file != this) continue;
InputSection<E> *section = this->symbols[symIndex]->get_input_section();
if (section)
section->address_significant = true;
}
}
for (Symbol<E> *sym : this->symbols) {
if (sym->file != this) continue;
InputSection<E> *section = sym->get_input_section();
if (
section &&
(!llvm_addrsig // We don't have address significance information and needs to be safe
|| (sym->is_imported || sym->is_exported)) // The symbol might be referenced from the
// outside in an address-significant manner
) {
section->address_significant = true;
}
}
}
template <typename E>
void ObjectFile<E>::parse(Context<E> &ctx) {
sections.resize(this->elf_sections.size());
symtab_sec = this->find_section(SHT_SYMTAB);
if (symtab_sec) {
// In ELF, all local symbols precede global symbols in the symbol table.
// sh_info has an index of the first global symbol.
this->first_global = symtab_sec->sh_info;
this->elf_syms = this->template get_data<ElfSym<E>>(ctx, *symtab_sec);
symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
this->symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
}
initialize_sections(ctx);
@@ -925,7 +981,7 @@ void ObjectFile<E>::eliminate_duplicate_comdat_groups() {
if (group->owner == this->priority)
continue;
std::span<u32> entries = pair.second;
std::span<ul32> entries = pair.second;
for (u32 i : entries)
if (sections[i])
sections[i]->kill();
@@ -937,6 +993,18 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
if (!this->is_alive)
return;
auto report_undef = [&](Symbol<E> &sym) {
std::stringstream ss;
if (std::string_view source = this->get_source_name(); !source.empty())
ss << ">>> referenced by " << source << "\n";
else
ss << ">>> referenced by " << *this << "\n";
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
acc->second.push_back(ss.str());
};
for (i64 i = this->first_global; i < this->symbols.size(); i++) {
const ElfSym<E> &esym = this->elf_syms[i];
Symbol<E> &sym = *this->symbols[i];
@@ -949,7 +1017,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
// imported symbol, it's handled as if no symbols were found.
if (sym.file && sym.file->is_dso &&
(sym.visibility == STV_PROTECTED || sym.visibility == STV_HIDDEN)) {
report_undef(ctx, *this, sym);
report_undef(sym);
continue;
}
@@ -959,7 +1027,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
// If a symbol name is in the form of "foo@version", search for
// symbol "foo" and check if the symbol has version "version".
std::string_view key = symbol_strtab.data() + esym.st_name;
std::string_view key = this->symbol_strtab.data() + esym.st_name;
if (i64 pos = key.find('@'); pos != key.npos) {
Symbol<E> *sym2 = get_symbol(ctx, key.substr(0, pos));
if (sym2->file && sym2->file->is_dso &&
@@ -979,7 +1047,7 @@ void ObjectFile<E>::claim_unresolved_symbols(Context<E> &ctx) {
};
if (ctx.arg.unresolved_symbols == UNRESOLVED_WARN)
report_undef(ctx, *this, sym);
report_undef(sym);
// Convert remaining undefined symbols to dynamic symbols.
if (ctx.arg.shared) {
@@ -1026,7 +1094,7 @@ void ObjectFile<E>::scan_relocations(Context<E> &ctx) {
// Scan relocations against exception frames
for (CieRecord<E> &cie : cies) {
for (ElfRel<E> &rel : cie.get_rels()) {
for (const ElfRel<E> &rel : cie.get_rels()) {
Symbol<E> &sym = *this->symbols[rel.r_sym];
if (sym.is_imported) {
@@ -1063,10 +1131,14 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
if (!has_common_symbol)
return;
OutputSection<E> *osec =
OutputSection<E> *common =
OutputSection<E>::get_instance(ctx, ".common", SHT_NOBITS,
SHF_WRITE | SHF_ALLOC);
OutputSection<E> *tls_common =
OutputSection<E>::get_instance(ctx, ".tls_common", SHT_NOBITS,
SHF_WRITE | SHF_ALLOC | SHF_TLS);
for (i64 i = this->first_global; i < this->elf_syms.size(); i++) {
if (!this->elf_syms[i].is_common())
continue;
@@ -1082,17 +1154,20 @@ void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
elf_sections2.push_back({});
ElfShdr<E> &shdr = elf_sections2.back();
memset(&shdr, 0, sizeof(shdr));
shdr.sh_flags = SHF_ALLOC;
bool is_tls = (sym.get_type() == STT_TLS);
shdr.sh_flags = is_tls ? (SHF_ALLOC | SHF_TLS) : SHF_ALLOC;
shdr.sh_type = SHT_NOBITS;
shdr.sh_size = this->elf_syms[i].st_size;
shdr.sh_addralign = this->elf_syms[i].st_value;
i64 idx = this->elf_sections.size() + elf_sections2.size() - 1;
std::unique_ptr<InputSection<E>> isec =
std::make_unique<InputSection<E>>(ctx, *this, ".common", idx);
isec->output_section = osec;
std::make_unique<InputSection<E>>(ctx, *this,
is_tls ? ".tls_common" : ".common",
idx);
isec->output_section = is_tls ? tls_common : common;
sym.file = this;
sym.shndx = idx;
@@ -1187,9 +1262,9 @@ void ObjectFile<E>::write_symtab(Context<E> &ctx) {
esym.st_name = strtab_off;
if (sym.get_type() == STT_TLS)
esym.st_value = sym.get_addr(ctx) - ctx.tls_begin;
esym.st_value = sym.get_addr(ctx, false) - ctx.tls_begin;
else
esym.st_value = sym.get_addr(ctx);
esym.st_value = sym.get_addr(ctx, false);
if (InputSection<E> *isec = sym.get_input_section())
esym.st_shndx = isec->output_section->shndx;
@@ -1251,6 +1326,7 @@ SharedFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
template <typename E>
SharedFile<E>::SharedFile(Context<E> &ctx, MappedFile<Context<E>> *mf)
: InputFile<E>(ctx, mf) {
this->is_needed = ctx.as_needed;
this->is_alive = !ctx.as_needed;
}
@@ -1259,7 +1335,7 @@ std::string SharedFile<E>::get_soname(Context<E> &ctx) {
if (ElfShdr<E> *sec = this->find_section(SHT_DYNAMIC))
for (ElfDyn<E> &dyn : this->template get_data<ElfDyn<E>>(ctx, *sec))
if (dyn.d_tag == DT_SONAME)
return symbol_strtab.data() + dyn.d_val;
return this->symbol_strtab.data() + dyn.d_val;
if (this->mf->given_fullpath)
return this->filename;
@@ -1273,7 +1349,7 @@ void SharedFile<E>::parse(Context<E> &ctx) {
if (!symtab_sec)
return;
symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
this->symbol_strtab = this->get_string(ctx, symtab_sec->sh_link);
soname = get_soname(ctx);
version_strings = read_verdef(ctx);
@@ -1294,7 +1370,7 @@ void SharedFile<E>::parse(Context<E> &ctx) {
if (ver == VER_NDX_LOCAL)
continue;
std::string_view name = symbol_strtab.data() + esyms[i].st_name;
std::string_view name = this->symbol_strtab.data() + esyms[i].st_name;
bool is_hidden = (!vers.empty() && (vers[i] & VERSYM_HIDDEN));
this->elf_syms2.push_back(esyms[i]);
@@ -1456,14 +1532,11 @@ void SharedFile<E>::write_symtab(Context<E> &ctx) {
}
#define INSTANTIATE(E) \
template class InputFile<E>; \
template class ObjectFile<E>; \
template class SharedFile<E>; \
template std::ostream &operator<<(std::ostream &, const InputFile<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+94 -41
View File
@@ -10,8 +10,8 @@ bool CieRecord<E>::equals(const CieRecord<E> &other) const {
if (get_contents() != other.get_contents())
return false;
std::span<ElfRel<E>> x = get_rels();
std::span<ElfRel<E>> y = other.get_rels();
std::span<const ElfRel<E>> x = get_rels();
std::span<const ElfRel<E>> y = other.get_rels();
if (x.size() != y.size())
return false;
@@ -36,12 +36,10 @@ InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
std::string_view name, i64 shndx)
: file(file), shndx(shndx) {
if (shndx < file.elf_sections.size())
contents = {(char *)file.mf->data + shdr().sh_offset, shdr().sh_size};
bool compressed;
contents = {(char *)file.mf->data + shdr().sh_offset, (size_t)shdr().sh_size};
if (name.starts_with(".zdebug")) {
sh_size = *(ubig64 *)&contents[4];
sh_size = *(ub64 *)&contents[4];
p2align = to_p2align(shdr().sh_addralign);
compressed = true;
} else if (shdr().sh_flags & SHF_COMPRESSED) {
@@ -55,28 +53,37 @@ InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
compressed = false;
}
// Uncompress early if the relocation is REL-type so that we can read
// addends from section contents. If RELA-type, we don't need to do this
// because addends are in relocations.
if (compressed && E::is_rel) {
u8 *buf = new u8[sh_size];
uncompress(ctx, buf);
contents = {(char *)buf, sh_size};
ctx.string_pool.emplace_back(buf);
}
// Sections may have been compressed. We usually uncompress them
// directly into the mmap'ed output file, but we want to uncompress
// early for REL-type ELF types to read relocation addends from
// section contents. For RELA-type, we don't need to do this because
// addends are in relocations.
if (E::is_rel)
uncompress(ctx);
output_section =
OutputSection<E>::get_instance(ctx, name, shdr().sh_type, shdr().sh_flags);
}
template <typename E>
bool InputSection<E>::is_compressed() {
return !E::is_rel &&
(name().starts_with(".zdebug") || (shdr().sh_flags & SHF_COMPRESSED));
void InputSection<E>::uncompress(Context<E> &ctx) {
if (!compressed || uncompressed)
return;
u8 *buf = new u8[sh_size];
uncompress_to(ctx, buf);
contents = {(char *)buf, sh_size};
ctx.string_pool.emplace_back(buf);
uncompressed = true;
}
template <typename E>
void InputSection<E>::uncompress(Context<E> &ctx, u8 *buf) {
void InputSection<E>::uncompress_to(Context<E> &ctx, u8 *buf) {
if (!compressed || uncompressed) {
memcpy(buf, contents.data(), contents.size());
return;
}
auto do_uncompress = [&](std::string_view data) {
unsigned long size = sh_size;
if (::uncompress(buf, &size, (u8 *)data.data(), data.size()) != Z_OK)
@@ -100,7 +107,8 @@ void InputSection<E>::uncompress(Context<E> &ctx, u8 *buf) {
ElfChdr<E> &hdr = *(ElfChdr<E> *)&contents[0];
if (hdr.ch_type != ELFCOMPRESS_ZLIB)
Fatal(ctx) << *this << ": unsupported compression type";
Fatal(ctx) << *this << ": unsupported compression type: 0x"
<< std::hex << hdr.ch_type;
do_uncompress(contents.substr(sizeof(ElfChdr<E>)));
}
@@ -213,8 +221,8 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
// Copy data
if constexpr (std::is_same_v<E, RISCV64>) {
copy_contents_riscv(ctx, buf);
} else if (is_compressed()) {
uncompress(ctx, buf);
} else if (compressed) {
uncompress_to(ctx, buf);
} else {
memcpy(buf, contents.data(), contents.size());
}
@@ -226,33 +234,78 @@ void InputSection<E>::write_to(Context<E> &ctx, u8 *buf) {
apply_reloc_nonalloc(ctx, buf);
}
// Get the name of a function containin a given offset.
template <typename E>
void report_undef(Context<E> &ctx, InputFile<E> &file, Symbol<E> &sym) {
if (ctx.arg.warn_once && !ctx.warned.insert({(void *)&sym, 1}))
return;
switch (ctx.arg.unresolved_symbols) {
case UNRESOLVED_ERROR:
Error(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UNRESOLVED_WARN:
Warn(ctx) << "undefined symbol: " << file << ": " << sym;
break;
case UNRESOLVED_IGNORE:
break;
std::string_view InputSection<E>::get_func_name(Context<E> &ctx, i64 offset) {
for (const ElfSym<E> &esym : file.elf_syms) {
if (esym.st_shndx == shndx && esym.st_type == STT_FUNC &&
esym.st_value <= offset && offset < esym.st_value + esym.st_size) {
std::string_view name = file.symbol_strtab.data() + esym.st_name;
if (ctx.arg.demangle)
return demangle(name);
return name;
}
}
return "";
}
// Record an undefined symbol error which will be displayed all at
// once by report_undef_errors().
template <typename E>
void InputSection<E>::record_undef_error(Context<E> &ctx, const ElfRel<E> &rel) {
std::stringstream ss;
if (std::string_view source = file.get_source_name(); !source.empty())
ss << ">>> referenced by " << source << "\n";
else
ss << ">>> referenced by " << *this << "\n";
ss << ">>> " << file;
if (std::string_view func = get_func_name(ctx, rel.r_offset); !func.empty())
ss << ":(" << func << ")";
Symbol<E> &sym = *file.symbols[rel.r_sym];
typename decltype(ctx.undef_errors)::accessor acc;
ctx.undef_errors.insert(acc, {sym.name(), {}});
acc->second.push_back(ss.str());
}
// Report all undefined symbols, grouped by symbol.
template <typename E>
void report_undef_errors(Context<E> &ctx) {
constexpr i64 max_errors = 3;
for (auto &pair : ctx.undef_errors) {
std::string_view sym_name = pair.first;
std::span<std::string> errors = pair.second;
if (ctx.arg.demangle)
sym_name = demangle(sym_name);
std::stringstream ss;
ss << "undefined symbol: " << sym_name << "\n";
for (i64 i = 0; i < errors.size() && i < max_errors; i++)
ss << errors[i];
if (errors.size() > max_errors)
ss << ">>> referenced " << (errors.size() - max_errors) << " more times\n";
if (ctx.arg.unresolved_symbols == UNRESOLVED_ERROR)
Error(ctx) << ss.str();
else if (ctx.arg.unresolved_symbols == UNRESOLVED_WARN)
Warn(ctx) << ss.str();
}
ctx.checkpoint();
}
#define INSTANTIATE(E) \
template struct CieRecord<E>; \
template class InputSection<E>; \
template void report_undef(Context<E> &, InputFile<E> &, Symbol<E> &)
template void report_undef_errors(Context<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+1 -5
View File
@@ -405,10 +405,6 @@ void parse_dynamic_list(Context<E> &ctx, std::string path) {
template \
void parse_dynamic_list(Context<E> &ctx, std::string path)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+46 -49
View File
@@ -79,13 +79,14 @@
// the compiler backend.
#include "mold.h"
#include "../lto.h"
#include "lto.h"
#include <cstdarg>
#include <cstring>
#include <dlfcn.h>
#include <fcntl.h>
#include <sstream>
#include <tbb/parallel_for_each.h>
#include <unistd.h>
#if 0
@@ -304,6 +305,44 @@ get_symbols(const void *handle, int nsyms, PluginSymbol *psyms, bool is_v2) {
return LDPS_OK;
}
// This function restarts mold itself with `--:lto-pass2` and
// `--:ignore-ir-file` flags. We do this as a workaround for the old
// linker plugins that do not support the get_symbols_v3 API.
//
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
// object file we previously passed to the linker plugin. So we can't
// "unload" object files in archives that we ended up not choosing to
// include into the final output.
//
// As a workaround, we restart the linker with a list of object files
// the linker has to ignore, so that it won't read the object files
// from archives next time.
//
// This is an ugly hack and should be removed once GCC adopts the v3 API.
template <typename E>
static void restart_process(Context<E> &ctx) {
std::vector<const char *> args;
for (std::string_view arg : ctx.cmdline_args)
args.push_back(strdup(std::string(arg).c_str()));
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
if (file->is_lto_obj && !file->is_alive)
args.push_back(strdup(("--:ignore-ir-file=" +
file->mf->get_identifier()).c_str()));
args.push_back("--:lto-pass2");
args.push_back(nullptr);
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::cout << std::flush;
std::cerr << std::flush;
execv(self.c_str(), (char * const *)args.data());
std::cerr << "execv failed: " << errno_string() << "\n";
_exit(1);
}
template <typename E>
static PluginStatus
get_symbols_v2(const void *handle, int nsyms, PluginSymbol *psyms) {
@@ -564,44 +603,6 @@ ObjectFile<E> *read_lto_object(Context<E> &ctx, MappedFile<Context<E>> *mf) {
return obj;
}
// This function restarts mold itself with `--:lto-pass2` and
// `--:ignore-ir-file` flags. We do this as a workaround for the old
// linker plugins that do not support the get_symbols_v3 API.
//
// get_symbols_v1 and get_symbols_v2 don't provide a way to ignore an
// object file we previously passed to the linker plugin. So we can't
// "unload" object files in archives that we ended up not choosing to
// include into the final output.
//
// As a workaround, we restart the linker with a list of object files
// the linker has to ignore, so that it won't read the object files
// from archives next time.
//
// This is an ugly hack and should be removed once GCC adopts the v3 API.
template <typename E>
static void restart_process(Context<E> &ctx) {
std::vector<const char *> args;
for (std::string_view arg : ctx.cmdline_args)
args.push_back(strdup(std::string(arg).c_str()));
for (std::unique_ptr<ObjectFile<E>> &file : ctx.obj_pool)
if (file->is_lto_obj && !file->is_alive)
args.push_back(strdup(("--:ignore-ir-file=" +
file->mf->get_identifier()).c_str()));
args.push_back("--:lto-pass2");
args.push_back(nullptr);
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::cout << std::flush;
std::cerr << std::flush;
execv(self.c_str(), (char * const *)args.data());
std::cerr << "execv failed: " << errno_string() << "\n";
_exit(1);
}
// Entry point
template <typename E>
std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
@@ -614,21 +615,21 @@ std::vector<ObjectFile<E> *> do_lto(Context<E> &ctx) {
phase = 2;
// Set `referenced_by_regular_obj` bit.
for (ObjectFile<E> *file : ctx.objs) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
if (file->is_lto_obj)
continue;
return;
for (i64 i = file->first_global; i < file->symbols.size(); i++) {
ElfSym<E> &esym = file->elf_syms[i];
Symbol<E> &sym = *file->symbols[i];
if (esym.is_undef() && sym.file && sym.file != file &&
!sym.file->is_dso && ((ObjectFile<E> *)sym.file)->is_lto_obj) {
if (sym.file && !sym.file->is_dso &&
((ObjectFile<E> *)sym.file)->is_lto_obj) {
std::scoped_lock lock(sym.mu);
sym.referenced_by_regular_obj = true;
}
}
}
});
// all_symbols_read_hook() calls add_input_file() and add_input_library()
LOG << "all symbols read\n";
@@ -652,10 +653,6 @@ void lto_cleanup(Context<E> &ctx) {
template std::vector<ObjectFile<E> *> do_lto(Context<E> &); \
template void lto_cleanup(Context<E> &)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+6 -6
View File
@@ -72,15 +72,15 @@ enum PluginOutputFileType {
struct PluginInputFile {
const char *name;
int fd;
off_t offset;
off_t filesize;
int32_t fd;
uint64_t offset;
uint64_t filesize;
void *handle;
};
struct PluginSection {
const void *handle;
int shndx;
uint32_t shndx;
};
struct PluginSymbol {
@@ -89,10 +89,10 @@ struct PluginSymbol {
char def;
char symbol_type;
char section_kind;
int visibility;
int32_t visibility;
uint64_t size;
char *comdat_key;
int resolution;
int32_t resolution;
};
enum PluginSymbolKind {
+88 -104
View File
@@ -1,6 +1,7 @@
#include "mold.h"
#include "../archive-file.h"
#include "../cmdline.h"
#include "../output-file.h"
#include <cstring>
#include <functional>
@@ -41,10 +42,11 @@ static ObjectFile<E> *new_lto_obj(Context<E> &ctx, MappedFile<Context<E>> *mf,
count++;
if (ctx.arg.ignore_ir_file.count(mf->get_identifier()))
return new ObjectFile<E>;
return nullptr;
ObjectFile<E> *file = read_lto_object(ctx, mf);
file->priority = ctx.file_priority++;
file->archive_name = archive_name;
file->is_in_lib = ctx.in_lib || (!archive_name.empty() && !ctx.whole_archive);
file->is_alive = !file->is_in_lib;
ctx.has_lto_object = true;
@@ -90,7 +92,8 @@ void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
break;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, child, mf->name));
if (ObjectFile<E> *file = new_lto_obj(ctx, child, mf->name))
ctx.objs.push_back(file);
break;
default:
break;
@@ -103,7 +106,8 @@ void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
return;
case FileType::GCC_LTO_OBJ:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(new_lto_obj(ctx, mf, ""));
if (ObjectFile<E> *file = new_lto_obj(ctx, mf, ""))
ctx.objs.push_back(file);
return;
default:
Fatal(ctx) << mf->name << ": unknown file type: " << type;
@@ -138,7 +142,7 @@ static i64 get_machine_type(Context<E> &ctx, MappedFile<Context<E>> *mf) {
template <typename E>
static i64
deduce_machine_type(Context<E> &ctx, std::span<std::string_view> args) {
deduce_machine_type(Context<E> &ctx, std::span<std::string> args) {
for (std::string_view arg : args)
if (!arg.starts_with('-'))
if (auto *mf = MappedFile<Context<E>>::open(ctx, std::string(arg)))
@@ -184,51 +188,51 @@ MappedFile<Context<E>> *find_library(Context<E> &ctx, std::string name) {
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string_view> args) {
static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
Timer t(ctx, "read_input_files");
std::vector<std::tuple<bool, bool, bool, bool>> state;
ctx.is_static = ctx.arg.is_static;
while (!args.empty()) {
std::string_view arg;
const std::string &arg = args[0];
args = args.subspan(1);
if (read_flag(args, "as-needed")) {
if (arg == "--as-needed") {
ctx.as_needed = true;
} else if (read_flag(args, "no-as-needed")) {
} else if (arg == "--no-as-needed") {
ctx.as_needed = false;
} else if (read_flag(args, "whole-archive")) {
} else if (arg == "--whole-archive") {
ctx.whole_archive = true;
} else if (read_flag(args, "no-whole-archive")) {
} else if (arg == "--no-whole-archive") {
ctx.whole_archive = false;
} else if (read_flag(args, "Bstatic")) {
} else if (arg == "--Bstatic") {
ctx.is_static = true;
} else if (read_flag(args, "Bdynamic")) {
} else if (arg == "--Bdynamic") {
ctx.is_static = false;
} else if (read_flag(args, "start-lib")) {
} else if (arg == "--start-lib") {
ctx.in_lib = true;
} else if (read_flag(args, "end-lib")) {
} else if (arg == "--end-lib") {
ctx.in_lib = false;
} else if (read_arg(ctx, args, arg, "version-script")) {
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")) {
} else if (arg.starts_with("--version-script=")) {
parse_version_script(ctx, arg.substr(strlen("--version-script=")));
} else if (arg.starts_with("--dynamic-list=")) {
parse_dynamic_list(ctx, arg.substr(strlen("--dynamic-list=")));
} else if (arg == "--push-state") {
state.push_back({ctx.as_needed, ctx.whole_archive, ctx.is_static,
ctx.in_lib});
} else if (read_flag(args, "pop-state")) {
} else if (arg == "--pop-state") {
if (state.empty())
Fatal(ctx) << "no state pushed before popping";
std::tie(ctx.as_needed, ctx.whole_archive, ctx.is_static, ctx.in_lib) =
state.back();
state.pop_back();
} else if (read_arg(ctx, args, arg, "l")) {
MappedFile<Context<E>> *mf = find_library(ctx, std::string(arg));
} else if (arg.starts_with("-l")) {
MappedFile<Context<E>> *mf = find_library(ctx, arg.substr(2));
mf->given_fullpath = false;
read_file(ctx, mf);
} else {
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, std::string(args[0])));
args = args.subspan(1);
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, std::string(arg)));
}
}
@@ -355,13 +359,6 @@ static void show_stats(Context<E> &ctx) {
sec->print_stats(ctx);
}
static i64 get_default_thread_count() {
// mold doesn't scale well above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
template <typename E>
static int elf_main(int argc, char **argv) {
Context<E> ctx;
@@ -373,8 +370,7 @@ static int elf_main(int argc, char **argv) {
// Parse non-positional command line options
ctx.cmdline_args = expand_response_files(ctx, argv);
std::vector<std::string_view> file_args;
parse_nonpositional_args(ctx, file_args);
std::vector<std::string> file_args = parse_nonpositional_args(ctx);
// If no -m option is given, deduce it from input files.
if (ctx.arg.emulation == -1)
@@ -382,6 +378,7 @@ static int elf_main(int argc, char **argv) {
// Redo if -m is not x86-64.
if (ctx.arg.emulation != E::e_machine) {
#if !MOLD_DEBUG_X86_64_ONLY && !MOLD_DEBUG_ARM64_ONLY
switch (ctx.arg.emulation) {
case EM_386:
return elf_main<I386>(argc, argv);
@@ -392,25 +389,12 @@ static int elf_main(int argc, char **argv) {
case EM_RISCV:
return elf_main<RISCV64>(argc, argv);
}
unreachable();
#endif
Fatal(ctx) << "unknown emulation: " << ctx.arg.emulation;
}
Timer t_all(ctx, "all");
if (ctx.arg.relocatable) {
combine_objects(ctx, file_args);
return 0;
}
if (!ctx.arg.preload)
try_resume_daemon(ctx);
i64 thread_count = ctx.arg.thread_count;
if (thread_count == 0)
thread_count = get_default_thread_count();
tbb::global_control tbb_cont(tbb::global_control::max_allowed_parallelism,
thread_count);
install_signal_handler();
if (!ctx.arg.directory.empty())
@@ -418,6 +402,19 @@ static int elf_main(int argc, char **argv) {
Fatal(ctx) << "chdir failed: " << ctx.arg.directory
<< ": " << errno_string();
if (ctx.arg.relocatable) {
combine_objects(ctx, file_args);
return 0;
}
// Fork a subprocess unless --no-fork is given.
std::function<void()> on_complete;
if (ctx.arg.fork)
on_complete = fork_child();
tbb::global_control tbb_cont(tbb::global_control::max_allowed_parallelism,
ctx.arg.thread_count);
// Handle --wrap options if any.
for (std::string_view name : ctx.arg.wrap)
get_symbol(ctx, name)->wrap = true;
@@ -427,31 +424,12 @@ static int elf_main(int argc, char **argv) {
for (std::string_view name : *ctx.arg.retain_symbols_file)
get_symbol(ctx, name)->write_to_symtab = true;
// Preload input files
std::function<void()> on_complete;
std::function<void()> wait_for_client;
if (ctx.arg.preload)
daemonize(ctx, &wait_for_client, &on_complete);
else if (ctx.arg.fork)
on_complete = fork_child();
for (std::string_view arg : ctx.arg.trace_symbol)
get_symbol(ctx, arg)->traced = true;
// Parse input files
read_input_files(ctx, file_args);
if (ctx.arg.preload) {
wait_for_client();
if (!reload_input_files(ctx)) {
std::vector<char *> args(argv, argv + argc);
args.push_back((char *)"--no-preload");
args.push_back(nullptr);
return elf_main<E>(argc + 1, args.data());
}
}
// Uniquify shared object files by soname
{
std::unordered_set<std::string_view> seen;
@@ -488,6 +466,10 @@ static int elf_main(int argc, char **argv) {
// Set is_import and is_export bits for each symbol.
compute_import_export(ctx);
// Read address-significant section information.
if (ctx.arg.icf && !ctx.arg.icf_all)
mark_addrsig(ctx);
// Garbage-collect unreachable sections.
if (ctx.arg.gc_sections)
gc_sections(ctx);
@@ -521,6 +503,12 @@ static int elf_main(int argc, char **argv) {
if (ctx.arg.z_cet_report != CET_REPORT_NONE)
check_cet_errors(ctx);
// Handle `-z execstack-if-needed`.
if (ctx.arg.z_execstack_if_needed)
for (ObjectFile<E> *file : ctx.objs)
if (file->needs_executable_stack)
ctx.arg.z_execstack = true;
// If we are linking a .so file, remaining undefined symbols does
// not cause a linker error. Instead, they are treated as if they
// were imported symbols.
@@ -532,6 +520,12 @@ static int elf_main(int argc, char **argv) {
// Beyond this point, no new symbols will be added to the result.
// Handle --print-dependencies
if (ctx.arg.print_dependencies == 1)
print_dependencies(ctx);
else if (ctx.arg.print_dependencies == 2)
print_dependencies_full(ctx);
// Handle -repro
if (ctx.arg.repro)
write_repro_file(ctx);
@@ -557,16 +551,6 @@ static int elf_main(int argc, char **argv) {
if (ctx.arg.shuffle_sections != SHUFFLE_SECTIONS_NONE)
shuffle_sections(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
// Sort sections by section attributes so that we'll have to
// create as few segments as possible.
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
return get_section_rank(ctx, a) < get_section_rank(ctx, b);
});
// Copy string referred by .dynamic to .dynstr.
for (SharedFile<E> *file : ctx.dsos)
ctx.dynstr->add_string(file->soname);
@@ -583,6 +567,16 @@ static int elf_main(int argc, char **argv) {
// .got.plt, .dynsym, .dynstr, etc.
scan_rels(ctx);
// Compute sizes of output sections while assigning offsets
// within an output section to input sections.
compute_section_sizes(ctx);
// Sort sections by section attributes so that we'll have to
// create as few segments as possible.
sort(ctx.chunks, [&](Chunk<E> *a, Chunk<E> *b) {
return get_section_rank(ctx, a) < get_section_rank(ctx, b);
});
// If --packed_dyn_relocs=relr was given, base relocations are stored
// to a .relr.dyn section in a compressed form. Construct a compressed
// relocations now so that we can fix section sizes and file layout.
@@ -594,6 +588,10 @@ static int elf_main(int argc, char **argv) {
// be added to .dynsym.
ctx.dynsym->finalize(ctx);
// Print reports about undefined symbols, if needed.
if (ctx.arg.unresolved_symbols == UNRESOLVED_ERROR)
report_undef_errors(ctx);
// Fill .gnu.version_d section contents.
if (ctx.verdef)
ctx.verdef->construct(ctx);
@@ -624,12 +622,12 @@ static int elf_main(int argc, char **argv) {
chunk->update_shdr(ctx);
std::erase_if(ctx.chunks, [](Chunk<E> *chunk) {
return !chunk->is_output_section() && chunk->shdr.sh_size == 0;
return chunk->kind() != OUTPUT_SECTION && chunk->shdr.sh_size == 0;
});
// Set section indices.
for (i64 i = 0, shndx = 1; i < ctx.chunks.size(); i++)
if (!ctx.chunks[i]->is_header())
if (ctx.chunks[i]->kind() != HEADER)
ctx.chunks[i]->shndx = shndx++;
// Some types of section header refer other section by index.
@@ -640,12 +638,6 @@ static int elf_main(int argc, char **argv) {
// Assign offsets to output sections
i64 filesize = set_osec_offsets(ctx);
// On ARM64, we may need to create so-called "range extension thunks"
// to extend branch instructions reach, as they can jump only to
// ±128 MiB.
if constexpr (std::is_same_v<E, ARM64>)
filesize = create_range_extension_thunks(ctx);
// On RISC-V, branches are encode using multiple instructions so
// that they can jump to anywhere in ±2 GiB by default. They may
// be replaced with shorter instruction sequences if destinations
@@ -669,7 +661,8 @@ static int elf_main(int argc, char **argv) {
t_before_copy.stop();
// Create an output file
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0777);
ctx.output_file =
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0777);
ctx.buf = ctx.output_file->buf;
Timer t_copy(ctx, "copy");
@@ -685,12 +678,9 @@ static int elf_main(int argc, char **argv) {
chunk->copy_buf(ctx);
});
ctx.checkpoint();
report_undef_errors(ctx);
}
if constexpr (std::is_same_v<E, ARM64>)
write_thunks(ctx);
if constexpr (std::is_same_v<E, ARM32>)
sort_arm_exidx(ctx);
@@ -707,12 +697,11 @@ static int elf_main(int argc, char **argv) {
// Zero-clear paddings between sections
clear_padding(ctx);
if (ctx.buildid) {
Timer t(ctx, "build_id");
if (ctx.buildid)
ctx.buildid->write_buildid(ctx);
}
t_copy.stop();
ctx.checkpoint();
// Close the output file. This is the end of the linker's main job.
ctx.output_file->close(ctx);
@@ -721,12 +710,6 @@ static int elf_main(int argc, char **argv) {
if (!ctx.arg.dependency_file.empty())
write_dependency_file(ctx);
// Handle --print-dependencies
if (ctx.arg.print_dependencies == 1)
print_dependencies(ctx);
else if (ctx.arg.print_dependencies == 2)
print_dependencies_full(ctx);
if (ctx.has_lto_object)
lto_cleanup(ctx);
@@ -753,20 +736,21 @@ static int elf_main(int argc, char **argv) {
for (std::function<void()> &fn : ctx.on_exit)
fn();
ctx.checkpoint();
return 0;
}
int main(int argc, char **argv) {
#if MOLD_DEBUG_ARM64_ONLY
return elf_main<ARM64>(argc, argv);
#else
return elf_main<X86_64>(argc, argv);
#endif
}
#define INSTANTIATE(E) \
template void read_file(Context<E> &, MappedFile<Context<E>> *);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+5 -6
View File
@@ -40,6 +40,9 @@ static Map<E> get_map(Context<E> &ctx) {
}
});
if (map.size() <= 1)
return map;
tbb::parallel_for(map.range(), [](const typename Map<E>::range_type &range) {
for (auto it = range.begin(); it != range.end(); it++) {
std::vector<Symbol<E> *> &vec = it->second;
@@ -72,7 +75,7 @@ void print_map(Context<E> &ctx) {
<< std::setw(6) << (u64)osec->shdr.sh_addralign
<< " " << osec->name << "\n";
if (!osec->is_output_section())
if (osec->kind() != OUTPUT_SECTION)
continue;
std::span<InputSection<E> *> members = ((OutputSection<E> *)osec)->members;
@@ -109,10 +112,6 @@ void print_map(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void print_map(Context<E> &ctx);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+29 -26
View File
@@ -1,5 +1,6 @@
#define _GNU_SOURCE 1
#include <alloca.h>
#include <dlfcn.h>
#include <spawn.h>
#include <stdarg.h>
@@ -31,13 +32,25 @@ static void debug_print(char *fmt, ...) {
va_end(ap);
}
static void get_args(va_list *ap, int argc, char **argv) {
for (int i = 1; i < argc - 1; i++) {
char *arg = va_arg(*ap, char *);
if (!arg)
break;
argv[i] = arg;
}
static int count_args(va_list *ap) {
va_list aq;
va_copy(aq, *ap);
int i = 0;
while (va_arg(aq, char *))
i++;
va_end(aq);
return i;
}
static void copy_args(char **argv, const char *arg0, va_list *ap) {
int i = 1;
char *arg;
while ((arg = va_arg(*ap, char *)))
argv[i++] = arg;
((const char **)argv)[0] = arg0;
((const char **)argv)[i] = NULL;
}
static bool is_ld(const char *path) {
@@ -52,10 +65,8 @@ static bool is_ld(const char *path) {
int execvpe(const char *file, char *const *argv, char *const *envp) {
debug_print("execvpe %s\n", file);
if (!strcmp(file, "ld") || is_ld(file)) {
if (!strcmp(file, "ld") || is_ld(file))
file = get_mold_path();
((const char **)argv)[0] = file;
}
for (int i = 0; envp[i]; i++)
putenv(envp[i]);
@@ -66,12 +77,8 @@ int execvpe(const char *file, char *const *argv, char *const *envp) {
int execve(const char *path, char *const *argv, char *const *envp) {
debug_print("execve %s\n", path);
if (is_ld(path)) {
if (is_ld(path))
path = get_mold_path();
((const char **)argv)[0] = path;
}
typeof(execve) *real = dlsym(RTLD_NEXT, "execve");
return real(path, argv, envp);
}
@@ -79,8 +86,8 @@ int execve(const char *path, char *const *argv, char *const *envp) {
int execl(const char *path, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
va_end(ap);
return execve(path, argv, environ);
}
@@ -88,8 +95,8 @@ int execl(const char *path, const char *arg0, ...) {
int execlp(const char *file, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
va_end(ap);
return execvpe(file, argv, environ);
}
@@ -97,8 +104,8 @@ int execlp(const char *file, const char *arg0, ...) {
int execle(const char *path, const char *arg0, ...) {
va_list ap;
va_start(ap, arg0);
char *argv[4096] = {(char *)arg0};
get_args(&ap, 4096, argv);
char **argv = alloca((count_args(&ap) + 2) * sizeof(char *));
copy_args(argv, arg0, &ap);
char **env = va_arg(ap, char **);
va_end(ap);
return execve(path, argv, env);
@@ -117,12 +124,8 @@ int posix_spawn(pid_t *pid, const char *path,
const posix_spawnattr_t *attrp,
char *const *argv, char *const *envp) {
debug_print("posix_spawn %s\n", path);
if (is_ld(path)) {
if (is_ld(path))
path = get_mold_path();
((const char **)argv)[0] = path;
}
typeof(posix_spawn) *real = dlsym(RTLD_NEXT, "posix_spawn");
return real(pid, path, file_actions, attrp, argv, envp);
}
+203 -296
View File
File diff suppressed because it is too large. Load diff
+274 -472
View File
File diff suppressed because it is too large. Load diff
+54 -16
View File
@@ -82,6 +82,7 @@ void create_synthetic_sections(Context<E> &ctx) {
ctx.versym = push(new VersymSection<E>);
ctx.verneed = push(new VerneedSection<E>);
ctx.note_package = push(new NotePackageSection<E>);
ctx.note_property = push(new NotePropertySection<E>);
if constexpr (std::is_same_v<E, ARM32>) {
@@ -128,8 +129,6 @@ static void mark_live_objects(Context<E> &ctx) {
template <typename E>
void do_resolve_symbols(Context<E> &ctx) {
Timer t(ctx, "do_resolve_symbols");
auto for_each_file = [&](std::function<void(InputFile<E> *)> fn) {
tbb::parallel_for_each(ctx.objs, fn);
tbb::parallel_for_each(ctx.dsos, fn);
@@ -163,7 +162,7 @@ void do_resolve_symbols(Context<E> &ctx) {
template <typename E>
void resolve_symbols(Context<E> &ctx) {
Timer t(ctx, "do_resolve_symbols");
Timer t(ctx, "resolve_symbols");
std::vector<ObjectFile<E> *> objs = ctx.objs;
std::vector<SharedFile<E> *> dsos = ctx.dsos;
@@ -207,7 +206,7 @@ void resolve_symbols(Context<E> &ctx) {
tbb::parallel_for_each(ctx.dsos, [&](SharedFile<E> *file) {
file->clear_symbols();
file->is_alive = true;
file->is_alive = !file->is_needed;
});
do_resolve_symbols(ctx);
@@ -365,7 +364,8 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
obj->priority = 1;
auto add = [&](std::string_view name, u8 st_type = STT_NOTYPE) {
ElfSym<E> esym = {};
ElfSym<E> esym;
memset(&esym, 0, sizeof(esym));
esym.st_type = st_type;
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_GLOBAL;
@@ -431,7 +431,8 @@ ObjectFile<E> *create_internal_file(Context<E> &ctx) {
for (i64 i = 0; i < ctx.arg.defsyms.size(); i++) {
Symbol<E> *sym = ctx.arg.defsyms[i].first;
ElfSym<E> esym = {};
ElfSym<E> esym;
memset(&esym, 0, sizeof(esym));
esym.st_type = STT_NOTYPE;
esym.st_shndx = SHN_ABS;
esym.st_bind = STB_GLOBAL;
@@ -550,7 +551,7 @@ R"(# This is an output of the mold linker's --print-dependencies=full option.
std::unordered_set<void *> visited;
for (ElfRel<E> &r : isec->get_rels(ctx)) {
for (const ElfRel<E> &r : isec->get_rels(ctx)) {
if (r.r_type == E::R_NONE)
continue;
@@ -753,7 +754,7 @@ void shuffle_sections(Context<E> &ctx) {
if (ctx.arg.shuffle_sections_seed)
seed = *ctx.arg.shuffle_sections_seed;
else
seed = std::random_device()();
seed = ((u64)std::random_device()() << 32) | std::random_device()();
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
@@ -806,6 +807,11 @@ void compute_section_sizes(Context<E> &ctx) {
tbb::parallel_for_each(ctx.output_sections,
[&](std::unique_ptr<OutputSection<E>> &osec) {
// This pattern will be processed in the next loop.
if constexpr (std::is_same_v<E, ARM64>)
if (osec->shdr.sh_flags & SHF_EXECINSTR)
return;
// Since one output section may contain millions of input sections,
// we first split input sections into groups and assign offsets to
// groups.
@@ -845,6 +851,16 @@ void compute_section_sizes(Context<E> &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.
// In this case, we compute the sizes of sections while inserting thunks.
// This pass cannot be parallelized (`create_range_extension_thunks` is
// parallelized internally, but the function itself is not thread-safe.
if constexpr (std::is_same_v<E, ARM64>)
for (std::unique_ptr<OutputSection<E>> &osec : ctx.output_sections)
if (osec->shdr.sh_flags & SHF_EXECINSTR)
create_range_extension_thunks(ctx, *osec);
}
template <typename E>
@@ -1048,8 +1064,8 @@ void apply_version_script(Context<E> &ctx) {
}
// Otherwise, use glob pattern matchers.
VersionMatcher matcher;
VersionMatcher cpp_matcher;
MultiGlob matcher;
MultiGlob cpp_matcher;
for (VersionPattern &v : ctx.version_patterns) {
if (v.is_cpp) {
@@ -1061,6 +1077,9 @@ void apply_version_script(Context<E> &ctx) {
}
}
matcher.compile();
cpp_matcher.compile();
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->get_global_syms()) {
if (sym->file != file)
@@ -1093,6 +1112,8 @@ void parse_symbol_version(Context<E> &ctx) {
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = 0; i < file->symbols.size() - file->first_global; i++) {
// Match VERSION part of symbol foo@VERSION with version definitions.
// The symbols' VERSION parts are in file->symvers.
if (!file->symvers[i])
continue;
@@ -1118,6 +1139,16 @@ void parse_symbol_version(Context<E> &ctx) {
sym->ver_idx = it->second;
if (!is_default)
sym->ver_idx |= VERSYM_HIDDEN;
// If both symbol `foo` and `foo@VERSION` are defined, `foo@VERSION`
// hides `foo` so that all references to `foo` are resolved to a
// versioned symbol. Likewise, if `foo@VERSION` and `foo@@VERSION` are
// defined, the default one takes precedence.
Symbol<E> *sym2 = get_symbol(ctx, sym->name());
if (sym2->file == file && !file->symvers[sym2->sym_idx - file->first_global])
if (sym2->ver_idx == ctx.default_version ||
(sym2->ver_idx & ~VERSYM_HIDDEN) == (sym->ver_idx & ~VERSYM_HIDDEN))
sym2->ver_idx = VER_NDX_LOCAL;
}
});
}
@@ -1144,12 +1175,14 @@ void compute_import_export(Context<E> &ctx) {
sym->ver_idx == VER_NDX_LOCAL)
continue;
// If we are using a symbol in a DSO, we need to import it at runtime.
if (sym->file != file && sym->file->is_dso) {
std::scoped_lock lock(sym->mu);
sym->is_imported = true;
continue;
}
// If we are creating a DSO, all global symbols are exported by default.
if (sym->file == file) {
std::scoped_lock lock(sym->mu);
sym->is_exported = true;
@@ -1163,6 +1196,14 @@ void compute_import_export(Context<E> &ctx) {
});
}
template <typename E>
void mark_addrsig(Context<E> &ctx) {
Timer t(ctx, "mark_addrsig");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->fill_addrsig(ctx);
});
}
template <typename E>
void clear_padding(Context<E> &ctx) {
Timer t(ctx, "clear_padding");
@@ -1463,7 +1504,7 @@ void fix_synthetic_symbols(Context<E> &ctx) {
// _end, _etext, _edata and the like
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk->is_header())
if (chunk->kind() == HEADER)
continue;
if (chunk->shdr.sh_flags & SHF_ALLOC) {
@@ -1665,6 +1706,7 @@ void write_dependency_file(Context<E> &ctx) {
template void apply_version_script(Context<E> &); \
template void parse_symbol_version(Context<E> &); \
template void compute_import_export(Context<E> &); \
template void mark_addrsig(Context<E> &); \
template void clear_padding(Context<E> &); \
template i64 get_section_rank(Context<E> &, Chunk<E> *); \
template i64 set_osec_offsets(Context<E> &); \
@@ -1672,10 +1714,6 @@ void write_dependency_file(Context<E> &ctx) {
template i64 compress_debug_sections(Context<E> &); \
template void write_dependency_file(Context<E> &);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+18 -21
View File
@@ -28,6 +28,7 @@
#include "mold.h"
#include "../archive-file.h"
#include "../output-file.h"
#include <unordered_map>
#include <unordered_set>
@@ -298,7 +299,7 @@ void ROutputEhdr<E>::write_to(Context<E> &ctx) {
ElfEhdr<E> &hdr = *(ElfEhdr<E> *)(ctx.buf + this->out_shdr.sh_offset);
memcpy(&hdr.e_ident, "\177ELF", 4);
hdr.e_ident[EI_CLASS] = (E::word_size == 8) ? ELFCLASS64 : ELFCLASS32;
hdr.e_ident[EI_DATA] = E::is_le ? ELFDATA2LSB : ELFDATA2MSB;
hdr.e_ident[EI_DATA] = ELFDATA2LSB;
hdr.e_ident[EI_VERSION] = EV_CURRENT;
hdr.e_type = ET_REL;
hdr.e_machine = E::e_machine;
@@ -413,36 +414,36 @@ std::span<T> RObjectFile<E>::get_data(Context<E> &ctx, const ElfShdr<E> &shdr) {
template <typename E>
static std::vector<std::unique_ptr<RObjectFile<E>>>
open_files(Context<E> &ctx, std::span<std::string_view> args) {
open_files(Context<E> &ctx, std::span<std::string> args) {
std::vector<std::unique_ptr<RObjectFile<E>>> files;
bool whole_archive = false;
while (!args.empty()) {
if (read_flag(args, "whole-archive")) {
const std::string &arg = args[0];
args = args.subspan(1);
if (arg == "--whole-archive") {
whole_archive = true;
continue;
}
if (read_flag(args, "no-whole-archive")) {
if (arg == "--no-whole-archive") {
whole_archive = false;
continue;
}
std::string_view arg;
if (read_arg(ctx, args, arg, "version-script") ||
read_arg(ctx, args, arg, "dynamic-list"))
if (arg.starts_with("--version-script=") ||
arg.starts_with("--dynamic-list="))
continue;
MappedFile<Context<E>> *mf = nullptr;
if (read_arg(ctx, args, arg, "l")) {
mf = find_library(ctx, std::string(arg));
if (arg == "-l") {
mf = find_library(ctx, arg.substr(2));
} else {
if (arg.starts_with('-'))
continue;
arg = args[0];
args = args.subspan(1);
mf = MappedFile<Context<E>>::must_open(ctx, std::string(arg));
mf = MappedFile<Context<E>>::must_open(ctx, arg);
}
switch (get_file_type(mf)) {
@@ -482,7 +483,7 @@ static bool contains(std::unordered_set<std::string_view> &a,
}
template <typename E>
void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
void combine_objects(Context<E> &ctx, std::span<std::string> file_args) {
// Read object files
std::vector<std::unique_ptr<RObjectFile<E>>> files = open_files(ctx, file_args);
@@ -574,8 +575,8 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
// Open an output file
i64 filesize = assign_offsets(ctx);
std::unique_ptr<OutputFile<E>> out =
OutputFile<E>::open(ctx, ctx.arg.output, filesize, 0666);
std::unique_ptr<OutputFile<Context<E>>> out =
OutputFile<Context<E>>::open(ctx, ctx.arg.output, filesize, 0666);
memset(out->buf, 0, filesize);
ctx.buf = out->buf;
@@ -586,12 +587,8 @@ void combine_objects(Context<E> &ctx, std::span<std::string_view> file_args) {
}
#define INSTANTIATE(E) \
template void combine_objects(Context<E> &, std::span<std::string_view>);
template void combine_objects(Context<E> &, std::span<std::string>);
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+8 -205
View File
@@ -3,17 +3,12 @@
#include <filesystem>
#include <signal.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/un.h>
#include <sys/wait.h>
#include <unistd.h>
#define DAEMON_TIMEOUT 30
namespace mold::elf {
// Exiting from a program with large memory usage is slow --
@@ -60,198 +55,8 @@ std::function<void()> fork_child() {
};
}
static std::string base64(u8 *data, u64 size) {
static const char chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+_";
std::ostringstream out;
auto encode = [&](u32 x) {
out << chars[x & 0b111111]
<< chars[(x >> 6) & 0b111111]
<< chars[(x >> 12) & 0b111111]
<< chars[(x >> 18) & 0b111111];
};
i64 i = 0;
for (; i < size - 3; i += 3)
encode((data[i + 2] << 16) | (data[i + 1] << 8) | data[i]);
if (i == size - 1)
encode(data[i]);
else if (i == size - 2)
encode((data[i + 1] << 8) | data[i]);
return out.str();
}
static std::string compute_sha256(std::span<std::string_view> argv) {
SHA256_CTX sha;
SHA256_Init(&sha);
for (std::string_view arg : argv) {
if (arg != "-preload" && arg != "--preload") {
SHA256_Update(&sha, arg.data(), arg.size());
char buf[] = {0};
SHA256_Update(&sha, buf, 1);
}
}
u8 digest[SHA256_SIZE];
SHA256_Final(digest, &sha);
return base64(digest, SHA256_SIZE);
}
template <typename E>
static void send_fd(Context<E> &ctx, i64 conn, i64 fd) {
struct iovec iov;
char dummy = '1';
iov.iov_base = &dummy;
iov.iov_len = 1;
struct msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = CMSG_LEN(sizeof(int));
struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
*(int *)CMSG_DATA(cmsg) = fd;
if (sendmsg(conn, &msg, 0) == -1)
Fatal(ctx) << "sendmsg failed: " << errno_string();
}
template <typename E>
static i64 recv_fd(Context<E> &ctx, i64 conn) {
struct iovec iov;
char buf[1];
iov.iov_base = buf;
iov.iov_len = sizeof(buf);
struct msghdr msg = {};
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
char cmsgbuf[CMSG_SPACE(sizeof(int))];
msg.msg_control = (caddr_t)cmsgbuf;
msg.msg_controllen = sizeof(cmsgbuf);
i64 len = recvmsg(conn, &msg, 0);
if (len <= 0)
Fatal(ctx) << "recvmsg failed: " << errno_string();
struct cmsghdr *cmsg;
cmsg = CMSG_FIRSTHDR(&msg);
return *(int *)CMSG_DATA(cmsg);
}
template <typename E>
void try_resume_daemon(Context<E> &ctx) {
i64 conn = socket(AF_UNIX, SOCK_STREAM, 0);
if (conn == -1)
Fatal(ctx) << "socket failed: " << errno_string();
std::string path = "/tmp/mold-" + compute_sha256(ctx.cmdline_args);
struct sockaddr_un name = {};
name.sun_family = AF_UNIX;
memcpy(name.sun_path, path.data(), path.size());
if (connect(conn, (struct sockaddr *)&name, sizeof(name)) != 0) {
close(conn);
return;
}
send_fd(ctx, conn, STDOUT_FILENO);
send_fd(ctx, conn, STDERR_FILENO);
char buf[1];
i64 r = read(conn, buf, 1);
close(conn);
if (r == 1)
exit(0);
}
template <typename E>
void daemonize(Context<E> &ctx, std::function<void()> *wait_for_client,
std::function<void()> *on_complete) {
if (daemon(1, 0) == -1)
Fatal(ctx) << "daemon failed: " << errno_string();
i64 sock = socket(AF_UNIX, SOCK_STREAM, 0);
if (sock == -1)
Fatal(ctx) << "socket failed: " << errno_string();
socket_tmpfile =
strdup(("/tmp/mold-" + compute_sha256(ctx.cmdline_args)).c_str());
struct sockaddr_un name = {};
name.sun_family = AF_UNIX;
strcpy(name.sun_path, socket_tmpfile);
u32 orig_mask = umask(0177);
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1) {
if (errno != EADDRINUSE)
Fatal(ctx) << "bind failed: " << errno_string();
unlink(socket_tmpfile);
if (bind(sock, (struct sockaddr *)&name, sizeof(name)) == -1)
Fatal(ctx) << "bind failed: " << errno_string();
}
umask(orig_mask);
if (listen(sock, 0) == -1)
Fatal(ctx) << "listen failed: " << errno_string();
static i64 conn = -1;
*wait_for_client = [=, &ctx] {
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock, &rfds);
struct timeval tv;
tv.tv_sec = DAEMON_TIMEOUT;
tv.tv_usec = 0;
i64 res = select(sock + 1, &rfds, NULL, NULL, &tv);
if (res == -1)
Fatal(ctx) << "select failed: " << errno_string();
if (res == 0) {
std::cout << "timeout\n";
exit(0);
}
conn = accept(sock, NULL, NULL);
if (conn == -1)
Fatal(ctx) << "accept failed: " << errno_string();
unlink(socket_tmpfile);
dup2(recv_fd(ctx, conn), STDOUT_FILENO);
dup2(recv_fd(ctx, conn), STDERR_FILENO);
};
*on_complete = [=] {
char buf[] = {1};
int n = write(conn, buf, 1);
assert(n == 1);
};
}
static std::string get_self_path() {
return std::filesystem::read_symlink("/proc/self/exe");
}
template <typename E>
std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
static std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
// Look for mold-wrapper.so from the same directory as the executable is.
std::filesystem::path path = self.parent_path() / "mold-wrapper.so";
std::error_code ec;
@@ -277,13 +82,18 @@ std::string find_dso(Context<E> &ctx, std::filesystem::path self) {
template <typename E>
[[noreturn]]
void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
#ifdef __APPLE__
// LD_PRELOAD is not supported on macOS
Fatal(ctx) << "mold -run is not supported on macOS";
#endif
std::string_view arg1 = argv[1];
assert(arg1 == "-run" || arg1 == "--run");
if (!argv[2])
Fatal(ctx) << "-run: argument missing";
// Get the mold-wrapper.so path
std::string self = get_self_path();
std::string self = std::filesystem::read_symlink("/proc/self/exe");
std::string dso_path = find_dso(ctx, self);
// Set environment variables
@@ -307,15 +117,8 @@ void process_run_subcommand(Context<E> &ctx, int argc, char **argv) {
}
#define INSTANTIATE(E) \
template void try_resume_daemon(Context<E> &); \
template void daemonize(Context<E> &, std::function<void()> *, \
std::function<void()> *); \
template void process_run_subcommand(Context<E> &, int, char **)
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
INSTANTIATE_ALL;
} // namespace mold::elf
+3 -3
View File
@@ -41,7 +41,7 @@ inline bool is_gcc_lto_obj(MappedFile<C> *mf) {
continue;
std::span<ElfSym<E>> elf_syms{(ElfSym<E> *)(data + sec.sh_offset),
sec.sh_size / sizeof(ElfSym<E>)};
(size_t)sec.sh_size / sizeof(ElfSym<E>)};
auto skip = [](u8 type) {
return type == STT_NOTYPE || type == STT_FILE || type == STT_SECTION;
@@ -73,7 +73,7 @@ FileType get_file_type(MappedFile<C> *mf) {
return FileType::EMPTY;
if (data.starts_with("\177ELF")) {
switch (*(u16 *)(data.data() + 16)) {
switch (*(ul16 *)(data.data() + 16)) {
case 1: {
// ET_REL
elf::Elf32Ehdr &ehdr = *(elf::Elf32Ehdr *)data.data();
@@ -95,7 +95,7 @@ FileType get_file_type(MappedFile<C> *mf) {
}
if (data.starts_with("\xcf\xfa\xed\xfe")) {
switch (*(u32 *)(data.data() + 12)) {
switch (*(ul32 *)(data.data() + 12)) {
case 1: // MH_OBJECT
return FileType::MACH_OBJ;
case 6: // MH_DYLIB
+11 -13
View File
@@ -2,9 +2,9 @@
#include <cstring>
namespace mold::elf {
namespace mold {
std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
std::optional<Glob> Glob::compile(std::string_view pat) {
std::vector<Element> vec;
while (!pat.empty()) {
@@ -20,8 +20,7 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
// [a-czg-i]: a, b, c, z, g, h, or i
// [^a-z]: Any character except lowercase letters
vec.push_back({BRACKET});
std::vector<bool> &bitmap = vec.back().bitmap;
bitmap.resize(256);
std::bitset<256> &bitset = vec.back().bitset;
bool negate = false;
if (!pat.empty() && pat[0] == '^') {
@@ -60,9 +59,9 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
return {};
for (i64 i = start; i <= end; i++)
bitmap[i] = true;
bitset[i] = true;
} else {
bitmap[(u8)pat[0]] = true;
bitset[(u8)pat[0]] = true;
pat = pat.substr(1);
}
}
@@ -71,8 +70,7 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
return {};
if (negate)
for (i64 i = 0; i < 256; i++)
bitmap[i] = !bitmap[i];
bitset.flip();
break;
}
case '?':
@@ -89,14 +87,14 @@ std::optional<GlobPattern> GlobPattern::compile(std::string_view pat) {
}
}
return {GlobPattern{std::move(vec)}};
return {Glob{std::move(vec)}};
}
bool GlobPattern::match(std::string_view str) {
bool Glob::match(std::string_view str) {
return do_match(str, elements);
}
bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
bool Glob::do_match(std::string_view str, std::span<Element> elements) {
while (!elements.empty()) {
Element &e = elements[0];
elements = elements.subspan(1);
@@ -138,7 +136,7 @@ bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
str = str.substr(1);
break;
case BRACKET:
if (str.empty() || !e.bitmap[str[0]])
if (str.empty() || !e.bitset[str[0]])
return false;
str = str.substr(1);
break;
@@ -148,4 +146,4 @@ bool GlobPattern::do_match(std::string_view str, std::span<Element> elements) {
return str.empty();
}
} // namespace mold::elf
} // namespace mold
+35
View File
@@ -0,0 +1,35 @@
#!/bin/bash -x
set -e
source /etc/os-release
# The first line for each distro installs a build dependency.
# The second line installs extra packages for `make test`.
case "$ID$VERSION_ID" in
ubuntu20.04)
apt install -y git cmake libssl-dev zlib1g-dev gcc g++ g++-10
apt-get install -y file bsdmainutils
;;
ubuntu22.04 | debian11)
apt install -y git cmake libssl-dev zlib1g-dev gcc g++
apt-get install -y file bsdmainutils
;;
fedora*)
dnf install -y git gcc-g++ cmake openssl-devel zlib-devel
dnf install -y glibc-static file libstdc++-static diffutils
;;
opensuse-leap*)
zypper install -y git make cmake zlib-devel libopenssl-devel gcc-c++ gcc11-c++
zypper install -y glibc-devel-static tar diffutils util-linux
;;
opensuse-tumbleweed*)
zypper install -y git make cmake zlib-devel libopenssl-devel gcc-c++
zypper install -y glibc-devel-static tar diffutils util-linux
;;
gentoo*)
emerge dev-vcs/git dev-util/cmake sys-libs/zlib
;;
*)
echo "Error: don't know anything about build dependencies on $ID $VERSION_ID"
exit 1
esac
+167
View File
@@ -0,0 +1,167 @@
// This file defines integral types for file input/output. You should
// use these types instead of the plain integers (such as u32 or i32)
// when reading from/writing to an mmap'ed file area.
//
// Here is why you need these types. In C/C++, all data accesses must
// be aligned. That is, if you read an N byte value from memory, the
// address of that value must be a multiple of N. For example, reading
// an u32 value from address 16 is legal, while reading from 18 is not
// (since 18 is not a multiple of 4.) An unaligned access yields an
// undefined behavior.
//
// All data members of the ELF data structures are naturally aligned,
// so it may look like we don't need the integral types defined in
// this file to access them. But there's a catch; if an object file is
// in an archive file (a .a file), the beginning of the file is
// guaranteed to be aligned only to a 2 bytes boundary, because ar
// aligns each member only to a 2 byte boundary. Therefore, any data
// members larger than 2 bytes may be unaligned in an mmap'ed memory,
// and thus you always need to use the integral types in this file to
// access it.
#pragma once
#include <cstdint>
#include <cstring>
#ifdef __BIG_ENDIAN__
#error "mold does not support big-endian hosts"
#endif
namespace mold {
template <typename T, size_t SIZE = sizeof(T)>
class LittleEndian {
public:
LittleEndian() { *this = 0; }
LittleEndian(T x) { *this = x; }
operator T() const {
T x = 0;
memcpy(&x, val, SIZE);
return x;
}
LittleEndian &operator=(T x) {
memcpy(&val, &x, SIZE);
return *this;
}
LittleEndian &operator++() {
return *this = *this + 1;
}
LittleEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
LittleEndian &operator--() {
return *this = *this - 1;
}
LittleEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
LittleEndian &operator+=(T x) {
return *this = *this + x;
}
LittleEndian &operator-=(T x) {
return *this = *this - x;
}
LittleEndian &operator&=(T x) {
return *this = *this & x;
}
LittleEndian &operator|=(T x) {
return *this = *this | x;
}
private:
uint8_t val[SIZE];
};
using il16 = LittleEndian<int16_t>;
using il32 = LittleEndian<int32_t>;
using il64 = LittleEndian<int64_t>;
using ul16 = LittleEndian<uint16_t>;
using ul24 = LittleEndian<uint32_t, 3>;
using ul32 = LittleEndian<uint32_t>;
using ul64 = LittleEndian<uint64_t>;
template <typename T>
class BigEndian {
public:
BigEndian() = delete;
operator T() const {
T x;
memcpy(&x, val, sizeof(T));
return bswap(x);
}
BigEndian &operator=(T x) {
x = bswap(x);
memcpy(&val, &x, sizeof(T));
return *this;
}
BigEndian &operator++() {
return *this = *this + 1;
}
BigEndian operator++(int) {
T ret = *this;
*this = *this + 1;
return ret;
}
BigEndian &operator--() {
return *this = *this - 1;
}
BigEndian operator--(int) {
T ret = *this;
*this = *this - 1;
return ret;
}
BigEndian &operator+=(T x) {
return *this = *this + x;
}
BigEndian &operator&=(T x) {
return *this = *this & x;
}
BigEndian &operator|=(T x) {
return *this = *this | x;
}
private:
uint8_t val[sizeof(T)];
static T bswap(T x) {
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 ib16 = BigEndian<int16_t>;
using ib32 = BigEndian<int32_t>;
using ib64 = BigEndian<int64_t>;
using ub16 = BigEndian<uint16_t>;
using ub32 = BigEndian<uint32_t>;
using ub64 = BigEndian<uint64_t>;
} // namespace mold
+279 -71
View File
@@ -1,11 +1,11 @@
#include "mold.h"
#include <tbb/parallel_for.h>
#include <tbb/parallel_for_each.h>
namespace mold::macho {
// Returns [hi:lo] bits of val.
static u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
}
using E = ARM64;
static u64 page(u64 val) {
return val & 0xffff'ffff'ffff'f000;
@@ -16,8 +16,8 @@ static u64 encode_page(u64 val) {
}
template <>
void StubsSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *buf = (u32 *)(ctx.buf + this->hdr.offset);
void StubsSection<E>::copy_buf(Context<E> &ctx) {
ul32 *buf = (ul32 *)(ctx.buf + this->hdr.offset);
for (i64 i = 0; i < syms.size(); i++) {
static const u32 insn[] = {
@@ -26,10 +26,10 @@ void StubsSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
0xd61f0200, // br x16
};
static_assert(sizeof(insn) == ARM64::stub_size);
static_assert(sizeof(insn) == E::stub_size);
u64 la_addr = ctx.lazy_symbol_ptr.hdr.addr + ARM64::word_size * i;
u64 this_addr = this->hdr.addr + ARM64::stub_size * i;
u64 la_addr = ctx.lazy_symbol_ptr.hdr.addr + E::word_size * i;
u64 this_addr = this->hdr.addr + E::stub_size * i;
memcpy(buf, insn, sizeof(insn));
buf[0] |= encode_page(page(la_addr) - page(this_addr));
@@ -39,9 +39,9 @@ void StubsSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
}
template <>
void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
u32 *start = (u32 *)(ctx.buf + this->hdr.offset);
u32 *buf = start;
void StubHelperSection<E>::copy_buf(Context<E> &ctx) {
ul32 *start = (ul32 *)(ctx.buf + this->hdr.offset);
ul32 *buf = start;
static const u32 insn0[] = {
0x90000011, // adrp x17, $__dyld_private@PAGE
@@ -52,14 +52,14 @@ void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
0xd61f0200, // br x16
};
static_assert(sizeof(insn0) == ARM64::stub_helper_hdr_size);
static_assert(sizeof(insn0) == E::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
u64 dyld_private = get_symbol(ctx, "__dyld_private")->get_addr(ctx);
buf[0] |= encode_page(page(dyld_private) - page(this->hdr.addr));
buf[1] |= bits(dyld_private, 11, 0) << 10;
u64 stub_binder = get_symbol(ctx, "dyld_stub_binder")->get_addr(ctx);
u64 stub_binder = get_symbol(ctx, "dyld_stub_binder")->get_got_addr(ctx);
buf[3] |= encode_page(page(stub_binder) - page(this->hdr.addr - 12));
buf[4] |= bits(stub_binder, 11, 0) << 10;
@@ -72,7 +72,7 @@ void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
0x00000000, // addr: .long <idx>
};
static_assert(sizeof(insn) == ARM64::stub_helper_size);
static_assert(sizeof(insn) == E::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
buf[1] |= bits((start - buf - 1) * 4, 27, 2);
@@ -81,31 +81,32 @@ void StubHelperSection<ARM64>::copy_buf(Context<ARM64> &ctx) {
}
}
static i64 read_addend(u8 *buf, const MachRel &r) {
if (r.p2size == 2)
return *(i32 *)(buf + r.offset);
if (r.p2size == 3)
return *(i64 *)(buf + r.offset);
unreachable();
}
static Relocation<ARM64>
read_reloc(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
static Relocation<E>
read_reloc(Context<E> &ctx, ObjectFile<E> &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]);
switch (MachRel &r = rels[idx]; r.p2size) {
case 2:
addend = *(il32 *)((u8 *)file.mf->data + hdr.offset + r.offset);
break;
case 3:
addend = *(il64 *)((u8 *)file.mf->data + hdr.offset + r.offset);
break;
default:
unreachable();
}
break;
case ARM64_RELOC_ADDEND:
addend = rels[idx++].offset;
addend = rels[idx++].idx;
break;
}
MachRel &r = rels[idx];
Relocation<ARM64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
Relocation<E> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
if (r.is_extern) {
rel.sym = file.syms[r.idx];
@@ -113,13 +114,8 @@ read_reloc(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
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);
u64 addr = r.is_pcrel ? (hdr.addr + r.offset + addend) : addend;
Subsection<E> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
@@ -129,10 +125,11 @@ read_reloc(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
}
template <>
std::vector<Relocation<ARM64>>
read_relocations(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
std::vector<Relocation<E>>
read_relocations(Context<E> &ctx, ObjectFile<E> &file,
const MachSection &hdr) {
std::vector<Relocation<ARM64>> vec;
std::vector<Relocation<E>> vec;
vec.reserve(hdr.nreloc);
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++)
@@ -141,13 +138,26 @@ read_relocations(Context<ARM64> &ctx, ObjectFile<ARM64> &file,
}
template <>
void Subsection<ARM64>::scan_relocations(Context<ARM64> &ctx) {
for (Relocation<ARM64> &r : get_rels()) {
Symbol<ARM64> *sym = r.sym;
void Subsection<E>::scan_relocations(Context<E> &ctx) {
for (Relocation<E> &r : get_rels()) {
Symbol<E> *sym = r.sym;
if (!sym)
continue;
if (sym->is_imported && sym->file->is_dylib)
((DylibFile<E> *)sym->file)->is_alive = true;
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
if (sym->is_imported) {
if (r.p2size != 3) {
Error(ctx) << this->isec << ": " << r << " relocation at offset 0x"
<< std::hex << r.offset << " against symbol `"
<< *sym << "' can not be used";
}
r.needs_dynrel = true;
}
break;
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
@@ -159,94 +169,108 @@ void Subsection<ARM64>::scan_relocations(Context<ARM64> &ctx) {
break;
}
if (sym->file && sym->file->is_dylib()) {
if (sym->is_imported)
sym->flags |= NEEDS_STUB;
((DylibFile<ARM64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<ARM64>::apply_reloc(Context<ARM64> &ctx, u8 *buf) {
std::span<Relocation<ARM64>> rels = get_rels();
void Subsection<E>::apply_reloc(Context<E> &ctx, u8 *buf) {
std::span<Relocation<E>> rels = get_rels();
for (i64 i = 0; i < rels.size(); i++) {
Relocation<ARM64> &r = rels[i];
Relocation<E> &r = rels[i];
u8 *loc = buf + r.offset;
i64 val = r.addend;
u64 pc = get_addr(ctx) + r.offset;
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
// Compute a relocated value.
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_BRANCH26:
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_PAGEOFF12:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
val += r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case ARM64_RELOC_SUBTRACTOR: {
Relocation<ARM64> s = rels[++i];
Relocation<E> s = rels[++i];
assert(s.type == ARM64_RELOC_UNSIGNED);
u64 val1 = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
u64 val2 = s.sym ? s.sym->get_addr(ctx) : s.subsec->get_addr(ctx);
val = val2 - val1;
val += val2 - val1;
break;
}
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_POINTER_TO_GOT:
val = r.sym->get_got_addr(ctx);
val += r.sym->get_got_addr(ctx);
break;
case ARM64_RELOC_TLVP_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
val = r.sym->get_tlv_addr(ctx);
val += r.sym->get_tlv_addr(ctx);
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
// An address of a thread-local variable is computed as an offset
// to the beginning of the first thread-local section.
if (isec.hdr.type == S_THREAD_LOCAL_VARIABLES)
val -= ctx.tls_begin;
// Write a computed value to the output buffer.
switch (r.type) {
case ARM64_RELOC_UNSIGNED:
case ARM64_RELOC_SUBTRACTOR:
case ARM64_RELOC_POINTER_TO_GOT:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
val -= pc;
if (r.p2size == 2)
*(i32 *)(buf + r.offset) = val;
*(ul32 *)loc = val;
else if (r.p2size == 3)
*(i64 *)(buf + r.offset) = val;
*(ul64 *)loc = val;
else
unreachable();
break;
case ARM64_RELOC_BRANCH26:
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
*(u32 *)(buf + r.offset) |= bits(val, 27, 2);
case ARM64_RELOC_BRANCH26: {
assert(r.is_pcrel);
val -= pc;
i64 lo = -(1 << 27);
i64 hi = 1 << 27;
if (val < lo || hi <= val) {
val = isec.osec.thunks[r.thunk_idx]->get_addr(r.thunk_sym_idx) - pc;
assert(lo <= val && val < hi);
}
*(ul32 *)loc |= bits(val, 27, 2);
break;
}
case ARM64_RELOC_PAGE21:
case ARM64_RELOC_GOT_LOAD_PAGE21:
case ARM64_RELOC_TLVP_LOAD_PAGE21:
assert(r.is_pcrel);
*(u32 *)(buf + r.offset) |=
encode_page(page(val) - page(get_addr(ctx) + r.offset));
*(ul32 *)loc |= encode_page(page(val) - page(pc));
break;
case ARM64_RELOC_PAGEOFF12:
case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: {
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset;
u32 insn = *(u32 *)(buf + r.offset);
assert(!r.is_pcrel);
u32 insn = *(ul32 *)loc;
i64 scale = 0;
if ((insn & 0x3b000000) == 0x39000000)
scale = insn >> 30;
*(u32 *)(buf + r.offset) |= bits(val, 11, scale) << 10;
if ((insn & 0x3b000000) == 0x39000000) {
scale = bits(insn, 31, 30);
if (scale == 0 && (insn & 0x04800000) == 0x04800000)
scale = 4;
}
*(ul32 *)loc |= bits(val, 11, scale) << 10;
break;
}
default:
@@ -255,4 +279,188 @@ void Subsection<ARM64>::apply_reloc(Context<ARM64> &ctx, u8 *buf) {
}
}
static void reset_thunk(RangeExtensionThunk<E> &thunk) {
for (Symbol<E> *sym : thunk.symbols) {
sym->thunk_idx = -1;
sym->thunk_sym_idx = -1;
sym->flags &= (u8)~NEEDS_RANGE_EXTN_THUNK;
}
}
static bool is_reachable(Context<E> &ctx, Symbol<E> &sym,
Subsection<E> &subsec, Relocation<E> &rel) {
// We pessimistically assume that PLT entries are unreacahble.
if (sym.stub_idx != -1)
return false;
// We create thunks with a pessimistic assumption that all
// out-of-section relocations would be out-of-range.
if (!sym.subsec || &sym.subsec->isec.osec != &subsec.isec.osec)
return false;
if (sym.subsec->output_offset == -1)
return false;
// Compute a distance between the relocated place and the symbol
// and check if they are within reach.
i64 S = sym.get_addr(ctx);
i64 A = rel.addend;
i64 P = subsec.get_addr(ctx) + rel.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;
// ARM64's call/jump instructions take 27 bits displacement, so they
// can refer only up to ±128 MiB. If a branch target is further than
// that, we need to let it branch to a linker-synthesized code
// sequence that construct a full 32 bit address in a register and
// jump there. That linker-synthesized code is called "thunk".
void create_range_extension_thunks(Context<E> &ctx, OutputSection<E> &osec) {
std::span<Subsection<E> *> members = osec.members;
if (members.empty())
return;
members[0]->output_offset = 0;
// Initialize input sections with a dummy offset so that we can
// distinguish sections that have got an address with the one who
// haven't.
tbb::parallel_for((i64)1, (i64)members.size(), [&](i64 i) {
members[i]->output_offset = -1;
});
// We create thunks from the beginning of the section to the end.
// We manage progress using four offsets which increase monotonically.
// The locations they point to are always A <= B <= C <= D.
i64 a = 0;
i64 b = 0;
i64 c = 0;
i64 d = 0;
i64 offset = 0;
while (b < members.size()) {
// Move D foward as far as we can jump from B to D.
while (d < members.size() &&
offset - members[b]->output_offset < MAX_DISTANCE) {
offset = align_to(offset, 1 << members[d]->p2align);
members[d]->output_offset = offset;
offset += members[d]->input_size;
d++;
}
// Move C forward so that C is apart from B by GROUP_SIZE.
while (c < members.size() &&
members[c]->output_offset - members[b]->output_offset < GROUP_SIZE)
c++;
// Move A forward so that A is reachable from C.
if (c > 0) {
i64 c_end = members[c - 1]->output_offset + members[c - 1]->input_size;
while (a < osec.thunks.size() &&
osec.thunks[a]->offset < c_end - MAX_DISTANCE)
reset_thunk(*osec.thunks[a++]);
}
// Create a thunk for input sections between B and C and place it at D.
osec.thunks.emplace_back(new RangeExtensionThunk<E>{osec});
RangeExtensionThunk<E> &thunk = *osec.thunks.back();
thunk.thunk_idx = osec.thunks.size() - 1;
thunk.offset = offset;
// Scan relocations between B and C to collect symbols that need thunks.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](Subsection<E> *subsec) {
std::span<Relocation<E>> rels = subsec->get_rels();
for (i64 i = 0; i < rels.size(); i++) {
Relocation<E> &r = rels[i];
if (!r.sym->file || r.type != ARM64_RELOC_BRANCH26)
continue;
// Skip if the destination is within reach.
if (is_reachable(ctx, *r.sym, *subsec, r))
continue;
// If the symbol is already in another thunk, reuse it.
if (r.sym->thunk_idx != -1) {
r.thunk_idx = r.sym->thunk_idx;
r.thunk_sym_idx = r.sym->thunk_sym_idx;
continue;
}
// Otherwise, add the symbol to this thunk if it's not added already.
r.thunk_idx = thunk.thunk_idx;
r.thunk_sym_idx = -1;
if (!(r.sym->flags.fetch_or(NEEDS_RANGE_EXTN_THUNK) &
NEEDS_RANGE_EXTN_THUNK)) {
std::scoped_lock lock(thunk.mu);
thunk.symbols.push_back(r.sym);
}
}
});
// Now that we know the number of symbols in the thunk, we can compute
// its size.
offset += thunk.size();
// Sort symbols added to the thunk to make the output deterministic.
sort(thunk.symbols, [](Symbol<E> *a, Symbol<E> *b) {
return std::tuple{a->file->priority, a->value} <
std::tuple{b->file->priority, b->value};
});
// Assign offsets within the thunk to the symbols.
for (i64 i = 0; Symbol<E> *sym : thunk.symbols) {
sym->thunk_idx = thunk.thunk_idx;
sym->thunk_sym_idx = i++;
}
// Scan relocations again to fix symbol offsets in the last thunk.
tbb::parallel_for_each(members.begin() + b, members.begin() + c,
[&](Subsection<E> *subsec) {
for (Relocation<E> &r : subsec->get_rels())
if (r.thunk_idx == thunk.thunk_idx)
r.thunk_sym_idx = r.sym->thunk_sym_idx;
});
// Move B forward to point to the begining of the next group.
b = c;
}
while (a < osec.thunks.size())
reset_thunk(*osec.thunks[a++]);
osec.hdr.size = offset;
}
void RangeExtensionThunk<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + output_section.hdr.offset + offset;
static const u32 data[] = {
0x90000010, // adrp x16, 0 # R_AARCH64_ADR_PREL_PG_HI21
0x91000210, // add x16, x16 # R_AARCH64_ADD_ABS_LO12_NC
0xd61f0200, // br x16
};
static_assert(ENTRY_SIZE == sizeof(data));
for (i64 i = 0; i < symbols.size(); i++) {
u64 S = symbols[i]->get_addr(ctx);
u64 P = output_section.hdr.addr + offset + i * ENTRY_SIZE;
u8 *loc = buf + i * ENTRY_SIZE;
memcpy(loc , data, sizeof(data));
*(ul32 *)loc |= encode_page(page(S) - page(P));
*(ul32 *)(loc + 4) |= bits(S, 11, 0) << 10;
}
}
} // namespace mold::macho
+53 -59
View File
@@ -2,25 +2,27 @@
namespace mold::macho {
using E = X86_64;
template <>
void StubsSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
void StubsSection<E>::copy_buf(Context<E> &ctx) {
u8 *buf = ctx.buf + this->hdr.offset;
for (i64 i = 0; i < syms.size(); i++) {
// `ff 25 xx xx xx xx` is a RIP-relative indirect jump instruction,
// i.e., `jmp *IMM(%rip)`. It loads an address from la_symbol_ptr
// and jump there.
static_assert(X86_64::stub_size == 6);
static_assert(E::stub_size == 6);
buf[i * 6] = 0xff;
buf[i * 6 + 1] = 0x25;
*(u32 *)(buf + i * 6 + 2) =
ctx.lazy_symbol_ptr.hdr.addr + i * X86_64::word_size -
*(ul32 *)(buf + i * 6 + 2) =
ctx.lazy_symbol_ptr.hdr.addr + i * E::word_size -
(this->hdr.addr + i * 6 + 6);
}
}
template <>
void StubHelperSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
void StubHelperSection<E>::copy_buf(Context<E> &ctx) {
u8 *start = ctx.buf + this->hdr.offset;
u8 *buf = start;
@@ -31,12 +33,12 @@ void StubHelperSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
0x90, // nop
};
static_assert(sizeof(insn0) == X86_64::stub_helper_hdr_size);
static_assert(sizeof(insn0) == E::stub_helper_hdr_size);
memcpy(buf, insn0, sizeof(insn0));
*(u32 *)(buf + 3) =
*(ul32 *)(buf + 3) =
get_symbol(ctx, "__dyld_private")->get_addr(ctx) - this->hdr.addr - 7;
*(u32 *)(buf + 11) =
*(ul32 *)(buf + 11) =
get_symbol(ctx, "dyld_stub_binder")->get_got_addr(ctx) - this->hdr.addr - 15;
buf += 16;
@@ -47,11 +49,11 @@ void StubHelperSection<X86_64>::copy_buf(Context<X86_64> &ctx) {
0xe9, 0, 0, 0, 0, // jmp $__stub_helper
};
static_assert(sizeof(insn) == X86_64::stub_helper_size);
static_assert(sizeof(insn) == E::stub_helper_size);
memcpy(buf, insn, sizeof(insn));
*(u32 *)(buf + 1) = ctx.stubs.bind_offsets[i];
*(u32 *)(buf + 6) = start - buf - 10;
*(ul32 *)(buf + 1) = ctx.stubs.bind_offsets[i];
*(ul32 *)(buf + 6) = start - buf - 10;
buf += 10;
}
}
@@ -71,14 +73,14 @@ static i64 get_reloc_addend(u32 type) {
static i64 read_addend(u8 *buf, const MachRel &r) {
if (r.p2size == 2)
return *(i32 *)(buf + r.offset) + get_reloc_addend(r.type);
return *(il32 *)(buf + r.offset) + get_reloc_addend(r.type);
if (r.p2size == 3)
return *(i64 *)(buf + r.offset) + get_reloc_addend(r.type);
return *(il64 *)(buf + r.offset) + get_reloc_addend(r.type);
unreachable();
}
static Relocation<X86_64>
read_reloc(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
static Relocation<E>
read_reloc(Context<E> &ctx, ObjectFile<E> &file,
const MachSection &hdr, MachRel &r) {
if (r.p2size != 2 && r.p2size != 3)
Fatal(ctx) << file << ": invalid r.p2size: " << (u32)r.p2size;
@@ -92,7 +94,7 @@ read_reloc(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
}
u8 *buf = (u8 *)file.mf->data + hdr.offset;
Relocation<X86_64> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
Relocation<E> rel{r.offset, (u8)r.type, (u8)r.p2size, (bool)r.is_pcrel};
i64 addend = read_addend(buf, r);
if (r.is_extern) {
@@ -107,7 +109,7 @@ read_reloc(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
else
addr = addend;
Subsection<X86_64> *target = file.find_subsection(ctx, addr);
Subsection<E> *target = file.find_subsection(ctx, addr);
if (!target)
Fatal(ctx) << file << ": bad relocation: " << r.offset;
@@ -117,10 +119,10 @@ read_reloc(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
}
template <>
std::vector<Relocation<X86_64>>
read_relocations(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
std::vector<Relocation<E>>
read_relocations(Context<E> &ctx, ObjectFile<E> &file,
const MachSection &hdr) {
std::vector<Relocation<X86_64>> vec;
std::vector<Relocation<E>> vec;
vec.reserve(hdr.nreloc);
MachRel *rels = (MachRel *)(file.mf->data + hdr.reloff);
@@ -130,42 +132,49 @@ read_relocations(Context<X86_64> &ctx, ObjectFile<X86_64> &file,
}
template <>
void Subsection<X86_64>::scan_relocations(Context<X86_64> &ctx) {
for (Relocation<X86_64> &r : get_rels()) {
Symbol<X86_64> *sym = r.sym;
void Subsection<E>::scan_relocations(Context<E> &ctx) {
for (Relocation<E> &r : get_rels()) {
Symbol<E> *sym = r.sym;
if (!sym)
continue;
if (sym->is_imported && sym->file->is_dylib)
((DylibFile<E> *)sym->file)->is_alive = true;
switch (r.type) {
case X86_64_RELOC_GOT_LOAD:
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_GOT;
case X86_64_RELOC_UNSIGNED:
if (sym->is_imported) {
if (r.p2size != 3) {
Error(ctx) << this->isec << ": " << r << " relocation at offset 0x"
<< std::hex << r.offset << " against symbol `"
<< *sym << "' can not be used";
}
r.needs_dynrel = true;
}
break;
case X86_64_RELOC_GOT:
case X86_64_RELOC_GOT_LOAD:
sym->flags |= NEEDS_GOT;
break;
case X86_64_RELOC_TLV:
if (sym->file && sym->file->is_dylib())
sym->flags |= NEEDS_THREAD_PTR;
sym->flags |= NEEDS_THREAD_PTR;
break;
}
if (sym->file && sym->file->is_dylib()) {
if (sym->is_imported)
sym->flags |= NEEDS_STUB;
((DylibFile<X86_64> *)sym->file)->is_needed = true;
}
}
}
template <>
void Subsection<X86_64>::apply_reloc(Context<X86_64> &ctx, u8 *buf) {
for (const Relocation<X86_64> &r : get_rels()) {
void Subsection<E>::apply_reloc(Context<E> &ctx, u8 *buf) {
for (const Relocation<E> &r : get_rels()) {
if (r.sym && !r.sym->file) {
Error(ctx) << "undefined symbol: " << isec.file << ": " << *r.sym;
continue;
}
u64 val = 0;
u64 val = r.addend;
switch (r.type) {
case X86_64_RELOC_UNSIGNED:
@@ -174,46 +183,31 @@ void Subsection<X86_64>::apply_reloc(Context<X86_64> &ctx, u8 *buf) {
case X86_64_RELOC_SIGNED_1:
case X86_64_RELOC_SIGNED_2:
case X86_64_RELOC_SIGNED_4:
val = r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
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);
}
val += r.sym ? r.sym->get_addr(ctx) : r.subsec->get_addr(ctx);
break;
case X86_64_RELOC_GOT:
val = r.sym->get_got_addr(ctx);
case X86_64_RELOC_GOT_LOAD:
val += r.sym->get_got_addr(ctx);
break;
case X86_64_RELOC_TLV:
if (r.sym->tlv_idx != -1) {
val = r.sym->get_tlv_addr(ctx);
} else {
// Relax MOVQ into LEAQ
if (buf[r.offset - 2] != 0x8b)
Error(ctx) << isec << ": invalid TLV relocation";
buf[r.offset - 2] = 0x8d;
val = r.sym->get_addr(ctx);
}
val += r.sym->get_tlv_addr(ctx);
break;
default:
Fatal(ctx) << isec << ": unknown reloc: " << (int)r.type;
}
val += r.addend;
// An address of a thread-local variable is computed as an offset
// to the beginning of the first thread-local section.
if (isec.hdr.type == S_THREAD_LOCAL_VARIABLES)
val -= ctx.tls_begin;
if (r.is_pcrel)
val -= get_addr(ctx) + r.offset + 4 + get_reloc_addend(r.type);
if (r.p2size == 2)
*(u32 *)(buf + r.offset) = val;
*(ul32 *)(buf + r.offset) = val;
else if (r.p2size == 3)
*(u64 *)(buf + r.offset) = val;
*(ul64 *)(buf + r.offset) = val;
else
unreachable();
}
+192 -23
View File
@@ -15,43 +15,79 @@ static const char helpmsg[] = R"(
Options:
-F<PATH> Add DIR to framework search path
-L<PATH> Add DIR to library search path
-Z Do not search the standard directories when
searching for libraries and frameworks
-ObjC Load all static archive members that implement
an Objective-C class or category
-U <SYMBOL> Allow a symbol to be undefined
-Z Do not search the standard directories when
searching for libraries and frameworks
-adhoc_codesign Add ad-hoc code signature to the output file
-no_adhoc_codesign
-all_load Include all objects from static archives
-noall_load
-arch <ARCH_NAME> Specify target architecture
-bundle Produce a mach-o bundle
-compatibility_version <VERSION>
Specifies the compatibility version number of the library
-current_version <VERSION> Specifies the current version number of the library.
-dead_strip Remove unreachable functions and data
-dead_strip_dylibs Remove unreachable dylibs from dependencies
-demangle Demangle C++ symbols in log messages (default)
-dylib Produce a dynamic library
-dylib_compatibility_version <VERSION>
Alias for -compatibility_version
-dylib_current_version <VERSION>
Alias for -current_version
-dynamic Link against dylibs (default)
-e <SYMBOL> Specify the entry point of a main executable
-execute Produce an executable (default)
-export_dynamic Preserves all global symbols in main executables during LTO
-exported_symbol <SYMBOL> Export a given symbol
-exported_symbols_list <FILE>
Read a list of exported symbols from a given file
-filelist <FILE>[,<DIR>] Specify the list of input file names
-final_output <NAME>
-force_load <FILE> Include all objects from a given static archive
-framework <NAME>,[,<SUFFIX>]
Search for a given framework
-headerpad <SIZE> Allocate the size of padding after load commands
-headerpad_max_install_names
Allocate MAXPATHLEN byte padding after load commands
-help Report usage information
-hidden-l<LIB>
-install_name <NAME>
-l<LIB> Search for a given library
-lto_library <FILE> Ignored
-macos_version_min <VERSION>
-map <FILE> Write map file to a given file
-needed-l<LIB> Search for a given library
-needed-framework <NAME>[,<SUFFIX>]
Search for a given framework
-no_deduplicate Ignored
-no_uuid Do not generate an LC_UUID load command
-o <FILE> Set output filename
-object_path_lto <FILE> Write a LTO temporary file to a given path
-order_file <FILE> Ignored
-pagezero_size <SIZE> Specify the size of the __PAGEZERO segment
-platform_version <PLATFORM> <MIN_VERSION> <SDK_VERSION>
Set platform, platform version and SDK version
-random_uuid Generate a random LC_UUID load command
-rpath <PATH> Add PATH to the runpath search path list
-search_dylibs_first
-search_paths_first
-sectcreate <SEGNAME> <SECTNAME> <FILE>
-stack_size <SIZE>
-stats Show statistics info
-syslibroot <DIR> Prepend DIR to library search paths
-t Print out each file the linker loads
-v Report version information)";
-thread_count <NUMBER> Use given number of threads
-u <SYMBOL> Force load a given symbol from archive if necessary
-uexported_symbol <SYMBOL> Export all but a given symbol
-unexported_symbols_list <FILE>
Read a list of unexported symbols from a given file
-v Report version information
-weak_framework <NAME>[,<SUFFIX>]
Search for a given framework
-weak-l<LIB> Search for a given library)";
template <typename E>
static i64 parse_platform(Context<E> &ctx, std::string_view arg) {
@@ -102,9 +138,37 @@ static bool is_directory(std::filesystem::path path) {
}
template <typename E>
void parse_nonpositional_args(Context<E> &ctx,
std::vector<std::string> &remaining) {
static std::vector<std::string>
read_lines(Context<E> &ctx, std::string_view path) {
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, std::string(path));
std::string_view data((char *)mf->data, mf->size);
std::vector<std::string> vec;
while (!data.empty()) {
size_t pos = data.find('\n');
std::string_view line;
if (pos == data.npos) {
line = data;
data = "";
} else {
line = data.substr(0, pos);
data = data.substr(pos + 1);
}
line = string_trim(line);
if (!line.empty() && !line.starts_with('#'))
vec.push_back(std::string(line));
}
return vec;
}
template <typename E>
std::vector<std::string> parse_nonpositional_args(Context<E> &ctx) {
std::vector<std::string_view> &args = ctx.cmdline_args;
std::vector<std::string> remaining;
i64 i = 1;
std::vector<std::string> framework_paths;
@@ -116,6 +180,7 @@ void parse_nonpositional_args(Context<E> &ctx,
std::string_view arg;
std::string_view arg2;
std::string_view arg3;
u64 hex_arg;
auto read_arg = [&](std::string name) {
if (args[i] == name) {
@@ -160,6 +225,17 @@ void parse_nonpositional_args(Context<E> &ctx,
return false;
};
auto read_hex = [&](std::string name) {
if (!read_arg(name))
return false;
size_t pos;
hex_arg = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed " << name << ": " << arg;
return true;
};
if (args[i].starts_with('@')) {
std::vector<std::string_view> vec =
read_response_file(ctx, args[i].substr(1));
@@ -169,7 +245,7 @@ void parse_nonpositional_args(Context<E> &ctx,
}
if (read_flag("-help") || read_flag("--help")) {
SyncOut(ctx) << "Usage: " << ctx.cmdline_args[i]
SyncOut(ctx) << "Usage: " << ctx.cmdline_args[0]
<< " [options] file...\n" << helpmsg;
exit(0);
}
@@ -182,10 +258,16 @@ void parse_nonpositional_args(Context<E> &ctx,
nostdlib = true;
} else if (read_flag("-ObjC")) {
ctx.arg.ObjC = true;
} else if (read_arg("-U")) {
ctx.arg.U.push_back(std::string(arg));
} else if (read_flag("-adhoc_codesign")) {
ctx.arg.adhoc_codesign = true;
} else if (read_flag("-no_adhoc_codesign")) {
ctx.arg.adhoc_codesign = false;
} else if (read_flag("-all_load")) {
remaining.push_back("-all_load");
} else if (read_flag("-noall_load")) {
remaining.push_back("-noall_load");
} else if (read_arg("-arch")) {
if (arg == "x86_64")
ctx.arg.arch = CPU_TYPE_X86_64;
@@ -195,8 +277,15 @@ void parse_nonpositional_args(Context<E> &ctx,
Fatal(ctx) << "unknown -arch: " << arg;
} else if (read_flag("-bundle")) {
ctx.output_type = MH_BUNDLE;
} else if (read_arg("-compatibility_version") ||
read_arg("-dylib_compatibility_version")) {
ctx.arg.compatibility_version = parse_version(ctx, arg);
} else if (read_arg("-current_version") ||
read_arg("-dylib_current_version")) {
ctx.arg.current_version = parse_version(ctx, arg);
} else if (read_flag("-color-diagnostics") ||
read_flag("--color-diagnostics")) {
ctx.arg.color_diagnostics = true;
} else if (read_flag("-dead_strip")) {
ctx.arg.dead_strip = true;
} else if (read_flag("-dead_strip_dylibs")) {
@@ -205,32 +294,55 @@ void parse_nonpositional_args(Context<E> &ctx,
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_hex("-headerpad")) {
ctx.arg.headerpad = hex_arg;
} else if (read_flag("-headerpad_max_install_names")) {
ctx.arg.headerpad = 1024;
} else if (read_flag("-dynamic")) {
ctx.arg.dynamic = true;
} else if (read_arg("-e")) {
ctx.arg.entry = arg;
ctx.arg.entry = get_symbol(ctx, arg);
} else if (read_flag("-execute")) {
ctx.output_type = MH_EXECUTE;
} else if (read_flag("-export_dynamic")) {
ctx.arg.export_dynamic = true;
} else if (read_arg("-exported_symbol")) {
if (!ctx.arg.exported_symbols_list.add(arg, 1))
Fatal(ctx) << "-exported_symbol: invalid glob pattern: " << arg;
} else if (read_arg("-exported_symbols_list")) {
for (std::string_view pat : read_lines(ctx, arg))
if (!ctx.arg.exported_symbols_list.add(pat, 1))
Fatal(ctx) << "-exported_symbols_list: " << arg
<< ": invalid glob pattern: " << pat;
} else if (read_arg("-fatal_warnings")) {
} else if (read_arg("-filelist")) {
remaining.push_back("-filelist");
remaining.push_back(std::string(arg));
} else if (read_arg("-final_output")) {
ctx.arg.final_output = arg;
} else if (read_arg("-force_load")) {
remaining.push_back("-force_load");
remaining.push_back(std::string(arg));
} else if (read_arg("-framework")) {
remaining.push_back("-framework");
remaining.push_back(std::string(arg));
} else if (read_arg("-lto_library")) {
ctx.arg.lto_library = arg;
} else if (read_arg("-macos_version_min")) {
ctx.arg.platform = PLATFORM_MACOS;
ctx.arg.platform_min_version = parse_version(ctx, arg);
} else if (read_joined("-hidden-l")) {
remaining.push_back("-hidden-l");
remaining.push_back(std::string(arg));
} else if (read_arg("-install_name")) {
ctx.arg.install_name = arg;
} else if (read_joined("-l")) {
remaining.push_back("-l");
remaining.push_back(std::string(arg));
} else if (read_arg("-map")) {
ctx.arg.map = arg;
} else if (read_arg("-mllvm")) {
ctx.arg.mllvm.push_back(std::string(arg));
} else if (read_joined("-needed-l")) {
remaining.push_back("-needed-l");
remaining.push_back(std::string(arg));
@@ -238,28 +350,65 @@ void parse_nonpositional_args(Context<E> &ctx,
remaining.push_back("-needed_framework");
remaining.push_back(std::string(arg));
} else if (read_flag("-no_deduplicate")) {
} else if (read_flag("-no_uuid")) {
ctx.arg.uuid = UUID_NONE;
} else if (read_arg("-o")) {
ctx.arg.output = arg;
} else if (read_arg("-pagezero_size")) {
size_t pos;
pagezero_size = std::stol(std::string(arg), &pos, 16);
if (pos != arg.size())
Fatal(ctx) << "malformed -pagezero_size: " << arg;
} else if (read_arg("-object_path_lto")) {
ctx.arg.object_path_lto = arg;
} else if (read_arg("-order_file")) {
ctx.arg.order_file = read_lines(ctx, arg);
} else if (read_hex("-pagezero_size")) {
pagezero_size = hex_arg;
} else if (read_flag("-perf")) {
ctx.arg.perf = true;
} else if (read_arg3("-platform_version")) {
ctx.arg.platform = parse_platform(ctx, arg);
ctx.arg.platform_min_version = parse_version(ctx, arg2);
ctx.arg.platform_sdk_version = parse_version(ctx, arg3);
} else if (read_flag("-quick_exit")) {
ctx.arg.quick_exit = true;
} else if (read_flag("-no_quick_exit")) {
ctx.arg.quick_exit = false;
} else if (read_flag("-random_uuid")) {
ctx.arg.uuid = UUID_RANDOM;
} else if (read_arg("-rpath")) {
ctx.arg.rpath.push_back(std::string(arg));
} else if (read_flag("-search_dylibs_first")) {
Fatal(ctx) << "-search_dylibs_first is not supported";
} else if (read_flag("-search_paths_first")) {
ctx.arg.search_paths_first = true;
} else if (read_flag("-search_dylibs_first")) {
ctx.arg.search_paths_first = false;
} else if (read_arg3("-sectcreate")) {
ctx.arg.sectcreate.push_back({arg, arg2, arg3});
} else if (read_hex("-stack_size")) {
ctx.arg.stack_size = hex_arg;
} else if (read_flag("-stats")) {
ctx.arg.stats = true;
Counter::enabled = true;
} else if (read_arg("-syslibroot")) {
ctx.arg.syslibroot.push_back(std::string(arg));
} else if (read_flag("-t")) {
ctx.arg.trace = true;
} else if (read_arg("-thread_count")) {
ctx.arg.thread_count = std::stoi(std::string(arg));
} else if (read_arg("-u")) {
ctx.arg.u.push_back(std::string(arg));
} else if (read_arg("-unexported_symbol")) {
if (!ctx.arg.unexported_symbols_list.add(arg, 1))
Fatal(ctx) << "-unexported_symbol: invalid glob pattern: " << arg;
} else if (read_arg("-unexported_symbols_list")) {
for (std::string_view pat : read_lines(ctx, arg))
if (!ctx.arg.unexported_symbols_list.add(pat, 1))
Fatal(ctx) << "-unexported_symbols_list: " << arg
<< ": invalid glob pattern: " << pat;
} else if (read_flag("-v")) {
SyncOut(ctx) << mold_version;
} else if (read_arg("-weak_framework")) {
remaining.push_back("-weak_framework");
remaining.push_back(std::string(arg));
} else if (read_joined("-weak-l")) {
remaining.push_back("-weak-l");
remaining.push_back(std::string(arg));
} else {
if (args[i][0] == '-')
Fatal(ctx) << "unknown command line option: " << args[i];
@@ -268,6 +417,12 @@ void parse_nonpositional_args(Context<E> &ctx,
}
}
if (!ctx.arg.entry)
ctx.arg.entry = get_symbol(ctx, "_main");
if (ctx.arg.thread_count == 0)
ctx.arg.thread_count = get_default_thread_count();
auto add_search_path = [&](std::vector<std::string> &vec, std::string path) {
if (!path.starts_with('/') || ctx.arg.syslibroot.empty()) {
if (is_directory(path))
@@ -305,18 +460,32 @@ void parse_nonpositional_args(Context<E> &ctx,
if (pagezero_size.has_value()) {
if (ctx.output_type != MH_EXECUTE)
Error(ctx) << "-pagezero_size option can only be used when"
Fatal(ctx) << "-pagezero_size option can only be used when"
<< " linking a main executable";
ctx.arg.pagezero_size = *pagezero_size;
} else {
ctx.arg.pagezero_size = (ctx.output_type == MH_EXECUTE) ? 0x100000000 : 0;
}
if (ctx.arg.final_output == "") {
if (ctx.arg.install_name != "")
ctx.arg.final_output = ctx.arg.install_name;
else
ctx.arg.final_output = ctx.arg.output;
}
if (ctx.arg.uuid == UUID_RANDOM)
memcpy(ctx.uuid, get_uuid_v4().data(), 16);
ctx.arg.exported_symbols_list.compile();
ctx.arg.unexported_symbols_list.compile();
return remaining;
}
#define INSTANTIATE(E) \
template void parse_nonpositional_args(Context<E> &, std::vector<std::string> &)
template std::vector<std::string> parse_nonpositional_args(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
INSTANTIATE_ALL;
} // namespace mold::macho
+16 -15
View File
@@ -11,12 +11,12 @@ static std::vector<Subsection<E> *> collect_root_set(Context<E> &ctx) {
rootset.push_back(sym->subsec);
};
mark(get_symbol(ctx, ctx.arg.entry));
mark(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)
if (sym->file == file && sym->scope == SCOPE_EXTERN)
mark(sym);
return rootset;
@@ -37,12 +37,11 @@ static void visit(Context<E> &ctx, Subsection<E> &subsec) {
}
for (UnwindRecord<E> &rec : subsec.get_unwind_records()) {
rec.is_alive = true;
visit(ctx, *rec.subsec);
if (rec.lsda)
visit(ctx, *rec.lsda);
if (Symbol<E> *sym = rec.personality; sym && sym->subsec)
visit(ctx, *sym->subsec);
if (rec.personality && rec.personality->subsec)
visit(ctx, *rec.personality->subsec);
}
}
@@ -62,6 +61,10 @@ static bool refers_live_subsection(Subsection<E> &subsec) {
template <typename E>
static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
for (ObjectFile<E> *file : ctx.objs)
for (Subsection<E> *subsec : file->subsections)
subsec->is_alive = false;
for (Subsection<E> *subsec : rootset)
visit(ctx, *subsec);
@@ -69,7 +72,7 @@ static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
do {
repeat = false;
for (ObjectFile<E> *file : ctx.objs) {
for (std::unique_ptr<Subsection<E>> &subsec : file->subsections) {
for (Subsection<E> *subsec : file->subsections) {
if ((subsec->isec.hdr.attr & S_ATTR_LIVE_SUPPORT) &&
!subsec->is_alive &&
refers_live_subsection(*subsec)) {
@@ -83,17 +86,16 @@ static void mark(Context<E> &ctx, const std::vector<Subsection<E> *> &rootset) {
template <typename E>
static void sweep(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs) {
std::erase_if(file->subsections,
[](const std::unique_ptr<Subsection<E>> &subsec) {
return !subsec->is_alive;
});
}
for (ObjectFile<E> *file : ctx.objs)
for (Symbol<E> *&sym : file->syms)
if (sym->file == file && sym->subsec && !sym->subsec->is_alive)
sym = nullptr;
for (ObjectFile<E> *file : ctx.objs) {
std::erase_if(file->subsections, [](const Subsection<E> *subsec) {
return !subsec->is_alive;
});
}
}
template <typename E>
@@ -106,7 +108,6 @@ void dead_strip(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void dead_strip(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
INSTANTIATE_ALL;
} // namespace mold::macho
-511
View File
@@ -1,511 +0,0 @@
#include "mold.h"
#include <cstdlib>
#include <fcntl.h>
#include <iomanip>
#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
static u8 *open_file(std::string path) {
i64 fd = ::open(path.c_str(), O_RDONLY);
if (fd == -1)
return nullptr;
struct stat st;
if (fstat(fd, &st) == -1) {
std::cerr << path << ": fstat failed: " << errno_string();
exit(1);
}
void *ptr = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
if (ptr == MAP_FAILED) {
std::cerr << path << ": mmap failed: " << errno_string();
exit(1);
}
close(fd);
return (u8 *)ptr;
}
static void print_bytes(u8 *buf, i64 size) {
if (size == 0) {
std::cout << "[]\n";
return;
}
std::cout << "[" << std::setw(2) << std::setfill('0') << (u32)buf[0];
for (i64 i = 1; i < size; i++)
std::cout << " " << std::setw(2) << std::setfill('0') << (u32)buf[i];
std::cout << "]\n";
}
struct ExportEntry {
std::string name;
u32 flags;
u64 addr;
};
static void read_trie(std::vector<ExportEntry> &vec, u8 *start, i64 offset = 0,
const std::string &prefix = "") {
u8 *buf = start + offset;
if (read_uleb(buf)) {
ExportEntry ent;
ent.name = prefix;
ent.flags = read_uleb(buf);
ent.addr = read_uleb(buf);
vec.push_back(std::move(ent));
}
for (i64 i = 0, n = read_uleb(buf); i < n; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(vec, start, off, prefix + suffix);
}
}
void dump_unwind_info(u8 *buf, MachSection &sec) {
UnwindSectionHeader &hdr = *(UnwindSectionHeader *)(buf + sec.offset);
std::cout << std::hex << " Unwind info:"
<< "\n version: 0x" << hdr.version
<< "\n encoding_offset: 0x" << hdr.encoding_offset
<< "\n encoding_count: 0x" << hdr.encoding_count
<< "\n personality_offset: 0x" << hdr.personality_offset
<< "\n personality_count: 0x" << hdr.personality_count
<< "\n page_offset: 0x" << hdr.page_offset
<< "\n page_count: 0x" << hdr.page_count;
std::cout << "\n encoding:";
u32 *enc = (u32 *)(buf + sec.offset + hdr.encoding_offset);
for (i64 i = 0; i < hdr.encoding_count; i++)
std::cout << std::hex << "\n 0x" << enc[i];
std::cout << "\n personality:";
u32 *pers = (u32 *)(buf + sec.offset + hdr.personality_offset);
for (i64 i = 0; i < hdr.personality_count; i++)
std::cout << std::hex << "\n 0x" << pers[i];
UnwindFirstLevelPage *ent =
(UnwindFirstLevelPage *)(buf + sec.offset + hdr.page_offset);
for (i64 i = 0; i < hdr.page_count; i++) {
std::cout << std::hex << "\n function:"
<< "\n func_addr: 0x" << ent[i].func_addr
<< "\n page_offset: 0x" << ent[i].page_offset
<< "\n lsda_offset: 0x" << ent[i].lsda_offset;
if (i != hdr.page_count - 1) {
UnwindLsdaEntry *lsda =
(UnwindLsdaEntry *)(buf + sec.offset + ent[i].lsda_offset);
i64 lsda_size = ent[i + 1].lsda_offset - ent[i].lsda_offset;
for (i64 j = 0; j < lsda_size / sizeof(UnwindLsdaEntry); j++)
std::cout << std::hex
<< "\n lsda:"
<< "\n func_addr: 0x" << lsda[j].func_addr
<< "\n lsda_addr: 0x" << lsda[j].lsda_addr;
}
if (ent[i].page_offset == 0)
break;
u8 *addr = buf + sec.offset + ent[i].page_offset;
switch (u32 kind = *addr; kind) {
case UNWIND_SECOND_LEVEL_REGULAR: {
std::cout << "\n UNWIND_SECOND_LEVEL_REGULAR:" ;
break;
}
case UNWIND_SECOND_LEVEL_COMPRESSED: {
std::cout << "\n UNWIND_SECOND_LEVEL_COMPRESSED" ;
UnwindSecondLevelPage &hdr2 = *(UnwindSecondLevelPage *)addr;
std::cout << std::hex
<< "\n page_offset: 0x" << hdr2.page_offset
<< "\n page_count: 0x" << hdr2.page_count
<< "\n encoding_offset: 0x" << hdr2.encoding_offset
<< "\n encoding_count: 0x" << hdr2.encoding_count;
UnwindPageEntry *ent2 = (UnwindPageEntry *)(addr + hdr2.page_offset);
for (i64 j = 0; j < hdr2.page_count; j++)
std::cout << std::hex << "\n ent 0x"
<< (ent[i].func_addr + ent2[j].func_addr)
<< " 0x" << ent2[j].encoding;
u32 *enc = (u32 *)(addr + hdr2.encoding_offset);
for (i64 j = 0; j < hdr2.encoding_count; j++)
std::cout << std::hex << "\n 0x" << enc[j];
break;
}
default:
std::cout << "\n bad 2nd-level unwind info header: " << kind;
}
}
std::cout << "\n";
}
void dump_compact_unwind(u8 *buf, MachSection &sec) {
CompactUnwindEntry *ent = (CompactUnwindEntry *)(buf + sec.offset);
i64 nentry = sec.size / sizeof(CompactUnwindEntry);
std::cout << " Compact unwind:"
<< "\n num_entry: " << nentry;
for (i64 i = 0; i < nentry; i++) {
std::cout << std::hex
<< "\n entry: 0x" << (i * sizeof(CompactUnwindEntry))
<< "\n code_start: 0x" << ent[i].code_start
<< "\n code_len: 0x" << ent[i].code_len
<< "\n encoding: 0x" << ent[i].encoding
<< "\n personality: 0x" << ent[i].personality
<< "\n lsda: 0x" << ent[i].lsda;
}
std::cout << "\n";
}
void dump_file(std::string path) {
u8 *buf = open_file(path);
if (!buf) {
std::cerr << "cannot open " << path << "\n";
exit(1);
}
MachHeader &hdr = *(MachHeader *)buf;
std::cout << "magic: 0x" << std::hex << hdr.magic
<< "\ncputype: 0x" << hdr.cputype
<< "\ncpusubtype: 0x" << hdr.cpusubtype
<< "\nfiletype: 0x" << hdr.filetype
<< "\nncmds: 0x" << hdr.ncmds
<< "\nsizeofcmds: 0x" << hdr.sizeofcmds
<< "\nflags: 0x" << hdr.flags
<< "\n\n";
u8 *p = buf + sizeof(MachHeader);
for (i64 i = 0; i < hdr.ncmds; i++) {
std::cout << "fileoff: 0x" << std::hex << (p - buf) << "\n";
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
auto print_dylib = [&] {
DylibCommand &cmd = *(DylibCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
<< "\n timestamp: 0x" << cmd.timestamp
<< "\n current_version: 0x" << cmd.current_version
<< "\n compatibility_version: 0x" << cmd.compatibility_version
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
<< "\n";
};
switch (lc.cmd) {
case LC_SYMTAB: {
std::cout << "LC_SYMTAB\n";
SymtabCommand &cmd = *(SymtabCommand *)&lc;
std::cout << " cmdsize: " << cmd.cmdsize
<< "\n symoff: 0x" << std::hex << cmd.symoff
<< "\n nsyms: " << std::dec << cmd.nsyms
<< "\n stroff: 0x" << std::hex << cmd.stroff
<< "\n strsize: 0x" << cmd.strsize
<< "\n symdata: ";
print_bytes(buf + cmd.symoff, cmd.nsyms * sizeof(MachSym));
std::cout << " strdata: ";
print_bytes(buf + cmd.stroff, cmd.strsize);
MachSym *syms = (MachSym *)(buf + cmd.symoff);
for (i64 j = 0; j < cmd.nsyms; j++) {
std::cout << " symbol:"
<< "\n name: " << (char *)(buf + cmd.stroff + syms[j].stroff)
<< "\n stub: " << (u32)syms[j].stub
<< "\n pext: " << (u32)syms[j].pext
<< "\n type: " << (u32)syms[j].type
<< "\n ext: " << (u32)syms[j].ext
<< "\n sect: 0x" << (u32)syms[j].sect
<< "\n desc: 0x" << (u32)syms[j].desc
<< "\n value: 0x" << syms[j].value
<< "\n";
}
break;
}
case LC_DYSYMTAB: {
std::cout << "LC_DYSYMTAB\n";
DysymtabCommand &cmd = *(DysymtabCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n ilocalsym: 0x" << cmd.ilocalsym
<< "\n nlocalsym: 0x" << cmd.nlocalsym
<< "\n iextdefsym: 0x" << cmd.iextdefsym
<< "\n nextdefsym: 0x" << cmd.nextdefsym
<< "\n iundefsym: 0x" << cmd.iundefsym
<< "\n nundefsym: 0x" << cmd.nundefsym
<< "\n tocoff: 0x" << cmd.tocoff
<< "\n ntoc: 0x" << cmd.ntoc
<< "\n modtaboff: 0x" << cmd.modtaboff
<< "\n nmodtab: 0x" << cmd.nmodtab
<< "\n extrefsymoff: 0x" << cmd.extrefsymoff
<< "\n nextrefsyms: 0x" << cmd.nextrefsyms
<< "\n indirectsymoff: 0x" << cmd.indirectsymoff
<< "\n nindirectsyms: 0x" << cmd.nindirectsyms
<< "\n extreloff: 0x" << cmd.extreloff
<< "\n nextrel: 0x" << cmd.nextrel
<< "\n locreloff: 0x" << cmd.locreloff
<< "\n nlocrel: 0x" << cmd.nlocrel
<< "\n";
if (cmd.indirectsymoff) {
std::cout << " indirectsymdata: ";
print_bytes(buf + cmd.indirectsymoff, 4 * cmd.nindirectsyms);
}
break;
}
case LC_LOAD_DYLIB:
std::cout << "LC_LOAD_DYLIB\n";
print_dylib();
break;
case LC_LOAD_WEAK_DYLIB:
std::cout << "LC_LOAD_WEAK_DYLIB\n";
print_dylib();
break;
case LC_ID_DYLIB:
std::cout << "LC_ID_DYLIB\n";
print_dylib();
break;
case LC_LOAD_DYLINKER: {
std::cout << "LC_LOAD_DYLINKER\n";
DylinkerCommand &cmd = *(DylinkerCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n nameoff: 0x" << cmd.nameoff
<< "\n data: " << (char *)((char *)&cmd + cmd.nameoff)
<< "\n";
break;
}
case LC_SEGMENT_64: {
std::cout << "LC_SEGMENT_64\n";
SegmentCommand &cmd = *(SegmentCommand *)&lc;
std::cout << " cmdsize: " << cmd.cmdsize
<< "\n segname: " << cmd.get_segname()
<< "\n vmaddr: 0x" << std::hex << cmd.vmaddr
<< "\n vmsize: 0x" << cmd.vmsize
<< "\n fileoff: 0x" << cmd.fileoff
<< "\n filesize: 0x" << cmd.filesize
<< "\n maxprot: " << std::dec << cmd.maxprot
<< "\n initprot: " << cmd.initprot
<< "\n nsects: " << cmd.nsects
<< "\n flags: 0x" << std::hex << cmd.flags
<< "\n";
MachSection *sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
for (i64 j = 0; j < cmd.nsects; j++) {
std::cout << " section:\n sectname: " << sec[j].get_sectname()
<< "\n segname: " << sec[j].get_segname()
<< "\n addr: 0x" << std::hex << sec[j].addr
<< "\n size: 0x" << sec[j].size
<< "\n offset: 0x" << sec[j].offset
<< "\n p2align: " << std::dec << sec[j].p2align
<< "\n reloff: " << std::hex << sec[j].reloff
<< "\n nreloc: " << std::dec << sec[j].nreloc
<< "\n type: 0x" << std::hex << sec[j].type
<< "\n attr: 0x" << std::hex << sec[j].attr
<< "\n";
if (sec[j].type != S_ZEROFILL) {
std::cout << " contents: ";
print_bytes(buf + sec[j].offset, sec[j].size);
}
if (sec[j].reloff) {
MachRel *rel = (MachRel *)(buf + sec[j].reloff);
for (i64 k = 0; k < sec[j].nreloc; k++) {
std::cout << " reloc: "
<< "\n offset: 0x" << rel[k].offset
<< "\n idx: 0x" << rel[k].idx
<< "\n is_pcrel: " << rel[k].is_pcrel
<< "\n p2size: 0x" << rel[k].p2size
<< "\n is_extern: " << rel[k].is_extern
<< "\n type: " << rel[k].type
<< "\n";
}
}
if (sec[j].match("__TEXT", "__unwind_info"))
dump_unwind_info(buf, sec[j]);
if (sec[j].match("__LD", "__compact_unwind"))
dump_compact_unwind(buf, sec[j]);
}
break;
}
case LC_UUID: {
std::cout << "LC_UUID\n";
UUIDCommand &cmd = *(UUIDCommand *)&lc;
std::cout << " data: ";
print_bytes(cmd.uuid, sizeof(cmd.uuid));
break;
}
case LC_DYLD_INFO_ONLY: {
std::cout << "LC_DYLD_INFO_ONLY\n";
DyldInfoCommand &cmd = *(DyldInfoCommand *)&lc;
if (cmd.rebase_off) {
std::cout << " rebase: ";
print_bytes(buf + cmd.rebase_off, cmd.rebase_size);
std::cout << " rebase_off: 0x" << std::hex << cmd.rebase_off
<< "\n rebase_size: 0x" << cmd.rebase_size
<< "\n";
}
if (cmd.bind_off) {
std::cout << " bind: ";
print_bytes(buf + cmd.bind_off, cmd.bind_size);
std::cout << " bind_off: 0x" << std::hex << cmd.bind_off
<< "\n bind_size: 0x" << cmd.bind_size
<< "\n";
}
if (cmd.weak_bind_off) {
std::cout << " weak_bind: ";
print_bytes(buf + cmd.weak_bind_off, cmd.weak_bind_size);
std::cout << " weak_bind_off: 0x" << std::hex << cmd.weak_bind_off
<< "\n weak_bind_size: 0x" << cmd.weak_bind_size
<< "\n";
}
if (cmd.lazy_bind_off) {
std::cout << " lazy_bind: ";
print_bytes(buf + cmd.lazy_bind_off, cmd.lazy_bind_size);
std::cout << " lazy_bind_off: 0x" << std::hex << cmd.lazy_bind_off
<< "\n lazy_bind_size: 0x" << cmd.lazy_bind_size
<< "\n";
}
if (cmd.export_off) {
std::cout << " export: ";
print_bytes(buf + cmd.export_off, cmd.export_size);
std::cout << " export_off: 0x" << std::hex << cmd.export_off
<< "\n export_size: 0x" << cmd.export_size
<< "\n";
std::vector<ExportEntry> vec;
read_trie(vec, buf + cmd.export_off);
for (ExportEntry &ent : vec)
std::cout << " export_sym: " << ent.name << " 0x" << ent.addr << "\n";
}
break;
}
case LC_FUNCTION_STARTS: {
std::cout << "LC_FUNCTION_STARTS\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
std::cout << " dataoff: 0x" << cmd.dataoff
<< "\n datasize: 0x" << cmd.datasize
<< "\n data:";
u8 *p = buf + cmd.dataoff;
u64 addr = 0;
for (;;) {
u64 delta = read_uleb(p);
if (!delta)
break;
addr += delta;
std::cout << std::hex << " 0x" << addr;
}
std::cout << "\n";
break;
}
case LC_MAIN: {
std::cout << "LC_MAIN\n";
EntryPointCommand &cmd = *(EntryPointCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n entryoff: 0x" << cmd.entryoff
<< "\n stacksize: 0x" << cmd.stacksize
<< "\n";
break;
}
case LC_DATA_IN_CODE: {
std::cout << "LC_DATA_IN_CODE\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
std::cout << " dataoff: 0x" << cmd.dataoff
<< "\n datasize: 0x" << cmd.datasize
<< "\n";
break;
}
case LC_SOURCE_VERSION: {
std::cout << "LC_SOURCE_VERSION\n";
SourceVersionCommand &cmd = *(SourceVersionCommand *)&lc;
std::cout << " version: 0x" << cmd.version
<< "\n";
break;
}
break;
case LC_BUILD_VERSION: {
std::cout << "LC_BUILD_VERSION\n";
BuildVersionCommand &cmd = *(BuildVersionCommand *)&lc;
std::cout << " cmdsize: 0x" << cmd.cmdsize
<< "\n platform: 0x" << cmd.platform
<< "\n minos: 0x" << cmd.minos
<< "\n sdk: 0x" << cmd.sdk
<< "\n ntools: 0x" << cmd.ntools
<< "\n";
BuildToolVersion *tools =
(BuildToolVersion *)((u8 *)&lc + sizeof(BuildVersionCommand));
for (i64 i = 0; i < cmd.ntools; i++)
std::cout << " tool: 0x" << tools[i].tool
<< "\n version: 0x" << tools[i].version
<< "\n";
break;
}
case LC_VERSION_MIN_MACOSX: {
std::cout << "LC_VERSION_MIN_MACOSX\n";
VersionMinCommand &cmd = *(VersionMinCommand *)&lc;
std::cout << " version: " << (int)cmd.version
<< "\n sdk: " << (int)cmd.sdk
<< "\n";
break;
}
case LC_CODE_SIGNATURE: {
std::cout << "LC_CODE_SIGNATURE\n";
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)&lc;
CodeSignatureHeader &sig = *(CodeSignatureHeader *)(buf + cmd.dataoff);
std::cout << " magic: " << sig.magic
<< "\n length: " << sig.length
<< "\n count: " << sig.count
<< "\n";
for (i64 i = 0; i < sig.count; i++) {
CodeSignatureBlobIndex &idx = ((CodeSignatureBlobIndex *)(&sig + 1))[i];
std::cout << " idx type: " << idx.type
<< "\n idx offset: " << idx.offset
<< "\n";
CodeSignatureDirectory &dir =
*(CodeSignatureDirectory *)((u8 *)&sig + idx.offset);
std::cout << std::hex
<< " magic: 0x" << dir.magic
<< "\n version: 0x" << dir.version
<< "\n flags: 0x" << dir.flags
<< "\n hash_offset: 0x" << dir.hash_offset
<< "\n n_code_slots: 0x" << dir.n_code_slots
<< "\n hash_size: 0x" << (u32)dir.hash_size
<< "\n hash_type: 0x" << (u32)dir.hash_type
<< "\n page_size: 0x" << (1 << dir.page_size)
<< "\n";
}
break;
}
default:
std::cout << "UNKNOWN (0x" << std::hex << lc.cmd << ")\n";
break;
}
}
}
} // namespace mold::macho
+851
View File
@@ -0,0 +1,851 @@
#include "mold.h"
#include "../archive-file.h"
namespace mold::macho {
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputFile<E> &file) {
if (file.archive_name.empty())
out << path_clean(file.filename);
else
out << path_clean(file.archive_name) << "(" << path_clean(file.filename) + ")";
return out;
}
template <typename E>
void InputFile<E>::clear_symbols() {
for (Symbol<E> *sym : syms) {
std::scoped_lock lock(sym->mu);
if (sym->file == this) {
sym->file = nullptr;
sym->scope = SCOPE_LOCAL;
sym->is_imported = false;
sym->is_weak_def = false;
sym->subsec = nullptr;
sym->value = 0;
sym->is_common = false;
}
}
}
template <typename E>
ObjectFile<E> *
ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
ObjectFile<E> *obj = new ObjectFile<E>(mf);
obj->archive_name = archive_name;
obj->is_alive = archive_name.empty() || ctx.all_load;
obj->is_hidden = ctx.hidden_l;
ctx.obj_pool.emplace_back(obj);
return obj;
};
template <typename E>
void ObjectFile<E>::parse(Context<E> &ctx) {
if (get_file_type(this->mf) == FileType::LLVM_BITCODE) {
// Open an compiler IR file
load_lto_plugin(ctx);
this->lto_module =
ctx.lto.module_create_from_memory(this->mf->data, this->mf->size);
if (!this->lto_module)
Fatal(ctx) << *this << ": lto_module_create_from_memory failed";
// Read a symbol table
parse_lto_symbols(ctx);
return;
}
parse_sections(ctx);
parse_symbols(ctx);
MachHeader &mach_hdr = *(MachHeader *)this->mf->data;
if (mach_hdr.flags & MH_SUBSECTIONS_VIA_SYMBOLS)
split_subsections_via_symbols(ctx);
else
init_subsections(ctx);
sort(subsections, [](Subsection<E> *a, Subsection<E> *b) {
return a->input_addr < b->input_addr;
});
fix_subsec_members(ctx);
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec)
isec->parse_relocations(ctx);
if (unwind_sec)
parse_compact_unwind(ctx, *unwind_sec);
}
template <typename E>
void ObjectFile<E>::parse_sections(Context<E> &ctx) {
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_symbols(Context<E> &ctx) {
SymtabCommand *cmd = (SymtabCommand *)find_load_command(ctx, LC_SYMTAB);
if (!cmd)
return;
mach_syms = {(MachSym *)(this->mf->data + cmd->symoff), cmd->nsyms};
this->syms.reserve(mach_syms.size());
i64 nlocal = 0;
for (MachSym &msym : mach_syms)
if (!msym.is_extern)
nlocal++;
local_syms.reserve(nlocal);
for (i64 i = 0; i < mach_syms.size(); i++) {
MachSym &msym = mach_syms[i];
std::string_view name = (char *)(this->mf->data + cmd->stroff + msym.stroff);
if (msym.is_extern) {
this->syms.push_back(get_symbol(ctx, name));
} else {
local_syms.emplace_back(name);
Symbol<E> &sym = local_syms.back();
sym.file = this;
sym.subsec = nullptr;
sym.scope = SCOPE_LOCAL;
sym.is_common = false;
sym.is_weak_def = false;
switch (msym.type) {
case N_UNDF:
Fatal(ctx) << sym << ": local undef symbol?";
case N_ABS:
sym.value = msym.value;
break;
case N_SECT:
// `value` and `subsec` will be filled by split_subsections
break;
default:
Fatal(ctx) << *this << ": unknown symbol type for "
<< sym << ": " << (u64)msym.type;
}
this->syms.push_back(&sym);
}
}
}
struct SplitRegion {
u32 offset;
u32 size;
u32 symidx;
bool is_alt_entry;
};
template <typename E>
struct SplitInfo {
InputSection<E> *isec;
std::vector<SplitRegion> regions;
};
template <typename E>
static std::vector<SplitInfo<E>>
split_regular_sections(Context<E> &ctx, ObjectFile<E> &file) {
std::vector<SplitInfo<E>> vec(file.sections.size());
for (i64 i = 0; i < file.sections.size(); i++)
if (InputSection<E> *isec = file.sections[i].get())
if (!isec->hdr.match("__TEXT", "__cstring"))
vec[i].isec = isec;
// Find all symbols whose type is N_SECT.
for (i64 i = 0; i < file.mach_syms.size(); i++) {
MachSym &msym = file.mach_syms[i];
if (msym.type == N_SECT && vec[msym.sect - 1].isec) {
SplitRegion r;
r.offset = msym.value - vec[msym.sect - 1].isec->hdr.addr;
r.symidx = i;
r.is_alt_entry = (msym.desc & N_ALT_ENTRY);
vec[msym.sect - 1].regions.push_back(r);
}
}
std::erase_if(vec, [](const SplitInfo<E> &info) { return !info.isec; });
sort(vec, [](const SplitInfo<E> &a, const SplitInfo<E> &b) {
return a.isec->hdr.addr < b.isec->hdr.addr;
});
// Fix regions so that they cover the entire section without overlapping.
for (SplitInfo<E> &info : vec) {
std::vector<SplitRegion> &r = info.regions;
if (r.empty()) {
r.push_back({0, (u32)info.isec->hdr.size, (u32)-1, false});
continue;
}
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_via_symbols(Context<E> &ctx) {
sym_to_subsec.resize(mach_syms.size());
auto add = [&](InputSection<E> &isec, u32 offset, u32 size, u8 p2align) {
Subsection<E> *subsec = new Subsection<E>{
.isec = isec,
.input_offset = offset,
.input_size = size,
.input_addr = (u32)(isec.hdr.addr + offset),
.p2align = p2align,
};
subsec_pool.emplace_back(subsec);
subsections.push_back(subsec);
};
// Split regular sections into subsections.
for (SplitInfo<E> &info : split_regular_sections(ctx, *this)) {
InputSection<E> &isec = *info.isec;
for (SplitRegion &r : info.regions) {
if (!r.is_alt_entry)
add(isec, r.offset, r.size, isec.hdr.p2align);
if (r.symidx != -1)
sym_to_subsec[r.symidx] = subsections.back();
}
}
// Split __cstring section.
for (std::unique_ptr<InputSection<E>> &isec : sections) {
if (isec && isec->hdr.match("__TEXT", "__cstring")) {
std::string_view str = isec->contents;
size_t pos = 0;
while (pos < str.size()) {
size_t end = str.find('\0', pos);
if (end == str.npos)
Fatal(ctx) << *this << " corruupted __TEXT,__cstring";
end = str.find_first_not_of('\0', end);
if (end == str.npos)
end = str.size();
// A constant string in __cstring has no alignment info, so we
// need to infer it.
u8 p2align = std::min<u8>(isec->hdr.p2align, std::countr_zero(pos));
add(*isec, pos, end - pos, p2align);
pos = end;
}
}
}
}
template <typename E>
void ObjectFile<E>::init_subsections(Context<E> &ctx) {
subsections.resize(sections.size());
for (i64 i = 0; i < sections.size(); i++) {
if (InputSection<E> *isec = sections[i].get()) {
Subsection<E> *subsec = new Subsection<E>{
.isec = *isec,
.input_offset = 0,
.input_size = (u32)isec->hdr.size,
.input_addr = (u32)isec->hdr.addr,
.p2align = (u8)isec->hdr.p2align,
};
subsec_pool.emplace_back(subsec);
subsections[i] = subsec;
}
}
sym_to_subsec.resize(mach_syms.size());
for (i64 i = 0; i < mach_syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (msym.type == N_SECT)
sym_to_subsec[i] = subsections[msym.sect - 1];
}
std::erase(subsections, nullptr);
}
// Fix local symbols `subsec` members.
template <typename E>
void ObjectFile<E>::fix_subsec_members(Context<E> &ctx) {
for (i64 i = 0; i < mach_syms.size(); i++) {
MachSym &msym = mach_syms[i];
Symbol<E> &sym = *this->syms[i];
if (!msym.is_extern && msym.type == N_SECT) {
Subsection<E> *subsec = sym_to_subsec[i];
if (!subsec)
subsec = find_subsection(ctx, msym.value);
if (subsec) {
sym.subsec = subsec;
sym.value = msym.value - subsec->input_addr;
} else {
// Subsec is null if a symbol is in a __compact_unwind.
sym.subsec = nullptr;
sym.value = msym.value;
}
}
}
}
template <typename E>
void ObjectFile<E>::parse_data_in_code(Context<E> &ctx) {
if (auto *cmd = (LinkEditDataCommand *)find_load_command(ctx, LC_DATA_IN_CODE)) {
data_in_code_entries = {
(DataInCodeEntry *)(this->mf->data + cmd->dataoff),
cmd->datasize / sizeof(DataInCodeEntry),
};
}
}
template <typename E>
std::vector<std::string> ObjectFile<E>::get_linker_options(Context<E> &ctx) {
if (get_file_type(this->mf) == FileType::LLVM_BITCODE)
return {};
auto *cmd = (LinkerOptionCommand *)find_load_command(ctx, LC_LINKER_OPTION);
if (!cmd)
return {};
char *buf = (char *)cmd + sizeof(*cmd);
std::vector<std::string> vec;
for (i64 i = 0; i < cmd->count; i++) {
vec.push_back(buf);
buf += vec.back().size() + 1;
}
return vec;
}
template <typename E>
LoadCommand *ObjectFile<E>::find_load_command(Context<E> &ctx, u32 type) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
if (lc.cmd == type)
return &lc;
p += lc.cmdsize;
}
return nullptr;
}
template <typename E>
Subsection<E> *ObjectFile<E>::find_subsection(Context<E> &ctx, u32 addr) {
auto it = std::upper_bound(subsections.begin(), subsections.end(), addr,
[&](u32 addr, Subsection<E> *subsec) {
return addr < subsec->input_addr;
});
if (it == subsections.begin())
return nullptr;
return *(it - 1);
}
template <typename E>
Symbol<E> *ObjectFile<E>::find_symbol(Context<E> &ctx, u32 addr) {
for (i64 i = 0; i < mach_syms.size(); i++)
if (MachSym &msym = mach_syms[i]; msym.is_extern && msym.value == addr)
return this->syms[i];
return nullptr;
}
template <typename E>
void ObjectFile<E>::parse_compact_unwind(Context<E> &ctx, MachSection &hdr) {
if (hdr.size % sizeof(CompactUnwindEntry))
Fatal(ctx) << *this << ": invalid __compact_unwind section size";
i64 num_entries = hdr.size / sizeof(CompactUnwindEntry);
unwind_records.reserve(num_entries);
CompactUnwindEntry *src = (CompactUnwindEntry *)(this->mf->data + hdr.offset);
// Read compact unwind entries
for (i64 i = 0; i < num_entries; i++)
unwind_records.emplace_back(src[i].code_len, src[i].encoding);
// Read relocations
MachRel *mach_rels = (MachRel *)(this->mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++) {
MachRel &r = mach_rels[i];
if (r.offset >= hdr.size)
Fatal(ctx) << *this << ": relocation offset too large: " << i;
i64 idx = r.offset / sizeof(CompactUnwindEntry);
UnwindRecord<E> &dst = unwind_records[idx];
auto error = [&] {
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
};
if (r.is_pcrel || r.p2size != 3 || r.type)
error();
switch (r.offset % sizeof(CompactUnwindEntry)) {
case offsetof(CompactUnwindEntry, code_start): {
if (r.is_extern)
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_extern) {
dst.personality = this->syms[r.idx];
} else {
u32 addr = *(ul32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
dst.personality = find_symbol(ctx, addr);
if (!dst.personality)
Fatal(ctx) << *this << ": __compact_unwind: unsupported local "
<< "personality reference: " << i;
}
break;
case offsetof(CompactUnwindEntry, lsda): {
if (r.is_extern)
error();
u32 addr = *(ul32 *)((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:
error();
}
}
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/archive
// 4. Weak Defined symbol in a DSO/archive
// 5. Common symbol
// 6. Common symbol in an archive
// 7. Unclaimed (nonexistent) symbol
//
// Ties are broken by file priority.
template <typename E>
static u64 get_rank(InputFile<E> *file, bool is_common, bool is_weak) {
if (is_common) {
assert(!file->is_dylib);
if (!file->is_alive)
return (6 << 24) + file->priority;
return (5 << 24) + file->priority;
}
if (file->is_dylib || !file->is_alive) {
if (is_weak)
return (4 << 24) + file->priority;
return (3 << 24) + file->priority;
}
if (is_weak)
return (2 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
static u64 get_rank(Symbol<E> &sym) {
if (!sym.file)
return 7 << 24;
return get_rank(sym.file, sym.is_common, sym.is_weak_def);
}
template <typename E>
void ObjectFile<E>::resolve_symbols(Context<E> &ctx) {
auto is_private_extern = [&](MachSym &msym) {
return this->is_hidden || msym.is_private_extern ||
((msym.desc & N_WEAK_REF) && (msym.desc & N_WEAK_DEF));
};
auto merge_scope = [&](Symbol<E> &sym, MachSym &msym) {
// If at least one symbol defines it as an EXTERN symbol,
// the result is an EXTERN symbol instead of PRIVATE_EXTERN,
// so that the symbol is exported.
if (sym.scope == SCOPE_EXTERN)
return SCOPE_EXTERN;
return is_private_extern(msym) ? SCOPE_PRIVATE_EXTERN : SCOPE_EXTERN;
};
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.is_extern || msym.is_undef())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
bool is_weak_def = (msym.desc & N_WEAK_DEF);
sym.scope = merge_scope(sym, msym);
if (get_rank(this, msym.is_common(), is_weak_def) < get_rank(sym)) {
sym.file = this;
sym.is_imported = false;
sym.is_weak_def = is_weak_def;
switch (msym.type) {
case N_UNDF:
assert(msym.is_common());
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = true;
break;
case N_ABS:
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = false;
break;
case N_SECT:
sym.subsec = sym_to_subsec[i];
sym.value = msym.value - sym.subsec->input_addr;
sym.is_common = false;
break;
default:
Fatal(ctx) << sym << ": unknown symbol type: " << (u64)msym.type;
}
}
}
}
template <typename E>
bool ObjectFile<E>::is_objc_object(Context<E> &ctx) {
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec)
if (isec->hdr.match("__DATA", "__objc_catlist") ||
isec->hdr.match("__TEXT", "__swift"))
return true;
for (i64 i = 0; i < this->syms.size(); i++)
if (!mach_syms[i].is_undef() && mach_syms[i].is_extern &&
this->syms[i]->name.starts_with("_OBJC_CLASS_$_"))
return true;
return false;
}
template <typename E>
void
ObjectFile<E>::mark_live_objects(Context<E> &ctx,
std::function<void(ObjectFile<E> *)> feeder) {
assert(this->is_alive);
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.is_extern)
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (!sym.file)
continue;
bool keep = msym.is_undef() || (msym.is_common() && !sym.is_common);
if (keep && !sym.file->is_alive.exchange(true))
if (!sym.file->is_dylib)
feeder((ObjectFile<E> *)sym.file);
}
}
template <typename E>
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->mach_syms.size(); i++) {
Symbol<E> &sym = *this->syms[i];
MachSym &msym = mach_syms[i];
if (sym.file == this && sym.is_common) {
InputSection<E> *isec = get_common_sec(ctx);
Subsection<E> *subsec = new Subsection<E>{
.isec = *isec,
.input_size = (u32)msym.value,
.p2align = (u8)msym.p2align,
};
subsections.emplace_back(subsec);
sym.is_imported = false;
sym.is_weak_def = false;
sym.subsec = subsec;
sym.value = 0;
sym.is_common = false;
}
}
}
template <typename E>
void ObjectFile<E>::check_duplicate_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->mach_syms.size(); i++) {
Symbol<E> *sym = this->syms[i];
MachSym &msym = mach_syms[i];
if (sym && sym->file && sym->file != this && !msym.is_undef() &&
!msym.is_common() && !(msym.desc & N_WEAK_DEF))
Error(ctx) << "duplicate symbol: " << *this << ": " << *sym->file
<< ": " << *sym;
}
}
template <typename E>
InputSection<E> *ObjectFile<E>::get_common_sec(Context<E> &ctx) {
if (!common_sec) {
MachSection *hdr = new MachSection;
common_hdr.reset(hdr);
memset(hdr, 0, sizeof(*hdr));
hdr->set_segname("__DATA");
hdr->set_sectname("__common");
hdr->type = S_ZEROFILL;
common_sec = new InputSection<E>(ctx, *this, *hdr);
sections.emplace_back(common_sec);
}
return common_sec;
}
template <typename E>
void ObjectFile<E>::parse_lto_symbols(Context<E> &ctx) {
i64 nsyms = ctx.lto.module_get_num_symbols(this->lto_module);
this->syms.reserve(nsyms);
this->mach_syms2.reserve(nsyms);
for (i64 i = 0; i < nsyms; i++) {
std::string_view name = ctx.lto.module_get_symbol_name(this->lto_module, i);
this->syms.push_back(get_symbol(ctx, name));
u32 attr = ctx.lto.module_get_symbol_attribute(this->lto_module, i);
MachSym msym = {};
msym.p2align = (attr & LTO_SYMBOL_ALIGNMENT_MASK);
switch (attr & LTO_SYMBOL_DEFINITION_MASK) {
case LTO_SYMBOL_DEFINITION_REGULAR:
case LTO_SYMBOL_DEFINITION_TENTATIVE:
case LTO_SYMBOL_DEFINITION_WEAK:
msym.type = N_ABS;
break;
case LTO_SYMBOL_DEFINITION_UNDEFINED:
case LTO_SYMBOL_DEFINITION_WEAKUNDEF:
msym.type = N_UNDF;
break;
default:
unreachable();
}
switch (attr & LTO_SYMBOL_SCOPE_MASK) {
case 0:
case LTO_SYMBOL_SCOPE_INTERNAL:
case LTO_SYMBOL_SCOPE_HIDDEN:
break;
case LTO_SYMBOL_SCOPE_DEFAULT:
case LTO_SYMBOL_SCOPE_PROTECTED:
case LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN:
msym.is_extern = true;
break;
default:
unreachable();
}
mach_syms2.push_back(msym);
}
mach_syms = mach_syms2;
}
template <typename E>
DylibFile<E> *DylibFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
DylibFile<E> *dylib = new DylibFile<E>(mf);
dylib->is_alive = (ctx.needed_l || !ctx.arg.dead_strip_dylibs);
dylib->is_weak = ctx.weak_l;
ctx.dylib_pool.emplace_back(dylib);
switch (get_file_type(mf)) {
case FileType::TAPI:
dylib->parse_tapi(ctx);
break;
case FileType::MACH_DYLIB:
dylib->parse_dylib(ctx);
break;
default:
Fatal(ctx) << mf->name << ": is not a 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, save_string(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_tapi(Context<E> &ctx) {
TextDylib tbd = parse_tbd(ctx, this->mf);
for (std::string_view s : tbd.exports)
this->syms.push_back(get_symbol(ctx, s));
for (std::string_view s : tbd.weak_exports)
this->syms.push_back(get_symbol(ctx, s));
install_name = tbd.install_name;
reexported_libs = tbd.reexported_libs;
}
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>::resolve_symbols(Context<E> &ctx) {
for (Symbol<E> *sym : this->syms) {
std::scoped_lock lock(sym->mu);
if (get_rank(this, false, false) < get_rank(*sym)) {
sym->file = this;
sym->scope = SCOPE_LOCAL;
sym->is_imported = true;
sym->is_weak_def = this->is_weak;
sym->subsec = nullptr;
sym->value = 0;
sym->is_common = false;
}
}
}
#define INSTANTIATE(E) \
template class InputFile<E>; \
template class ObjectFile<E>; \
template class DylibFile<E>; \
template std::ostream &operator<<(std::ostream &, const InputFile<E> &)
INSTANTIATE_ALL;
} // namespace mold::macho
+8 -14
View File
@@ -14,8 +14,6 @@ InputSection<E>::InputSection(Context<E> &ctx, ObjectFile<E> &file,
template <typename E>
void InputSection<E>::parse_relocations(Context<E> &ctx) {
rels.reserve(hdr.nreloc);
// Parse mach-o relocations to fill `rels` vector
rels = read_relocations(ctx, file, hdr);
@@ -24,18 +22,15 @@ void InputSection<E>::parse_relocations(Context<E> &ctx) {
return a.offset < b.offset;
});
// Find subsections for this section
auto begin = std::lower_bound(
file.subsections.begin(), file.subsections.end(), hdr.addr,
[](std::unique_ptr<Subsection<E>> &subsec, u32 addr) {
// Find subsections this relocation section refers to
auto cmp = [](Subsection<E> *subsec, u32 addr) {
return subsec->input_addr < addr;
});
};
auto end = std::lower_bound(
begin, file.subsections.end(), hdr.addr + hdr.size,
[](std::unique_ptr<Subsection<E>> &subsec, u32 addr) {
return subsec->input_addr < addr;
});
auto begin = std::lower_bound(file.subsections.begin(),
file.subsections.end(), hdr.addr, cmp);
auto end = std::lower_bound(begin, file.subsections.end(),
hdr.addr + hdr.size, cmp);
// Assign each subsection a group of relocations
i64 i = 0;
@@ -54,7 +49,6 @@ void InputSection<E>::parse_relocations(Context<E> &ctx) {
#define INSTANTIATE(E) \
template class InputSection<E>
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
INSTANTIATE_ALL;
} // namespace mold::macho
+165
View File
@@ -0,0 +1,165 @@
#include "lto.h"
#include "mold.h"
#include <algorithm>
#include <cstdio>
#include <dlfcn.h>
namespace mold::macho {
LTOPlugin::~LTOPlugin() {
if (dlopen_handle)
dlclose(dlopen_handle);
}
template <typename E>
static void do_load_plugin(Context<E> &ctx) {
void *handle = dlopen(ctx.arg.lto_library.c_str(), RTLD_NOW | RTLD_GLOBAL);
if (!handle)
Fatal(ctx) << "could not open plugin file: " << dlerror();
ctx.lto.dlopen_handle = handle;
// I don't want to use a macro, but I couldn't find an easy way to
// write the same thing without a macro.
#define DLSYM(x) ctx.lto.x = (decltype(ctx.lto.x))dlsym(handle, "lto_" #x)
DLSYM(get_version);
DLSYM(get_error_message);
DLSYM(module_is_object_file);
DLSYM(module_is_object_file_for_target);
DLSYM(module_has_objc_category);
DLSYM(module_is_object_file_in_memory);
DLSYM(module_is_object_file_in_memory_for_target);
DLSYM(module_create);
DLSYM(module_create_from_memory);
DLSYM(module_create_from_memory_with_path);
DLSYM(module_create_in_local_context);
DLSYM(module_create_in_codegen_context);
DLSYM(module_create_from_fd);
DLSYM(module_create_from_fd_at_offset);
DLSYM(module_dispose);
DLSYM(module_get_target_triple);
DLSYM(module_set_target_triple);
DLSYM(module_get_num_symbols);
DLSYM(module_get_symbol_name);
DLSYM(module_get_symbol_attribute);
DLSYM(module_get_linkeropts);
DLSYM(module_get_macho_cputype);
DLSYM(module_has_ctor_dtor);
DLSYM(codegen_set_diagnostic_handler);
DLSYM(codegen_create);
DLSYM(codegen_create_in_local_context);
DLSYM(codegen_dispose);
DLSYM(codegen_add_module);
DLSYM(codegen_set_module);
DLSYM(codegen_set_debug_model);
DLSYM(codegen_set_pic_model);
DLSYM(codegen_set_cpu);
DLSYM(codegen_set_assembler_path);
DLSYM(codegen_set_assembler_args);
DLSYM(codegen_add_must_preserve_symbol);
DLSYM(codegen_write_merged_modules);
DLSYM(codegen_compile);
DLSYM(codegen_compile_to_file);
DLSYM(codegen_optimize);
DLSYM(codegen_compile_optimized);
DLSYM(api_version);
DLSYM(set_debug_options);
DLSYM(codegen_debug_options);
DLSYM(codegen_debug_options_array);
DLSYM(initialize_disassembler);
DLSYM(codegen_set_should_internalize);
DLSYM(codegen_set_should_embed_uselists);
#undef DLSYM
}
template <typename E>
void load_lto_plugin(Context<E> &ctx) {
std:call_once(ctx.lto_plugin_loaded, [&]() { do_load_plugin(ctx); });
}
template <typename E>
void do_lto(Context<E> &ctx) {
LTOCodeGen *cg = ctx.lto.codegen_create();
// Add bitcode files to CodeGen.
for (const std::string &opt : ctx.arg.mllvm)
ctx.lto.codegen_debug_options(cg, opt.c_str());
for (ObjectFile<E> *file : ctx.objs)
if (file->lto_module)
ctx.lto.codegen_add_module(cg, file->lto_module);
// Mark symbols that have to be preserved. All symbols that are not
// marked here may be internalized and deleted as an extenrally-
// visible symbol.
if (ctx.arg.dylib || ctx.arg.export_dynamic) {
for (ObjectFile<E> *file : ctx.objs) {
if (!file->lto_module) {
for (i64 i = 0; i < file->mach_syms.size(); i++) {
MachSym &msym = file->mach_syms[i];
Symbol<E> &sym = *file->syms[i];
if (msym.is_undef() && !sym.file->is_dylib &&
((ObjectFile<E> *)sym.file)->lto_module)
ctx.lto.codegen_add_must_preserve_symbol(cg, sym.name.data());
}
}
}
for (ObjectFile<E> *file : ctx.objs)
if (file->lto_module)
for (Symbol<E> *sym : file->syms)
if (sym->file == file && sym->scope != SCOPE_LOCAL)
ctx.lto.codegen_add_must_preserve_symbol(cg, sym->name.data());
}
if (ctx.arg.entry->file)
ctx.lto.codegen_add_must_preserve_symbol(cg, ctx.arg.entry->name.data());
// Run the compiler backend to do LTO.
size_t size;
u8 *data = (u8 *)ctx.lto.codegen_compile(cg, &size);
if (!data)
Fatal(ctx) << "lto_codegen_compile failed: " << ctx.lto.get_error_message();
if (!ctx.arg.object_path_lto.empty()) {
FILE *out = fopen(ctx.arg.object_path_lto.c_str(), "w");
if (!out)
Fatal(ctx) << "-object_path_lto: cannot open " << ctx.arg.object_path_lto
<< ": " << errno_string();
fwrite(data, size, 1, out);
fclose(out);
}
// Remove bitcode object files from ctx.objs.
for (ObjectFile<E> *file : ctx.objs) {
if (file->lto_module) {
file->clear_symbols();
file->is_alive = false;
}
}
std::erase_if(ctx.objs, [](InputFile<E> *file) { return !file->is_alive; });
// Add a result of LTO as a new object file.
MappedFile<Context<E>> *mf = new MappedFile<Context<E>>;
mf->name = "<LTO>";
mf->data = data;
mf->size = size;
ctx.mf_pool.emplace_back(mf);
ObjectFile<E> *obj = ObjectFile<E>::create(ctx, mf, "");
obj->parse(ctx);
obj->is_alive = true;
obj->priority = 100; // regular file starts with priority 10,000
obj->resolve_symbols(ctx);
ctx.objs.push_back(obj);
}
#define INSTANTIATE(E) \
template void load_lto_plugin(Context<E> &); \
template void do_lto(Context<E> &);
INSTANTIATE_ALL;
} // namespace mold::macho
+176
View File
@@ -0,0 +1,176 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <sys/types.h>
namespace mold::macho {
enum LTOSymbolAttributes {
LTO_SYMBOL_ALIGNMENT_MASK = 0x0000001F,
LTO_SYMBOL_PERMISSIONS_MASK = 0x000000E0,
LTO_SYMBOL_PERMISSIONS_CODE = 0x000000A0,
LTO_SYMBOL_PERMISSIONS_DATA = 0x000000C0,
LTO_SYMBOL_PERMISSIONS_RODATA = 0x00000080,
LTO_SYMBOL_DEFINITION_MASK = 0x00000700,
LTO_SYMBOL_DEFINITION_REGULAR = 0x00000100,
LTO_SYMBOL_DEFINITION_TENTATIVE = 0x00000200,
LTO_SYMBOL_DEFINITION_WEAK = 0x00000300,
LTO_SYMBOL_DEFINITION_UNDEFINED = 0x00000400,
LTO_SYMBOL_DEFINITION_WEAKUNDEF = 0x00000500,
LTO_SYMBOL_SCOPE_MASK = 0x00003800,
LTO_SYMBOL_SCOPE_INTERNAL = 0x00000800,
LTO_SYMBOL_SCOPE_HIDDEN = 0x00001000,
LTO_SYMBOL_SCOPE_DEFAULT = 0x00001800,
LTO_SYMBOL_SCOPE_PROTECTED = 0x00002000,
LTO_SYMBOL_SCOPE_DEFAULT_CAN_BE_HIDDEN = 0x00002800,
LTO_SYMBOL_COMDAT = 0x00004000,
LTO_SYMBOL_ALIAS = 0x00008000,
};
enum LTODebugModel {
LTO_DEBUG_MODEL_NONE = 0,
LTO_DEBUG_MODEL_DWARF = 1,
};
enum LTOCodegenModel {
LTO_CODEGEN_PIC_MODEL_STATIC = 0,
LTO_CODEGEN_PIC_MODEL_DYNAMIC = 1,
LTO_CODEGEN_PIC_MODEL_DYNAMIC_NO_PIC = 2,
LTO_CODEGEN_PIC_MODEL_DEFAULT = 3,
};
enum LTOCodegenDiagnosticSeverity {
LTO_DS_ERROR = 0,
LTO_DS_WARNING = 1,
LTO_DS_REMARK = 3,
LTO_DS_NOTE = 2,
};
struct LTOObjectBuffer {
const char *buffer;
size_t size;
};
using LTOModule = void;
using LTOCodeGen = void;
typedef void LTODiagnosticHandler(LTOCodegenDiagnosticSeverity severity,
const char *diag, void *ctxt);
struct LTOPlugin {
void *dlopen_handle = nullptr;
~LTOPlugin();
const char (*get_version)();
const char (*get_error_message)();
bool (*module_is_object_file)(const char *path);
bool (*module_is_object_file_for_target)(const char *path,
const char *target_triple_prefix);
bool (*module_has_objc_category)(const void *mem, size_t length);
bool (*module_is_object_file_in_memory)(const void *mem, size_t length);
bool (*module_is_object_file_in_memory_for_target)(
const void *mem, size_t length, const char *target_triple_prefix);
LTOModule *(*module_create)(const char *path);
LTOModule *(*module_create_from_memory)(const void *mem, size_t length);
LTOModule *(*module_create_from_memory_with_path)(const void *mem,
size_t length,
const char *path);
LTOModule *(*module_create_in_local_context)(const void *mem, size_t length,
const char *path);
LTOModule *(*module_create_in_codegen_context)(const void *mem, size_t length,
const char *path,
LTOCodeGen *cg);
LTOModule *(*module_create_from_fd)(int fd, const char *path,
size_t file_size);
LTOModule *(*module_create_from_fd_at_offset)(int fd, const char *path,
size_t file_size,
size_t map_size, off_t offset);
void (*module_dispose)(LTOModule *mod);
const char *(*module_get_target_triple)(LTOModule *mod);
void (*module_set_target_triple)(LTOModule *mod, const char *triple);
unsigned (*module_get_num_symbols)(LTOModule *mod);
const char *(*module_get_symbol_name)(LTOModule *mod, unsigned index);
uint32_t (*module_get_symbol_attribute)(LTOModule *mod, unsigned index);
const char *(*module_get_linkeropts)(LTOModule *mod);
bool (*module_get_macho_cputype)(LTOModule *mod, unsigned *out_cputype,
unsigned *out_cpusubtype);
bool (*module_has_ctor_dtor)(LTOModule *mod);
void (*codegen_set_diagnostic_handler)(LTOCodeGen *cg, LTODiagnosticHandler,
void *);
LTOCodeGen *(*codegen_create)();
LTOCodeGen *(*codegen_create_in_local_context)();
void (*codegen_dispose)(LTOCodeGen *cg);
bool (*codegen_add_module)(LTOCodeGen *cg, LTOModule *mod);
void (*codegen_set_module)(LTOCodeGen *cg, LTOModule *mod);
bool (*codegen_set_debug_model)(LTOCodeGen *cg, LTODebugModel);
bool (*codegen_set_pic_model)(LTOCodeGen *cg, LTOCodegenModel);
void (*codegen_set_cpu)(LTOCodeGen *cg, const char *cpu);
void (*codegen_set_assembler_path)(LTOCodeGen *cg, const char *path);
void (*codegen_set_assembler_args)(LTOCodeGen *cg, const char **args,
int nargs);
void (*codegen_add_must_preserve_symbol)(LTOCodeGen *cg, const char *symbol);
bool (*codegen_write_merged_modules)(LTOCodeGen *cg, const char *path);
const void *(*codegen_compile)(LTOCodeGen *cg, size_t *length);
bool (*codegen_compile_to_file)(LTOCodeGen *cg, const char **name);
bool (*codegen_optimize)(LTOCodeGen *cg);
const void *(*codegen_compile_optimized)(LTOCodeGen *cg, size_t *length);
unsigned (*api_version)();
void (*set_debug_options)(const char *const *options, int number);
void (*codegen_debug_options)(LTOCodeGen *cg, const char *);
void (*codegen_debug_options_array)(LTOCodeGen *cg, const char *const *,
int number);
void (*initialize_disassembler)();
void (*codegen_set_should_internalize)(LTOCodeGen *cg,
bool should_internalize);
void (*codegen_set_should_embed_uselists)(LTOCodeGen *cg,
bool should_embed_use_lists);
};
} // namespace mold::macho
+231 -179
View File
@@ -1,10 +1,11 @@
#pragma once
#include "../big-endian.h"
#include "../inttypes.h"
#include <cassert>
#include <cstdint>
#include <cstring>
#include <string>
#include <string_view>
namespace mold::macho {
@@ -19,6 +20,12 @@ typedef int16_t i16;
typedef int32_t i32;
typedef int64_t i64;
class ARM64;
class X86_64;
template <typename E>
std::string rel_to_string(u8 r_type);
static constexpr u32 FAT_MAGIC = 0xcafebabe;
static constexpr u32 MH_OBJECT = 0x1;
@@ -282,6 +289,11 @@ static constexpr u32 REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5;
static constexpr u32 REFERENCED_DYNAMICALLY = 0x0010;
static constexpr u32 SELF_LIBRARY_ORDINAL = 0x0;
static constexpr u32 MAX_LIBRARY_ORDINAL = 0xfd;
static constexpr u32 DYNAMIC_LOOKUP_ORDINAL = 0xfe;
static constexpr u32 EXECUTABLE_ORDINAL = 0xff;
static constexpr u32 N_NO_DEAD_STRIP = 0x0020;
static constexpr u32 N_DESC_DISCARDED = 0x0020;
static constexpr u32 N_WEAK_REF = 0x0040;
@@ -318,6 +330,24 @@ static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGE21 = 8;
static constexpr u32 ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9;
static constexpr u32 ARM64_RELOC_ADDEND = 10;
template <>
inline std::string rel_to_string<ARM64>(u8 type) {
switch (type) {
case ARM64_RELOC_UNSIGNED: return "ARM64_RELOC_UNSIGNED";
case ARM64_RELOC_SUBTRACTOR: return "ARM64_RELOC_SUBTRACTOR";
case ARM64_RELOC_BRANCH26: return "ARM64_RELOC_BRANCH26";
case ARM64_RELOC_PAGE21: return "ARM64_RELOC_PAGE21";
case ARM64_RELOC_PAGEOFF12: return "ARM64_RELOC_PAGEOFF12";
case ARM64_RELOC_GOT_LOAD_PAGE21: return "ARM64_RELOC_GOT_LOAD_PAGE21";
case ARM64_RELOC_GOT_LOAD_PAGEOFF12: return "ARM64_RELOC_GOT_LOAD_PAGEOFF12";
case ARM64_RELOC_POINTER_TO_GOT: return "ARM64_RELOC_POINTER_TO_GOT";
case ARM64_RELOC_TLVP_LOAD_PAGE21: return "ARM64_RELOC_TLVP_LOAD_PAGE21";
case ARM64_RELOC_TLVP_LOAD_PAGEOFF12: return "ARM64_RELOC_TLVP_LOAD_PAGEOFF12";
case ARM64_RELOC_ADDEND: return "ARM64_RELOC_ADDEND";
}
return "unknown (" + std::to_string(type) + ")";
}
static constexpr u32 X86_64_RELOC_UNSIGNED = 0;
static constexpr u32 X86_64_RELOC_SIGNED = 1;
static constexpr u32 X86_64_RELOC_BRANCH = 2;
@@ -329,33 +359,50 @@ static constexpr u32 X86_64_RELOC_SIGNED_2 = 7;
static constexpr u32 X86_64_RELOC_SIGNED_4 = 8;
static constexpr u32 X86_64_RELOC_TLV = 9;
template <>
inline std::string rel_to_string<X86_64>(u8 type) {
switch (type) {
case X86_64_RELOC_UNSIGNED: return "X86_64_RELOC_UNSIGNED";
case X86_64_RELOC_SIGNED: return "X86_64_RELOC_SIGNED";
case X86_64_RELOC_BRANCH: return "X86_64_RELOC_BRANCH";
case X86_64_RELOC_GOT_LOAD: return "X86_64_RELOC_GOT_LOAD";
case X86_64_RELOC_GOT: return "X86_64_RELOC_GOT";
case X86_64_RELOC_SUBTRACTOR: return "X86_64_RELOC_SUBTRACTOR";
case X86_64_RELOC_SIGNED_1: return "X86_64_RELOC_SIGNED_1";
case X86_64_RELOC_SIGNED_2: return "X86_64_RELOC_SIGNED_2";
case X86_64_RELOC_SIGNED_4: return "X86_64_RELOC_SIGNED_4";
case X86_64_RELOC_TLV: return "X86_64_RELOC_TLV";
}
return "unknown (" + std::to_string(type) + ")";
}
struct FatHeader {
ubig32 magic;
ubig32 nfat_arch;
ub32 magic;
ub32 nfat_arch;
};
struct FatArch {
ubig32 cputype;
ubig32 cpusubtype;
ubig32 offset;
ubig32 size;
ubig32 align;
ub32 cputype;
ub32 cpusubtype;
ub32 offset;
ub32 size;
ub32 align;
};
struct MachHeader {
u32 magic;
u32 cputype;
u32 cpusubtype;
u32 filetype;
u32 ncmds;
u32 sizeofcmds;
u32 flags;
u32 reserved;
ul32 magic;
ul32 cputype;
ul32 cpusubtype;
ul32 filetype;
ul32 ncmds;
ul32 sizeofcmds;
ul32 flags;
ul32 reserved;
};
struct LoadCommand {
u32 cmd;
u32 cmdsize;
ul32 cmd;
ul32 cmdsize;
};
struct SegmentCommand {
@@ -363,17 +410,17 @@ struct SegmentCommand {
return {segname, strnlen(segname, sizeof(segname))};
}
u32 cmd;
u32 cmdsize;
ul32 cmd;
ul32 cmdsize;
char segname[16];
u64 vmaddr;
u64 vmsize;
u64 fileoff;
u64 filesize;
u32 maxprot;
u32 initprot;
u32 nsects;
u32 flags;
ul64 vmaddr;
ul64 vmsize;
ul64 fileoff;
ul64 filesize;
ul32 maxprot;
ul32 initprot;
ul32 nsects;
ul32 flags;
};
struct MachSection {
@@ -401,138 +448,144 @@ struct MachSection {
char sectname[16];
char segname[16];
u64 addr;
u64 size;
u32 offset;
u32 p2align;
u32 reloff;
u32 nreloc;
u32 type : 8;
u32 attr : 24;
u32 reserved1;
u32 reserved2;
u32 reserved3;
ul64 addr;
ul64 size;
ul32 offset;
ul32 p2align;
ul32 reloff;
ul32 nreloc;
u8 type;
ul24 attr;
ul32 reserved1;
ul32 reserved2;
ul32 reserved3;
};
struct DylibCommand {
u32 cmd;
u32 cmdsize;
u32 nameoff;
u32 timestamp;
u32 current_version;
u32 compatibility_version;
ul32 cmd;
ul32 cmdsize;
ul32 nameoff;
ul32 timestamp;
ul32 current_version;
ul32 compatibility_version;
};
struct DylinkerCommand {
u32 cmd;
u32 cmdsize;
u32 nameoff;
ul32 cmd;
ul32 cmdsize;
ul32 nameoff;
};
struct SymtabCommand {
u32 cmd;
u32 cmdsize;
u32 symoff;
u32 nsyms;
u32 stroff;
u32 strsize;
ul32 cmd;
ul32 cmdsize;
ul32 symoff;
ul32 nsyms;
ul32 stroff;
ul32 strsize;
};
struct DysymtabCommand {
u32 cmd;
u32 cmdsize;
u32 ilocalsym;
u32 nlocalsym;
u32 iextdefsym;
u32 nextdefsym;
u32 iundefsym;
u32 nundefsym;
u32 tocoff;
u32 ntoc;
u32 modtaboff;
u32 nmodtab;
u32 extrefsymoff;
u32 nextrefsyms;
u32 indirectsymoff;
u32 nindirectsyms;
u32 extreloff;
u32 nextrel;
u32 locreloff;
u32 nlocrel;
ul32 cmd;
ul32 cmdsize;
ul32 ilocalsym;
ul32 nlocalsym;
ul32 iextdefsym;
ul32 nextdefsym;
ul32 iundefsym;
ul32 nundefsym;
ul32 tocoff;
ul32 ntoc;
ul32 modtaboff;
ul32 nmodtab;
ul32 extrefsymoff;
ul32 nextrefsyms;
ul32 indirectsymoff;
ul32 nindirectsyms;
ul32 extreloff;
ul32 nextrel;
ul32 locreloff;
ul32 nlocrel;
};
struct VersionMinCommand {
u32 cmd;
u32 cmdsize;
u32 version;
u32 sdk;
ul32 cmd;
ul32 cmdsize;
ul32 version;
ul32 sdk;
};
struct DyldInfoCommand {
u32 cmd;
u32 cmdsize;
u32 rebase_off;
u32 rebase_size;
u32 bind_off;
u32 bind_size;
u32 weak_bind_off;
u32 weak_bind_size;
u32 lazy_bind_off;
u32 lazy_bind_size;
u32 export_off;
u32 export_size;
ul32 cmd;
ul32 cmdsize;
ul32 rebase_off;
ul32 rebase_size;
ul32 bind_off;
ul32 bind_size;
ul32 weak_bind_off;
ul32 weak_bind_size;
ul32 lazy_bind_off;
ul32 lazy_bind_size;
ul32 export_off;
ul32 export_size;
};
struct UUIDCommand {
u32 cmd;
u32 cmdsize;
ul32 cmd;
ul32 cmdsize;
u8 uuid[16];
};
struct RpathCommand {
u32 cmd;
u32 cmdsize;
u32 path_off;
ul32 cmd;
ul32 cmdsize;
ul32 path_off;
};
struct LinkEditDataCommand {
u32 cmd;
u32 cmdsize;
u32 dataoff;
u32 datasize;
ul32 cmd;
ul32 cmdsize;
ul32 dataoff;
ul32 datasize;
};
struct BuildToolVersion {
u32 tool;
u32 version;
ul32 tool;
ul32 version;
};
struct BuildVersionCommand {
u32 cmd;
u32 cmdsize;
u32 platform;
u32 minos;
u32 sdk;
u32 ntools;
ul32 cmd;
ul32 cmdsize;
ul32 platform;
ul32 minos;
ul32 sdk;
ul32 ntools;
};
struct EntryPointCommand {
u32 cmd;
u32 cmdsize;
u64 entryoff;
u64 stacksize;
ul32 cmd;
ul32 cmdsize;
ul64 entryoff;
ul64 stacksize;
};
struct SourceVersionCommand {
u32 cmd;
u32 cmdsize;
u64 version;
ul32 cmd;
ul32 cmdsize;
ul64 version;
};
struct DataInCodeEntry {
u32 offset;
u16 length;
u16 kind;
ul32 offset;
ul16 length;
ul16 kind;
};
struct LinkerOptionCommand {
ul32 cmd;
ul32 cmdsize;
ul32 count;
};
// This struct is named `n_list` on BSD and macOS.
@@ -542,34 +595,33 @@ struct MachSym {
}
bool is_common() const {
return type == N_UNDF && ext && value;
return type == N_UNDF && is_extern && value;
}
u32 stroff;
u8 ext : 1;
ul32 stroff;
u8 is_extern : 1;
u8 type : 3;
u8 pext : 1;
u8 is_private_extern : 1;
u8 stub : 3;
u8 sect;
union {
u16 desc;
ul16 desc;
struct {
u16 : 8;
u16 p2align : 4;
u16 : 4;
u8 padding;
u8 p2align : 4;
};
};
u64 value;
ul64 value;
};
// This struct is named `relocation_info` on BSD and macOS.
struct MachRel {
u32 offset;
u32 idx : 24;
u32 is_pcrel : 1;
u32 p2size : 2;
u32 is_extern : 1;
u32 type : 4;
ul32 offset;
ul24 idx;
u8 is_pcrel : 1;
u8 p2size : 2;
u8 is_extern : 1;
u8 type : 4;
};
// __TEXT,__unwind_info section contents
@@ -580,47 +632,47 @@ static constexpr u32 UNWIND_SECOND_LEVEL_COMPRESSED = 3;
static constexpr u32 UNWIND_PERSONALITY_MASK = 0x30000000;
struct UnwindSectionHeader {
u32 version;
u32 encoding_offset;
u32 encoding_count;
u32 personality_offset;
u32 personality_count;
u32 page_offset;
u32 page_count;
ul32 version;
ul32 encoding_offset;
ul32 encoding_count;
ul32 personality_offset;
ul32 personality_count;
ul32 page_offset;
ul32 page_count;
};
struct UnwindFirstLevelPage {
u32 func_addr;
u32 page_offset;
u32 lsda_offset;
ul32 func_addr;
ul32 page_offset;
ul32 lsda_offset;
};
struct UnwindSecondLevelPage {
u32 kind;
u16 page_offset;
u16 page_count;
u16 encoding_offset;
u16 encoding_count;
ul32 kind;
ul16 page_offset;
ul16 page_count;
ul16 encoding_offset;
ul16 encoding_count;
};
struct UnwindLsdaEntry {
u32 func_addr;
u32 lsda_addr;
ul32 func_addr;
ul32 lsda_addr;
};
struct UnwindPageEntry {
u32 func_addr : 24;
u32 encoding : 8;
ul24 func_addr;
u8 encoding;
};
// __LD,__compact_unwind section contents
struct CompactUnwindEntry {
u64 code_start;
u32 code_len;
u32 encoding;
u64 personality;
u64 lsda;
ul64 code_start;
ul32 code_len;
ul32 encoding;
ul64 personality;
ul64 lsda;
};
// __LINKEDIT,__code_signature
@@ -635,39 +687,39 @@ static constexpr u32 CS_EXECSEG_MAIN_BINARY = 1;
static constexpr u32 CS_HASHTYPE_SHA256 = 2;
struct CodeSignatureHeader {
ubig32 magic;
ubig32 length;
ubig32 count;
ub32 magic;
ub32 length;
ub32 count;
};
struct CodeSignatureBlobIndex {
ubig32 type;
ubig32 offset;
u32 padding;
ub32 type;
ub32 offset;
ub32 padding;
};
struct CodeSignatureDirectory {
ubig32 magic;
ubig32 length;
ubig32 version;
ubig32 flags;
ubig32 hash_offset;
ubig32 ident_offset;
ubig32 n_special_slots;
ubig32 n_code_slots;
ubig32 code_limit;
ub32 magic;
ub32 length;
ub32 version;
ub32 flags;
ub32 hash_offset;
ub32 ident_offset;
ub32 n_special_slots;
ub32 n_code_slots;
ub32 code_limit;
u8 hash_size;
u8 hash_type;
u8 platform;
u8 page_size;
ubig32 spare2;
ubig32 scatter_offset;
ubig32 team_offset;
ubig32 spare3;
ubig64 code_limit64;
ubig64 exec_seg_base;
ubig64 exec_seg_limit;
ubig64 exec_seg_flags;
ub32 spare2;
ub32 scatter_offset;
ub32 team_offset;
ub32 spare3;
ub64 code_limit64;
ub64 exec_seg_base;
ub64 exec_seg_limit;
ub64 exec_seg_flags;
};
struct ARM64 {
+559 -112
View File
@@ -1,6 +1,8 @@
#include "mold.h"
#include "../archive-file.h"
#include "../cmdline.h"
#include "../output-file.h"
#include "../sha.h"
#include <cstdlib>
#include <fcntl.h>
@@ -9,6 +11,9 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tbb/concurrent_vector.h>
#include <tbb/global_control.h>
#include <tbb/parallel_for_each.h>
namespace mold::macho {
@@ -20,9 +25,93 @@ split_string(std::string_view str, char sep) {
return {str.substr(0, pos), str.substr(pos + 1)};
}
template <typename E>
static bool has_lto_obj(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs)
if (file->lto_module)
return true;
return false;
}
template <typename E>
static void resolve_symbols(Context<E> &ctx) {
Timer t(ctx, "resolve_symbols");
auto for_each_file = [&](std::function<void(InputFile<E> *)> fn) {
tbb::parallel_for_each(ctx.objs, fn);
tbb::parallel_for_each(ctx.dylibs, fn);
};
for_each_file([&](InputFile<E> *file) { file->resolve_symbols(ctx); });
for (std::string_view name : ctx.arg.u)
if (InputFile<E> *file = get_symbol(ctx, name)->file)
file->is_alive = true;
if (InputFile<E> *file = ctx.arg.entry->file)
file->is_alive = true;
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++) {
live_objs[i]->mark_live_objects(ctx, [&](ObjectFile<E> *file) {
live_objs.push_back(file);
});
}
// Remove symbols of eliminated files.
for_each_file([&](InputFile<E> *file) {
if (!file->is_alive)
file->clear_symbols();
});
std::erase_if(ctx.objs, [](InputFile<E> *file) { return !file->is_alive; });
std::erase_if(ctx.dylibs, [](InputFile<E> *file) { return !file->is_alive; });
for_each_file([&](InputFile<E> *file) { file->resolve_symbols(ctx); });
if (has_lto_obj(ctx))
do_lto(ctx);
}
template <typename E>
static void handle_exported_symbols_list(Context<E> &ctx) {
Timer t(ctx, "handle_exported_symbols_list");
if (ctx.arg.exported_symbols_list.empty())
return;
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->syms)
if (sym && sym->file == file)
if (sym->scope == SCOPE_EXTERN || sym->scope == SCOPE_PRIVATE_EXTERN)
sym->scope = ctx.arg.exported_symbols_list.find(sym->name)
? SCOPE_EXTERN : SCOPE_PRIVATE_EXTERN;
});
}
template <typename E>
static void handle_unexported_symbols_list(Context<E> &ctx) {
Timer t(ctx, "handle_unexported_symbols_list");
if (ctx.arg.unexported_symbols_list.empty())
return;
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Symbol<E> *sym : file->syms)
if (sym && sym->file == file)
if (sym->scope == SCOPE_EXTERN &&
ctx.arg.unexported_symbols_list.find(sym->name))
sym->scope = SCOPE_PRIVATE_EXTERN;
});
}
template <typename E>
static void create_internal_file(Context<E> &ctx) {
ObjectFile<E> *obj = new ObjectFile<E>;
obj->is_alive = true;
obj->mach_syms = obj->mach_syms2;
ctx.obj_pool.emplace_back(obj);
ctx.objs.push_back(obj);
@@ -38,7 +127,7 @@ static void create_internal_file(Context<E> &ctx) {
switch (ctx.output_type) {
case MH_EXECUTE: {
Symbol<E> *sym = add("__mh_execute_header");
sym->is_extern = true;
sym->scope = SCOPE_EXTERN;
sym->referenced_dynamically = true;
sym->value = ctx.arg.pagezero_size;
break;
@@ -52,6 +141,30 @@ static void create_internal_file(Context<E> &ctx) {
default:
unreachable();
}
add("___dso_handle");
}
// Remove unreferenced subsections to eliminate code and data
// referenced by duplicated weakdef symbols.
template <typename E>
static void remove_unreferenced_subsections(Context<E> &ctx) {
Timer t(ctx, "remove_unreferenced_subsections");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (i64 i = 0; i < file->mach_syms.size(); i++) {
MachSym &msym = file->mach_syms[i];
Symbol<E> &sym = *file->syms[i];
if (sym.file != file && (msym.type == N_SECT) && (msym.desc & N_WEAK_DEF))
file->sym_to_subsec[i]->is_alive = false;
}
});
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
std::erase_if(file->subsections, [](Subsection<E> *subsec) {
return !subsec->is_alive;
});
});
}
template <typename E>
@@ -66,32 +179,32 @@ static bool compare_segments(const std::unique_ptr<OutputSegment<E>> &a,
return 1;
if (name == "__DATA")
return 2;
return INT_MAX;
if (name == "__LINKEDIT")
return 4;
return 3;
};
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;
std::string_view x = a->cmd.get_segname();
std::string_view y = b->cmd.get_segname();
return std::tuple{get_rank(x), x} < std::tuple{get_rank(y), y};
}
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;
auto is_bss = [](const Chunk<E> *x) {
return x->hdr.type == S_ZEROFILL || x->hdr.type == S_THREAD_LOCAL_ZEROFILL;
};
if (is_bss(a) != is_bss(b))
return !is_bss(a);
static const std::string_view rank[] = {
// __TEXT
"__mach_header",
"__text",
"__StaticInit",
"__stubs",
"__stub_helper",
"__gcc_except_tab",
@@ -99,8 +212,11 @@ static bool compare_chunks(const Chunk<E> *a, const Chunk<E> *b) {
"__unwind_info",
// __DATA_CONST
"__got",
"__const",
// __DATA
"__mod_init_func",
"__la_symbol_ptr",
"__thread_ptrs",
"__data",
"__thread_ptr",
"__thread_data",
@@ -122,30 +238,173 @@ static bool compare_chunks(const Chunk<E> *a, const Chunk<E> *b) {
"__code_signature",
};
auto get_rank = [](const Chunk<E> *chunk) -> i64 {
std::string_view name = chunk->hdr.get_sectname();
auto get_rank = [](std::string_view name) {
i64 i = 0;
for (; i < sizeof(rank) / sizeof(rank[0]); i++)
if (name == rank[i])
return i;
return INT_MAX;
return i;
};
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;
std::string_view x = a->hdr.get_sectname();
std::string_view y = b->hdr.get_sectname();
return std::tuple{get_rank(x), x} < std::tuple{get_rank(y), y};
}
template <typename E>
static void claim_unresolved_symbols(Context<E> &ctx) {
Timer t(ctx, "claim_unresolved_symbols");
for (std::string_view name : ctx.arg.U)
if (Symbol<E> *sym = get_symbol(ctx, name); !sym->file)
sym->is_imported = true;
for (ObjectFile<E> *file : ctx.objs) {
for (i64 i = 0; i < file->mach_syms.size(); i++) {
MachSym &msym = file->mach_syms[i];
if (!msym.is_extern || !msym.is_undef())
continue;
Symbol<E> &sym = *file->syms[i];
std::scoped_lock lock(sym.mu);
if (sym.is_imported) {
if (!sym.file ||
(!sym.file->is_dylib && file->priority < sym.file->priority)) {
sym.file = file;
sym.scope = SCOPE_PRIVATE_EXTERN;
sym.is_imported = true;
sym.subsec = nullptr;
sym.value = 0;
sym.is_common = false;
}
}
}
}
}
template <typename E>
static void merge_cstring_sections(Context<E> &ctx) {
Timer t(ctx, "merge_cstring_sections");
struct Entry {
Entry(Subsection<E> *subsec) : owner(subsec) {}
Entry(const Entry &other) :
owner(other.owner.load()), p2align(other.p2align.load()) {}
std::atomic<Subsection<E> *> owner = nullptr;
std::atomic_uint8_t p2align = 0;
};
struct SubsecRef {
Subsection<E> &subsec;
u64 hash = 0;
Entry *ent = nullptr;
};
std::vector<std::vector<SubsecRef>> vec(ctx.objs.size());
// Estimate the number of unique strings.
HyperLogLog estimator;
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
ObjectFile<E> *file = ctx.objs[i];
HyperLogLog e;
for (Subsection<E> *subsec : file->subsections) {
if (&subsec->isec.osec == ctx.cstring) {
std::string_view str = subsec->get_contents();
u64 h = hash_string(str);
vec[i].push_back({*subsec, h, nullptr});
estimator.insert(h);
}
}
estimator.merge(e);
});
// Create a hash map large enough to hold all strings.
ConcurrentMap<Entry> map(estimator.get_cardinality() * 3 / 2);
// Insert all strings into the hash table.
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
ObjectFile<E> *file = ctx.objs[i];
for (i64 j = 0; j < vec[i].size(); j++) {
SubsecRef &ref = vec[i][j];
std::string_view s = ref.subsec.get_contents();
ref.ent = map.insert(s, ref.hash, {&ref.subsec}).first;
Subsection<E> *existing = ref.ent->owner;
while (existing->isec.file.priority < file->priority &&
!ref.ent->owner.compare_exchange_weak(existing, &ref.subsec));
update_maximum(ref.ent->p2align, ref.subsec.p2align.load());
}
});
// Decide who will become the owner for each subsection.
tbb::parallel_for((i64)0, (i64)ctx.objs.size(), [&](i64 i) {
ObjectFile<E> *file = ctx.objs[i];
for (i64 j = 0; j < vec[i].size(); j++) {
SubsecRef &ref = vec[i][j];
if (ref.ent->owner != &ref.subsec) {
ref.subsec.is_coalesced = true;
ref.subsec.replacer = ref.ent->owner;
static Counter counter("num_merged_strings");
counter++;
}
}
});
// Merge strings
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (isec)
for (Relocation<E> &r : isec->rels)
if (r.subsec && r.subsec->is_coalesced)
r.subsec = r.subsec->replacer;
});
auto replace = [&](InputFile<E> *file) {
for (Symbol<E> *sym : file->syms)
if (sym->subsec && sym->subsec->is_coalesced)
sym->subsec = sym->subsec->replacer;
};
tbb::parallel_for_each(ctx.objs, replace);
tbb::parallel_for_each(ctx.dylibs, replace);
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
std::erase_if(file->subsections, [](Subsection<E> *subsec) {
return subsec->is_coalesced;
});
});
}
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());
Timer t(ctx, "create_synthetic_chunks");
// First, we add subsections specified by -order_file to output sections.
for (std::string_view name : ctx.arg.order_file)
if (Symbol<E> *sym = get_symbol(ctx, name); sym->file)
if (Subsection<E> *subsec = sym->subsec)
if (!subsec->added_to_osec)
subsec->isec.osec.add_subsec(subsec);
// Add remaining subsections to output sections.
for (ObjectFile<E> *file : ctx.objs)
for (Subsection<E> *subsec : file->subsections)
if (!subsec->added_to_osec)
subsec->isec.osec.add_subsec(subsec);
// Add output sections to segments.
for (Chunk<E> *chunk : ctx.chunks) {
if (chunk != ctx.data && chunk->is_regular &&
if (chunk != ctx.data && chunk->is_output_section &&
((OutputSection<E> *)chunk)->members.empty())
continue;
@@ -154,6 +413,7 @@ static void create_synthetic_chunks(Context<E> &ctx) {
seg->chunks.push_back(chunk);
}
// Sort segments and output sections.
sort(ctx.segments, compare_segments<E>);
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
@@ -162,15 +422,18 @@ static void create_synthetic_chunks(Context<E> &ctx) {
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)
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
for (Subsection<E> *subsec : file->subsections)
for (UnwindRecord<E> &rec : subsec->get_unwind_records())
if (rec.personality)
rec.personality->flags |= NEEDS_GOT;
});
}
template <typename E>
static void export_symbols(Context<E> &ctx) {
Timer t(ctx, "export_symbols");
ctx.got.add(ctx, get_symbol(ctx, "dyld_stub_binder"));
for (ObjectFile<E> *file : ctx.objs) {
@@ -200,6 +463,8 @@ static void export_symbols(Context<E> &ctx) {
template <typename E>
static i64 assign_offsets(Context<E> &ctx) {
Timer t(ctx, "assign_offsets");
i64 sect_idx = 1;
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
for (Chunk<E> *chunk : seg->chunks)
@@ -217,11 +482,56 @@ static i64 assign_offsets(Context<E> &ctx) {
return fileoff;
}
// An address of a symbol of type S_THREAD_LOCAL_VARIABLES is computed
// as a relative address to the beginning of the first thread-local
// section. This function finds the beginnning address.
template <typename E>
static u64 get_tls_begin(Context<E> &ctx) {
for (std::unique_ptr<OutputSegment<E>> &seg : ctx.segments)
for (Chunk<E> *chunk : seg->chunks)
if (chunk->hdr.type == S_THREAD_LOCAL_REGULAR ||
chunk->hdr.type == S_THREAD_LOCAL_ZEROFILL)
return chunk->hdr.addr;
return 0;
}
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;
get_symbol(ctx, "___dso_handle")->value = ctx.data->hdr.addr;
}
template <typename E>
static void copy_sections_to_output_file(Context<E> &ctx) {
Timer t(ctx, "copy_sections_to_output_file");
tbb::parallel_for_each(ctx.segments,
[&](std::unique_ptr<OutputSegment<E>> &seg) {
Timer t2(ctx, std::string(seg->cmd.get_segname()), &t);
// Fill text segment paddings with NOPs
if (seg->cmd.get_segname() == "__TEXT")
memset(ctx.buf + seg->cmd.fileoff, 0x90, seg->cmd.filesize);
tbb::parallel_for_each(seg->chunks, [&](Chunk<E> *sec) {
if (sec->hdr.type != S_ZEROFILL) {
Timer t3(ctx, std::string(sec->hdr.get_sectname()), &t2);
sec->copy_buf(ctx);
}
});
});
}
template <typename E>
static void compute_uuid(Context<E> &ctx) {
Timer t(ctx, "copy_sections_to_output_file");
u8 buf[SHA256_SIZE];
SHA256(ctx.buf, ctx.output_file->filesize, buf);
memcpy(ctx.uuid, buf, 16);
ctx.mach_hdr.copy_buf(ctx);
}
template <typename E>
@@ -247,14 +557,25 @@ MappedFile<Context<E>> *find_framework(Context<E> &ctx, std::string 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;
auto search = [&](std::vector<std::string> extn) -> MappedFile<Context<E>> * {
for (std::string dir : ctx.arg.library_paths) {
for (std::string e : extn) {
std::string path = dir + "/lib" + name + e;
if (MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path))
return mf;
}
}
}
return nullptr;
return nullptr;
};
// -search_paths_first
if (ctx.arg.search_paths_first)
return search({".tbd", ".dylib", ".a"});
// -search_dylibs_first
if (MappedFile<Context<E>> *mf = search({".tbd", ".dylib"}))
return mf;
return search({".a"});
}
template <typename E>
@@ -271,8 +592,7 @@ strip_universal_header(Context<E> &ctx, MappedFile<Context<E>> *mf) {
}
template <typename E>
static void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf,
bool is_needed = false) {
static void read_file(Context<E> &ctx, MappedFile<Context<E>> *mf) {
if (get_file_type(mf) == FileType::MACH_UNIVERSAL)
mf = strip_universal_header(ctx, mf);
@@ -280,20 +600,25 @@ static void read_file(Context<E> &ctx, MappedFile<Context<E>> *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:
case FileType::LLVM_BITCODE:
ctx.objs.push_back(ObjectFile<E>::create(ctx, mf, ""));
break;
case FileType::AR:
if (!ctx.all_load && !ctx.loaded_archives.insert(mf->name).second) {
// If the same .a file is specified more than once, ignore all
// but the first one because they would be ignored anyway.
break;
}
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:
Fatal(ctx) << mf->name << ": unknown file type";
break;
// Fatal(ctx) << mf->name << ": unknown file type";
}
}
@@ -323,107 +648,204 @@ read_filelist(Context<E> &ctx, std::string arg) {
return vec;
}
template <typename E>
static bool has_dylib(Context<E> &ctx, std::string_view path) {
for (DylibFile<E> *file : ctx.dylibs)
if (file->install_name == path)
return true;
return false;
}
template <typename E>
static void read_input_files(Context<E> &ctx, std::span<std::string> args) {
Timer t(ctx, "read_input_files");
auto must_find_library = [&](std::string arg) {
MappedFile<Context<E>> *mf = find_library(ctx, arg);
if (!mf)
Fatal(ctx) << "library not found: -l" << arg;
return mf;
};
while (!args.empty()) {
if (args[0].starts_with("-filelist")) {
for (std::string &path : read_filelist(ctx, args[1])) {
const std::string &opt = args[0];
args = args.subspan(1);
if (!opt.starts_with('-')) {
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, opt));
continue;
}
if (opt == "-all_load") {
ctx.all_load = true;
continue;
}
if (opt == "-noall_load") {
ctx.all_load = false;
continue;
}
if (args.empty())
Fatal(ctx) << opt << ": missing argument";
const std::string &arg = args[0];
args = args.subspan(1);
if (opt == "-filelist") {
for (std::string &path : read_filelist(ctx, arg)) {
MappedFile<Context<E>> *mf = MappedFile<Context<E>>::open(ctx, path);
if (!mf)
Fatal(ctx) << "-filepath " << args[1] << ": cannot open file: " << path;
Fatal(ctx) << "-filepath " << arg << ": 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 if (opt == "-force_load") {
bool orig = ctx.all_load;
ctx.all_load = true;
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, arg));
ctx.all_load = orig;
} else if (opt == "-framework") {
read_file(ctx, find_framework(ctx, arg));
} else if (opt == "-needed_framework") {
ctx.needed_l = true;
read_file(ctx, find_framework(ctx, arg));
} else if (opt == "-weak_framework") {
ctx.weak_l = true;
read_file(ctx, find_framework(ctx, arg));
} else if (opt == "-l") {
read_file(ctx, must_find_library(arg));
} else if (opt == "-needed-l") {
ctx.needed_l = true;
read_file(ctx, must_find_library(arg));
} else if (opt == "-hidden-l") {
ctx.hidden_l = true;
read_file(ctx, must_find_library(arg));
} else if (opt == "-weak-l") {
ctx.weak_l = true;
read_file(ctx, must_find_library(arg));
} else {
read_file(ctx, MappedFile<Context<E>>::must_open(ctx, args[0]));
args = args.subspan(1);
unreachable();
}
ctx.needed_l = false;
ctx.hidden_l = false;
ctx.weak_l = false;
}
// An object file can contain linker directives to load other object
// files or libraries, so process them if any.
for (ObjectFile<E> *file : ctx.objs) {
std::vector<std::string> opts = file->get_linker_options(ctx);
for (i64 j = 0; j < opts.size();) {
if (opts[j] == "-framework") {
read_file(ctx, find_framework(ctx, opts[j + 1]));
j += 2;
} else if (opts[j].starts_with("-l")) {
read_file(ctx, must_find_library(opts[j].substr(2)));
j++;
} else {
Fatal(ctx) << *file << ": unknown LC_LINKER_OPTION command: " << opts[j];
}
}
}
if (ctx.objs.empty())
Fatal(ctx) << "no input files";
for (ObjectFile<E> *file : ctx.objs)
file->priority = ctx.file_priority++;
for (DylibFile<E> *dylib : ctx.dylibs)
dylib->priority = ctx.file_priority++;
for (i64 i = 0; i < ctx.dylibs.size(); i++)
ctx.dylibs[i]->dylib_idx = i + 1;
}
template <typename E>
static void parse_object_files(Context<E> &ctx) {
Timer t(ctx, "parse_object_files");
tbb::parallel_for_each(ctx.objs, [&](ObjectFile<E> *file) {
file->parse(ctx);
});
}
template <typename E>
static void print_stats(Context<E> &ctx) {
for (ObjectFile<E> *file : ctx.objs) {
static Counter subsections("num_subsections");
subsections += file->subsections.size();
static Counter syms("num_syms");
syms += file->syms.size();
static Counter rels("num_rels");
for (std::unique_ptr<InputSection<E>> &isec : file->sections)
if (isec)
rels += isec->rels.size();
}
Counter::print();
}
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);
std::vector<std::string> file_args = parse_nonpositional_args(ctx);
if (ctx.arg.arch != E::cputype) {
if (ctx.arg.arch == CPU_TYPE_X86_64)
#if !defined(MOLD_DEBUG_X86_64_ONLY) && !defined(MOLD_DEBUG_ARM64_ONLY)
switch (ctx.arg.arch) {
case CPU_TYPE_X86_64:
return do_main<X86_64>(argc, argv);
case CPU_TYPE_ARM64:
return do_main<X86_64>(argc, argv);
}
#endif
Fatal(ctx) << "unknown cputype: " << ctx.arg.arch;
}
Timer t(ctx, "all");
// Handle -sectcreate
for (SectCreateOption arg : ctx.arg.sectcreate) {
MappedFile<Context<E>> *mf =
MappedFile<Context<E>>::must_open(ctx, std::string(arg.filename));
SectCreateSection<E> *sec =
new SectCreateSection<E>(ctx, arg.segname, arg.sectname, mf->get_contents());
ctx.chunk_pool.emplace_back(sec);
}
if (ctx.arg.adhoc_codesign)
ctx.code_sig.reset(new CodeSignatureSection<E>(ctx));
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);
parse_object_files(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);
}
resolve_symbols(ctx);
remove_unreferenced_subsections(ctx);
std::vector<ObjectFile<E> *> live_objs;
for (ObjectFile<E> *file : ctx.objs)
if (file->is_alive)
live_objs.push_back(file);
if (ctx.output_type == MH_EXECUTE && !ctx.arg.entry->file)
Error(ctx) << "undefined entry point symbol: " << *ctx.arg.entry;
for (i64 i = 0; i < live_objs.size(); i++)
append(live_objs, live_objs[i]->mark_live_objects(ctx));
// Handle -exported_symbol and -exported_symbols_list
handle_exported_symbols_list(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;
// Handle -unexported_symbol and -unexported_symbols_list
handle_unexported_symbols_list(ctx);
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;
@@ -434,6 +856,10 @@ static int do_main(int argc, char **argv) {
for (ObjectFile<E> *file : ctx.objs)
file->convert_common_symbols(ctx);
claim_unresolved_symbols(ctx);
merge_cstring_sections(ctx);
if (ctx.arg.dead_strip)
dead_strip(ctx);
@@ -442,34 +868,51 @@ static int do_main(int argc, char **argv) {
for (ObjectFile<E> *file : ctx.objs)
file->check_duplicate_symbols(ctx);
bool has_pagezero_seg = ctx.arg.pagezero_size;
for (i64 i = 0; i < ctx.segments.size(); i++)
ctx.segments[i]->seg_idx = i + 1;
ctx.segments[i]->seg_idx = (has_pagezero_seg ? i + 1 : i);
for (ObjectFile<E> *file : ctx.objs)
for (std::unique_ptr<Subsection<E> > &subsec : file->subsections)
for (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);
ctx.tls_begin = get_tls_begin(ctx);
fix_synthetic_symbol_values(ctx);
ctx.output_file = OutputFile<E>::open(ctx, ctx.arg.output, output_size, 0777);
ctx.output_file =
OutputFile<Context<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);
copy_sections_to_output_file(ctx);
if (ctx.code_sig)
ctx.code_sig->write_signature(ctx);
else if (ctx.arg.uuid == UUID_HASH)
compute_uuid(ctx);
ctx.output_file->close(ctx);
ctx.checkpoint();
t.stop();
if (ctx.arg.perf)
print_timer_records(ctx.timer_records);
if (ctx.arg.stats)
print_stats(ctx);
if (!ctx.arg.map.empty())
print_map(ctx);
if (ctx.arg.quick_exit) {
std::cout << std::flush;
std::cerr << std::flush;
_exit(0);
}
return 0;
}
@@ -483,7 +926,11 @@ Do not report bugs because it's too early to manage missing features as bugs.
)";
}
#ifdef MOLD_DEBUG_X86_64_ONLY
return do_main<X86_64>(argc, argv);
#else
return do_main<ARM64>(argc, argv);
#endif
}
}
+4 -4
View File
@@ -30,8 +30,9 @@ void print_map(Context<E> &ctx) {
if (file.is_alive) {
out << "[" << std::setw(3) << i << "] " << file << "\n";
for (Symbol<E> *sym : file.syms)
if (sym->file == &file)
syms.push_back({sym->get_addr(ctx), 0, (u32)i, sym->name});
if (sym && sym->file == &file)
if (!sym->subsec || sym->subsec->is_alive)
syms.push_back({sym->get_addr(ctx), 0, (u32)i, sym->name});
}
}
@@ -74,7 +75,6 @@ void print_map(Context<E> &ctx) {
#define INSTANTIATE(E) \
template void print_map(Context<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
INSTANTIATE_ALL;
} // namespace mold::macho
+269 -183
View File
@@ -1,16 +1,30 @@
#pragma once
#include "macho.h"
#include "lto.h"
#include "../mold.h"
#include <map>
#include <memory>
#include <optional>
#include <span>
#include <tbb/concurrent_hash_map.h>
#include <tbb/spin_mutex.h>
#include <tbb/task_group.h>
#include <unordered_map>
#include <unordered_set>
#include <variant>
#if MOLD_DEBUG_X86_64_ONLY
# define INSTANTIATE_ALL INSTANTIATE(X86_64)
#elif MOLD_DEBUG_ARM64_ONLY
# define INSTANTIATE_ALL INSTANTIATE(ARM64)
#else
# define INSTANTIATE_ALL \
INSTANTIATE(X86_64); \
INSTANTIATE(ARM64);
#endif
namespace mold::macho {
static constexpr i64 COMMON_PAGE_SIZE = 0x4000;
@@ -24,7 +38,7 @@ template <typename E> struct Context;
template <typename E> struct Symbol;
//
// object-file.cc
// input-files.cc
//
template <typename E>
@@ -32,108 +46,133 @@ struct Relocation {
u32 offset = 0;
u8 type = -1;
u8 p2size = 0;
bool is_pcrel = false;
bool is_pcrel : 1 = false;
bool needs_dynrel : 1 = false;
i64 addend = 0;
Symbol<E> *sym = nullptr;
Subsection<E> *subsec = nullptr;
// For range extension thunks
i32 thunk_idx = -1;
i32 thunk_sym_idx = -1;
};
template <typename E>
std::ostream &operator<<(std::ostream &out, const Relocation<E> &rel) {
out << rel_to_string<E>(rel.type);
return out;
}
template <typename E>
struct UnwindRecord {
UnwindRecord(u32 len, u32 enc) : code_len(len), encoding(enc) {}
inline u64 get_func_raddr(Context<E> &ctx) const;
u64 get_func_addr(Context<E> &ctx) const {
return subsec->get_addr(ctx) + offset;
}
Subsection<E> *subsec = nullptr;
u32 offset = 0;
u32 code_len;
u32 encoding;
Symbol<E> *personality = nullptr;
Subsection<E> *lsda = nullptr;
u32 offset = 0;
u32 code_len = 0;
u32 encoding = 0;
u32 lsda_offset = 0;
bool is_alive = false;
};
template <typename E>
class InputFile {
public:
virtual ~InputFile() = default;
virtual void resolve_symbols(Context<E> &ctx) = 0;
void clear_symbols();
MappedFile<Context<E>> *mf = nullptr;
std::string_view filename;
std::vector<Symbol<E> *> syms;
i64 priority = 0;
std::atomic_bool is_alive = false;
bool is_dylib = false;
bool is_hidden = false;
bool is_weak = false;
std::string archive_name;
virtual bool is_dylib() const = 0;
protected:
InputFile() = default;
InputFile(MappedFile<Context<E>> *mf) : mf(mf), filename(mf->name) {}
InputFile() : filename("<internal>") {}
};
template <typename E>
class ObjectFile : public InputFile<E> {
public:
ObjectFile() = default;
ObjectFile(MappedFile<Context<E>> *mf) : InputFile<E>(mf) {}
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);
Symbol<E> *find_symbol(Context<E> &ctx, u32 addr);
std::vector<std::string> get_linker_options(Context<E> &ctx);
void parse_compact_unwind(Context<E> &ctx, MachSection &hdr);
void resolve_regular_symbols(Context<E> &ctx);
void resolve_lazy_symbols(Context<E> &ctx);
void resolve_symbols(Context<E> &ctx) override;
bool is_objc_object(Context<E> &ctx);
std::vector<ObjectFile *> mark_live_objects(Context<E> &ctx);
void mark_live_objects(Context<E> &ctx,
std::function<void(ObjectFile<E> *)> feeder);
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::vector<Subsection<E> *> subsections;
std::vector<Subsection<E> *> 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;
LTOModule *lto_module = nullptr;
// For the internal file and LTO object files
std::vector<MachSym> mach_syms2;
private:
void parse_sections(Context<E> &ctx);
void parse_symtab(Context<E> &ctx);
void split_subsections(Context<E> &ctx);
void parse_symbols(Context<E> &ctx);
void split_subsections_via_symbols(Context<E> &ctx);
void init_subsections(Context<E> &ctx);
void fix_subsec_members(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);
void parse_lto_symbols(Context<E> &ctx);
MachSection *unwind_sec = nullptr;
std::unique_ptr<MachSection> common_hdr;
InputSection<E> *common_sec = nullptr;
std::vector<Subsection<E> *> subsec_pool;
};
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);
void parse_tapi(Context<E> &ctx);
void parse_dylib(Context<E> &ctx);
void resolve_symbols(Context<E> &ctx) override;
std::string_view install_name;
i64 dylib_idx = 0;
std::atomic_bool is_needed = false;
bool is_dylib() const override { return true; }
std::vector<std::string_view> reexported_libs;
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;
DylibFile(MappedFile<Context<E>> *mf) : InputFile<E>(mf) {
this->is_dylib = true;
}
};
@@ -187,13 +226,17 @@ public:
u32 input_offset = 0;
u32 input_size = 0;
u32 input_addr = 0;
u32 output_offset = -1;
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;
Subsection<E> *replacer; // Used if is_coalesced is true
std::atomic_uint8_t p2align = 0;
std::atomic_bool is_alive = true;
bool is_coalesced : 1 = false;
bool added_to_osec : 1 = false;
};
template <typename E>
@@ -205,9 +248,16 @@ read_relocations(Context<E> &ctx, ObjectFile<E> &file, const MachSection &hdr);
//
enum {
NEEDS_GOT = 1 << 0,
NEEDS_STUB = 1 << 1,
NEEDS_THREAD_PTR = 1 << 2,
NEEDS_GOT = 1 << 0,
NEEDS_STUB = 1 << 1,
NEEDS_THREAD_PTR = 1 << 2,
NEEDS_RANGE_EXTN_THUNK = 1 << 3,
};
enum {
SCOPE_LOCAL, // input file scope
SCOPE_PRIVATE_EXTERN, // output file scope (non-exported symbol)
SCOPE_EXTERN, // global scope (exported symbol)
};
template <typename E>
@@ -229,10 +279,15 @@ struct Symbol {
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;
u8 scope : 2 = SCOPE_LOCAL;
bool is_common : 1 = false;
bool is_weak_def : 1 = false;
bool is_imported : 1 = false;
bool referenced_dynamically : 1 = false;
// For range extension thunks
i32 thunk_idx = -1;
i32 thunk_sym_idx = -1;
inline u64 get_addr(Context<E> &ctx) const;
inline u64 get_got_addr(Context<E> &ctx) const;
@@ -242,6 +297,14 @@ struct Symbol {
template <typename E>
std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym);
// This operator defines a total order over symbols. This is used to
// make the output deterministic.
template <typename E>
inline bool operator<(const Symbol<E> &a, const Symbol<E> &b) {
return std::tuple{a.file->priority, a.value} <
std::tuple{b.file->priority, b.value};
}
//
// output-chunks.cc
//
@@ -253,14 +316,15 @@ public:
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:
void set_offset_regular(Context<E> &ctx, i64 fileoff, u64 vmaddr);
void set_offset_linkedit(Context<E> &ctx, i64 fileoff, u64 vmaddr);
OutputSegment(std::string_view name);
};
@@ -277,7 +341,7 @@ public:
MachSection hdr = {};
u32 sect_idx = 0;
bool is_hidden = false;
bool is_regular = false;
bool is_output_section = false;
};
template <typename E>
@@ -295,6 +359,28 @@ public:
void copy_buf(Context<E> &ctx) override;
};
template <typename E>
class RangeExtensionThunk {};
template <>
class RangeExtensionThunk<ARM64> {
public:
RangeExtensionThunk(OutputSection<ARM64> &osec)
: output_section(osec) {}
i64 size() const { return symbols.size() * ENTRY_SIZE; }
u64 get_addr(i64 idx) const;
void copy_buf(Context<ARM64> &ctx);
static constexpr i64 ENTRY_SIZE = 12;
OutputSection<ARM64> &output_section;
i32 thunk_idx = -1;
i64 offset = -1;
std::mutex mu;
std::vector<Symbol<ARM64> *> symbols;
};
template <typename E>
class OutputSection : public Chunk<E> {
public:
@@ -305,7 +391,7 @@ public:
OutputSection(Context<E> &ctx, std::string_view segname,
std::string_view sectname)
: Chunk<E>(ctx, segname, sectname) {
this->is_regular = true;
this->is_output_section = true;
}
void compute_size(Context<E> &ctx) override;
@@ -316,9 +402,13 @@ public:
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;
assert(!subsec->added_to_osec);
subsec->added_to_osec = true;
}
std::vector<Subsection<E> *> members;
std::vector<std::unique_ptr<RangeExtensionThunk<E>>> thunks;
};
class RebaseEncoder {
@@ -337,9 +427,9 @@ private:
};
template <typename E>
class OutputRebaseSection : public Chunk<E> {
class RebaseSection : public Chunk<E> {
public:
OutputRebaseSection(Context<E> &ctx)
RebaseSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__rebase") {
this->is_hidden = true;
}
@@ -353,14 +443,14 @@ public:
class BindEncoder {
public:
BindEncoder();
void add(i64 dylib_idx, std::string_view sym, i64 flags, i64 seg_idx,
i64 offset);
template <typename E> void add(Symbol<E> &sym, i64 seg_idx, i64 offset);
void finish();
std::vector<u8> buf;
private:
std::string_view last_sym;
std::string_view last_name;
i64 last_flags = -1;
i64 last_dylib = -1;
i64 last_seg = -1;
@@ -368,9 +458,9 @@ private:
};
template <typename E>
class OutputBindSection : public Chunk<E> {
class BindSection : public Chunk<E> {
public:
OutputBindSection(Context<E> &ctx)
BindSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__binding") {
this->is_hidden = true;
}
@@ -382,15 +472,15 @@ public:
};
template <typename E>
class OutputLazyBindSection : public Chunk<E> {
class LazyBindSection : public Chunk<E> {
public:
OutputLazyBindSection(Context<E> &ctx)
LazyBindSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__lazy_binding") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
this->hdr.p2align = 3;
}
void add(Context<E> &ctx, Symbol<E> &sym, i64 flags);
void add(Context<E> &ctx, Symbol<E> &sym);
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
@@ -400,14 +490,8 @@ public:
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;
@@ -416,15 +500,16 @@ private:
struct TrieNode {
std::string_view prefix;
bool is_leaf = false;
u32 flags = 0;
std::vector<std::unique_ptr<TrieNode>> children;
u64 addr = 0;
u32 flags = 0;
u32 offset = -1;
std::vector<TrieNode> children;
bool is_leaf = false;
};
static i64 common_prefix_len(std::span<Entry> entries, i64 len);
static TrieNode construct_trie(std::span<Entry> entries, i64 len);
void construct_trie(TrieNode &node, std::span<Entry> entries, i64 len,
tbb::task_group *tg, i64 grain_size, bool divide);
static i64 set_offset(TrieNode &node, i64 offset);
void write_trie(u8 *buf, TrieNode &node);
@@ -433,9 +518,9 @@ private:
};
template <typename E>
class OutputExportSection : public Chunk<E> {
class ExportSection : public Chunk<E> {
public:
OutputExportSection(Context<E> &ctx)
ExportSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__export") {
this->is_hidden = true;
}
@@ -448,9 +533,9 @@ private:
};
template <typename E>
class OutputFunctionStartsSection : public Chunk<E> {
class FunctionStartsSection : public Chunk<E> {
public:
OutputFunctionStartsSection(Context<E> &ctx)
FunctionStartsSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__func_starts") {
this->is_hidden = true;
}
@@ -462,63 +547,34 @@ public:
};
template <typename E>
class OutputSymtabSection : public Chunk<E> {
class SymtabSection : public Chunk<E> {
public:
OutputSymtabSection(Context<E> &ctx)
SymtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__symbol_table") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
this->hdr.p2align = 3;
}
void compute_size(Context<E> &ctx) override;
void copy_buf(Context<E> &ctx) override;
struct Entry {
Symbol<E> *sym;
i64 stroff;
};
std::vector<i64> symtab_offsets;
std::vector<i64> strtab_offsets;
std::vector<Entry> locals;
std::vector<Entry> globals;
std::vector<Entry> undefs;
i64 num_locals = 0;
i64 num_globals = 0;
i64 num_undefs =0;
};
template <typename E>
class OutputStrtabSection : public Chunk<E> {
class StrtabSection : public Chunk<E> {
public:
OutputStrtabSection(Context<E> &ctx)
StrtabSection(Context<E> &ctx)
: Chunk<E>(ctx, "__LINKEDIT", "__string_table") {
this->is_hidden = true;
this->hdr.p2align = std::countr_zero(8U);
this->hdr.p2align = 3;
this->hdr.size = 1;
}
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>
@@ -558,6 +614,7 @@ public:
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.reserved1 = 0;
this->hdr.reserved2 = E::stub_size;
}
@@ -580,20 +637,6 @@ public:
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:
@@ -612,7 +655,7 @@ 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.p2align = 3;
this->hdr.type = S_NON_LAZY_SYMBOL_POINTERS;
}
@@ -620,6 +663,7 @@ public:
void copy_buf(Context<E> &ctx) override;
std::vector<Symbol<E> *> syms;
std::vector<Subsection<E> *> subsections;
};
template <typename E>
@@ -627,7 +671,7 @@ 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.p2align = 3;
this->hdr.type = S_LAZY_SYMBOL_POINTERS;
}
@@ -639,7 +683,7 @@ 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.p2align = 3;
this->hdr.type = S_THREAD_LOCAL_VARIABLE_POINTERS;
}
@@ -649,6 +693,20 @@ public:
std::vector<Symbol<E> *> syms;
};
template <typename E>
class SectCreateSection : public Chunk<E> {
public:
SectCreateSection(Context<E> &ctx, std::string_view seg, std::string_view sect,
std::string_view contents)
: Chunk<E>(ctx, seg, sect), contents(contents) {
this->hdr.size = contents.size();
}
void copy_buf(Context<E> &ctx) override;
std::string_view contents;
};
//
// mapfile.cc
//
@@ -656,36 +714,6 @@ public:
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
//
@@ -709,12 +737,10 @@ parse_yaml(std::string_view str);
//
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;
std::vector<std::string_view> weak_exports;
};
template <typename E>
@@ -725,8 +751,7 @@ TextDylib parse_tbd(Context<E> &ctx, MappedFile<Context<E>> *mf);
//
template <typename E>
void parse_nonpositional_args(Context<E> &ctx,
std::vector<std::string> &remaining);
std::vector<std::string> parse_nonpositional_args(Context<E> &ctx);
//
// dead-strip.cc
@@ -735,10 +760,34 @@ void parse_nonpositional_args(Context<E> &ctx,
template <typename E>
void dead_strip(Context<E> &ctx);
//
// lto.cc
//
template <typename E>
void load_lto_plugin(Context<E> &ctx);
template <typename E>
void do_lto(Context<E> &ctx);
//
// arch-arm64.cc
//
void create_range_extension_thunks(Context<ARM64> &ctx, OutputSection<ARM64> &osec);
//
// main.cc
//
enum UuidKind { UUID_NONE, UUID_HASH, UUID_RANDOM };
struct SectCreateOption {
std::string_view segname;
std::string_view sectname;
std::string_view filename;
};
template <typename E>
struct Context {
Context() {
@@ -769,49 +818,83 @@ struct Context {
// Command-line arguments
struct {
MultiGlob exported_symbols_list;
MultiGlob unexported_symbols_list;
Symbol<E> *entry = nullptr;
UuidKind uuid = UUID_HASH;
bool ObjC = false;
bool adhoc_codesign = true;
bool adhoc_codesign = std::is_same_v<E, ARM64>;
bool color_diagnostics = false;
bool dead_strip = true;
bool dead_strip = false;
bool dead_strip_dylibs = false;
bool deduplicate = true;
bool demangle = false;
bool demangle = true;
bool dylib = false;
bool dynamic = true;
bool export_dynamic = false;
bool fatal_warnings = false;
bool noinhibit_exec = false;
bool perf = false;
bool quick_exit = true;
bool search_paths_first = true;
bool stats = false;
bool trace = false;
i64 arch = CPU_TYPE_ARM64;
i64 compatibility_version = 0;
i64 current_version = 0;
i64 filler = 0;
i64 headerpad = 256;
i64 pagezero_size = 0;
i64 platform = PLATFORM_MACOS;
i64 platform_min_version = 0;
i64 platform_sdk_version = 0;
i64 stack_size = 0;
i64 thread_count = 0;
std::string chroot;
std::string entry = "_main";
std::string final_output;
std::string install_name;
std::string lto_library;
std::string map;
std::string object_path_lto;
std::string output = "a.out";
std::vector<SectCreateOption> sectcreate;
std::vector<std::string> U;
std::vector<std::string> framework_paths;
std::vector<std::string> library_paths;
std::vector<std::string> mllvm;
std::vector<std::string> order_file;
std::vector<std::string> rpath;
std::vector<std::string> syslibroot;
std::vector<std::string> u;
} arg;
std::vector<std::string_view> cmdline_args;
u32 output_type = MH_EXECUTE;
i64 file_priority = 10000;
bool all_load = false;
bool needed_l = false;
bool hidden_l = false;
bool weak_l = false;
std::unordered_set<std::string_view> loaded_archives;
u8 uuid[16] = {};
bool has_error = false;
u64 tls_begin = 0;
tbb::concurrent_hash_map<std::string_view, Symbol<E>> symbol_map;
LTOPlugin lto = {};
std::once_flag lto_plugin_loaded;
std::unique_ptr<OutputFile<E>> output_file;
tbb::concurrent_hash_map<std::string_view, Symbol<E>, HashCmp> symbol_map;
std::unique_ptr<OutputFile<Context<E>>> output_file;
u8 *buf;
bool overwrite_output_file = false;
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;
std::vector<std::unique_ptr<Chunk<E>>> chunk_pool;
tbb::concurrent_vector<std::unique_ptr<TimerRecord>> timer_records;
@@ -832,18 +915,17 @@ struct Context {
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};
RebaseSection<E> rebase{*this};
BindSection<E> bind{*this};
LazyBindSection<E> lazy_bind{*this};
ExportSection<E> export_{*this};
FunctionStartsSection<E> function_starts{*this};
SymtabSection<E> symtab{*this};
StrtabSection<E> strtab{*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};
std::unique_ptr<CodeSignatureSection<E>> code_sig;
OutputSection<E> *text = nullptr;
OutputSection<E> *data = nullptr;
@@ -867,13 +949,15 @@ std::ostream &operator<<(std::ostream &out, const InputSection<E> &sec) {
template <typename E>
u64 Subsection<E>::get_addr(Context<E> &ctx) const {
return ctx.arg.pagezero_size + raddr;
return isec.osec.hdr.addr + output_offset;
}
template <typename E>
u64 Symbol<E>::get_addr(Context<E> &ctx) const {
if (subsec)
if (subsec) {
assert(subsec->is_alive);
return subsec->get_addr(ctx) + value;
}
if (stub_idx != -1)
return ctx.stubs.hdr.addr + stub_idx * E::stub_size;
return value;
@@ -900,7 +984,10 @@ inline Symbol<E> *get_symbol(Context<E> &ctx, std::string_view name) {
template <typename E>
inline std::ostream &operator<<(std::ostream &out, const Symbol<E> &sym) {
out << sym.name;
if (opt_demangle && sym.name.starts_with("__Z"))
out << demangle(sym.name.substr(1));
else
out << sym.name;
return out;
}
@@ -912,9 +999,8 @@ Chunk<E>::Chunk(Context<E> &ctx, std::string_view segname,
hdr.set_sectname(sectname);
}
template <typename E>
u64 UnwindRecord<E>::get_func_raddr(Context<E> &ctx) const {
return subsec->raddr + offset;
inline u64 RangeExtensionThunk<ARM64>::get_addr(i64 idx) const {
return output_section.hdr.addr + offset + idx * ENTRY_SIZE;
}
} // namespace mold::macho
-622
View File
@@ -1,622 +0,0 @@
#include "mold.h"
#include "../archive-file.h"
namespace mold::macho {
template <typename E>
std::ostream &operator<<(std::ostream &out, const InputFile<E> &file) {
if (file.archive_name.empty())
out << path_clean(file.mf->name);
else
out << path_clean(file.archive_name) << "(" << path_clean(file.mf->name) + ")";
return out;
}
template <typename E>
ObjectFile<E> *
ObjectFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string archive_name) {
ObjectFile<E> *obj = new ObjectFile<E>;
obj->mf = mf;
obj->archive_name = archive_name;
obj->is_alive = archive_name.empty();
ctx.obj_pool.emplace_back(obj);
return obj;
};
template <typename E>
void ObjectFile<E>::parse(Context<E> &ctx) {
parse_sections(ctx);
parse_symtab(ctx);
split_subsections(ctx);
parse_data_in_code(ctx);
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec)
isec->parse_relocations(ctx);
if (unwind_sec)
parse_compact_unwind(ctx, *unwind_sec);
}
template <typename E>
void ObjectFile<E>::parse_sections(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
// Read all but a symtab section
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
p += lc.cmdsize;
if (lc.cmd != LC_SEGMENT_64)
continue;
SegmentCommand &cmd = *(SegmentCommand *)&lc;
MachSection *mach_sec = (MachSection *)((u8 *)&lc + sizeof(cmd));
for (MachSection &msec : std::span(mach_sec, mach_sec + cmd.nsects)) {
sections.push_back(nullptr);
if (msec.match("__LD", "__compact_unwind")) {
unwind_sec = &msec;
continue;
}
if (msec.attr & S_ATTR_DEBUG)
continue;
sections.back().reset(new InputSection<E>(ctx, *this, msec));
}
}
}
template <typename E>
void ObjectFile<E>::parse_symtab(Context<E> &ctx) {
SymtabCommand *cmd = (SymtabCommand *)find_load_command(ctx, LC_SYMTAB);
if (!cmd)
return;
mach_syms = {(MachSym *)(this->mf->data + cmd->symoff), cmd->nsyms};
this->syms.reserve(mach_syms.size());
i64 nlocal = 0;
for (MachSym &msym : mach_syms)
if (!msym.ext)
nlocal++;
local_syms.reserve(nlocal);
for (MachSym &msym : mach_syms) {
std::string_view name = (char *)(this->mf->data + cmd->stroff + msym.stroff);
if (msym.ext) {
this->syms.push_back(get_symbol(ctx, name));
} else {
local_syms.emplace_back(name);
this->syms.push_back(&local_syms.back());
}
}
}
struct SplitRegion {
u32 offset;
u32 size;
u32 symidx;
bool is_alt_entry;
};
template <typename E>
struct SplitInfo {
SplitInfo(InputSection<E> *isec) : isec(isec) {}
InputSection<E> *isec;
std::vector<SplitRegion> regions;
};
template <typename E>
static std::vector<SplitInfo<E>> split(Context<E> &ctx, ObjectFile<E> &file) {
std::vector<SplitInfo<E>> vec;
for (std::unique_ptr<InputSection<E>> &isec : file.sections)
vec.push_back({isec.get()});
for (i64 i = 0; i < file.mach_syms.size(); i++) {
MachSym &msym = file.mach_syms[i];
if (msym.type == N_SECT && file.sections[msym.sect - 1]) {
SplitRegion r;
r.offset = msym.value - file.sections[msym.sect - 1]->hdr.addr;
r.symidx = i;
r.is_alt_entry = (msym.desc & N_ALT_ENTRY);
vec[msym.sect - 1].regions.push_back(r);
}
}
std::erase_if(vec, [](const SplitInfo<E> &info) { return !info.isec; });
sort(vec, [](const SplitInfo<E> &a, const SplitInfo<E> &b) {
return a.isec->hdr.addr < b.isec->hdr.addr;
});
for (SplitInfo<E> &info : vec) {
std::vector<SplitRegion> &r = info.regions;
if (r.empty()) {
r.push_back({0, (u32)info.isec->hdr.size, (u32)-1, false});
continue;
}
sort(r, [](const SplitRegion &a, const SplitRegion &b) {
return a.offset < b.offset;
});
if (r[0].offset > 0)
r.insert(r.begin(), {0, r[0].offset, (u32)-1, false});
for (i64 i = 1; i < r.size(); i++)
if (r[i - 1].offset == r[i].offset)
r[i++].is_alt_entry = true;
i64 last = -1;
for (i64 i = 0; i < r.size(); i++) {
if (!r[i].is_alt_entry) {
if (last != -1)
r[last].size = r[i].offset - r[last].offset;
last = i;
}
}
if (last != -1)
r[last].size = info.isec->hdr.size - r[last].offset;
}
return vec;
}
template <typename E>
void ObjectFile<E>::split_subsections(Context<E> &ctx) {
sym_to_subsec.resize(mach_syms.size());
for (SplitInfo<E> &info : split(ctx, *this)) {
InputSection<E> &isec = *info.isec;
for (SplitRegion &r : info.regions) {
if (!r.is_alt_entry) {
Subsection<E> *subsec = new Subsection<E>{
.isec = isec,
.input_offset = r.offset,
.input_size = r.size,
.input_addr = (u32)(isec.hdr.addr + r.offset),
.p2align = (u8)isec.hdr.p2align,
};
subsections.emplace_back(subsec);
}
if (r.symidx != -1)
sym_to_subsec[r.symidx] = subsections.size() - 1;
}
}
for (i64 i = 0; i < mach_syms.size(); i++)
if (!mach_syms[i].ext)
override_symbol(ctx, i);
}
template <typename E>
void ObjectFile<E>::parse_data_in_code(Context<E> &ctx) {
if (auto *cmd = (LinkEditDataCommand *)find_load_command(ctx, LC_DATA_IN_CODE))
data_in_code_entries = {
(DataInCodeEntry *)(this->mf->data + cmd->dataoff),
cmd->datasize / sizeof(DataInCodeEntry)
};
}
template <typename E>
LoadCommand *ObjectFile<E>::find_load_command(Context<E> &ctx, u32 type) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
if (lc.cmd == type)
return &lc;
p += lc.cmdsize;
}
return nullptr;
}
template <typename E>
i64 ObjectFile<E>::find_subsection_idx(Context<E> &ctx, u32 addr) {
auto it = std::upper_bound(
subsections.begin(), subsections.end(), addr,
[&](u32 addr, const std::unique_ptr<Subsection<E>> &subsec) {
return addr < subsec->input_addr;
});
if (it == subsections.begin())
return -1;
return it - subsections.begin() - 1;
}
template <typename E>
Subsection<E> *ObjectFile<E>::find_subsection(Context<E> &ctx, u32 addr) {
i64 i = find_subsection_idx(ctx, addr);
return (i == -1) ? nullptr : subsections[i].get();
}
template <typename E>
void ObjectFile<E>::parse_compact_unwind(Context<E> &ctx, MachSection &hdr) {
if (hdr.size % sizeof(CompactUnwindEntry))
Fatal(ctx) << *this << ": invalid __compact_unwind section size";
i64 num_entries = hdr.size / sizeof(CompactUnwindEntry);
unwind_records.reserve(num_entries);
CompactUnwindEntry *src = (CompactUnwindEntry *)(this->mf->data + hdr.offset);
// Read compact unwind entries
for (i64 i = 0; i < num_entries; i++)
unwind_records.emplace_back(src[i].code_len, src[i].encoding);
// Read relocations
MachRel *mach_rels = (MachRel *)(this->mf->data + hdr.reloff);
for (i64 i = 0; i < hdr.nreloc; i++) {
MachRel &r = mach_rels[i];
if (r.offset >= hdr.size)
Fatal(ctx) << *this << ": relocation offset too large: " << i;
i64 idx = r.offset / sizeof(CompactUnwindEntry);
UnwindRecord<E> &dst = unwind_records[idx];
auto error = [&] {
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
};
switch (r.offset % sizeof(CompactUnwindEntry)) {
case offsetof(CompactUnwindEntry, code_start): {
if (r.is_pcrel || r.p2size != 3 || r.is_extern || r.type)
error();
Subsection<E> *target = find_subsection(ctx, src[idx].code_start);
if (!target)
error();
dst.subsec = target;
dst.offset = src[idx].code_start - target->input_addr;
break;
}
case offsetof(CompactUnwindEntry, personality):
if (r.is_pcrel || r.p2size != 3 || !r.is_extern || r.type)
error();
dst.personality = this->syms[r.idx];
break;
case offsetof(CompactUnwindEntry, lsda): {
if (r.is_pcrel || r.p2size != 3 || r.is_extern || r.type)
error();
i32 addr = *(i32 *)((u8 *)this->mf->data + hdr.offset + r.offset);
Subsection<E> *target = find_subsection(ctx, addr);
if (!target)
error();
dst.lsda = target;
dst.lsda_offset = addr - target->input_addr;
break;
}
default:
Fatal(ctx) << *this << ": __compact_unwind: unsupported relocation: " << i;
}
}
for (i64 i = 0; i < num_entries; i++)
if (!unwind_records[i].subsec)
Fatal(ctx) << ": __compact_unwind: missing relocation at " << i;
// Sort unwind entries by offset
sort(unwind_records, [](const UnwindRecord<E> &a, const UnwindRecord<E> &b) {
return std::tuple(a.subsec->input_addr, a.offset) <
std::tuple(b.subsec->input_addr, b.offset);
});
// Associate unwind entries to subsections
for (i64 i = 0; i < num_entries;) {
Subsection<E> &subsec = *unwind_records[i].subsec;
subsec.unwind_offset = i;
i64 j = i + 1;
while (j < num_entries && unwind_records[j].subsec == &subsec)
j++;
subsec.nunwind = j - i;
i = j;
}
}
// Symbols with higher priorities overwrites symbols with lower priorities.
// Here is the list of priorities, from the highest to the lowest.
//
// 1. Strong defined symbol
// 2. Weak defined symbol
// 3. Strong defined symbol in a DSO
// 4. Weak defined symbol in a DSO
// 5. Strong or weak defined symbol in an archive
// 6. Common symbol
// 7. Unclaimed (nonexistent) symbol
//
// Ties are broken by file priority.
template <typename E>
static u64 get_rank(InputFile<E> *file, MachSym &msym, bool is_lazy) {
if (msym.is_common())
return (6 << 24) + file->priority;
if (is_lazy)
return (5 << 24) + file->priority;
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
static u64 get_rank(Symbol<E> &sym) {
InputFile<E> *file = sym.file;
if (!file)
return 7 << 24;
if (sym.is_common)
return (6 << 24) + file->priority;
if (!file->archive_name.empty())
return (5 << 24) + file->priority;
if (file->is_dylib())
return (3 << 24) + file->priority;
return (1 << 24) + file->priority;
}
template <typename E>
void ObjectFile<E>::override_symbol(Context<E> &ctx, i64 idx) {
Symbol<E> &sym = *this->syms[idx];
MachSym &msym = mach_syms[idx];
sym.file = this;
sym.is_extern = msym.ext;
sym.is_lazy = false;
switch (msym.type) {
case N_UNDF:
assert(msym.is_common());
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = true;
break;
case N_ABS:
sym.subsec = nullptr;
sym.value = msym.value;
sym.is_common = false;
break;
case N_SECT:
sym.subsec = subsections[sym_to_subsec[idx]].get();
sym.value = msym.value - sym.subsec->input_addr;
sym.is_common = false;
break;
default:
Fatal(ctx) << sym << ": unknown symbol type: " << (u64)msym.type;
}
}
template <typename E>
void ObjectFile<E>::resolve_regular_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext || msym.is_undef())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, msym, false) < get_rank(sym))
override_symbol(ctx, i);
}
}
template <typename E>
void ObjectFile<E>::resolve_lazy_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext || msym.is_undef() || msym.is_common())
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (get_rank(this, msym, true) < get_rank(sym)) {
sym.file = this;
sym.subsec = nullptr;
sym.value = 0;
sym.is_extern = false;
sym.is_lazy = true;
sym.is_common = false;
}
}
}
template <typename E>
bool ObjectFile<E>::is_objc_object(Context<E> &ctx) {
for (std::unique_ptr<InputSection<E>> &isec : sections)
if (isec->hdr.match("__DATA", "__objc_catlist") ||
isec->hdr.match("__TEXT", "__swift"))
return true;
for (i64 i = 0; i < this->syms.size(); i++)
if (!mach_syms[i].is_undef() && mach_syms[i].ext &&
this->syms[i]->name.starts_with("_OBJC_CLASS_$_"))
return true;
return false;
}
template <typename E>
std::vector<ObjectFile<E> *> ObjectFile<E>::mark_live_objects(Context<E> &ctx) {
std::vector<ObjectFile<E> *> vec;
assert(this->is_alive);
for (i64 i = 0; i < this->syms.size(); i++) {
MachSym &msym = mach_syms[i];
if (!msym.ext)
continue;
Symbol<E> &sym = *this->syms[i];
std::scoped_lock lock(sym.mu);
if (msym.is_undef()) {
if (sym.file && !sym.file->is_alive.exchange(true))
vec.push_back((ObjectFile<E> *)sym.file);
continue;
}
if (get_rank(this, msym, false) < get_rank(sym))
override_symbol(ctx, i);
}
return vec;
}
template <typename E>
void ObjectFile<E>::convert_common_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> &sym = *this->syms[i];
MachSym &msym = mach_syms[i];
if (sym.file == this && sym.is_common) {
InputSection<E> *isec = get_common_sec(ctx);
Subsection<E> *subsec = new Subsection<E>{
.isec = *isec,
.input_size = (u32)msym.value,
.p2align = (u8)msym.p2align,
};
subsections.emplace_back(subsec);
sym.subsec = subsec;
sym.value = 0;
sym.is_common = false;
}
}
}
template <typename E>
void ObjectFile<E>::check_duplicate_symbols(Context<E> &ctx) {
for (i64 i = 0; i < this->syms.size(); i++) {
Symbol<E> *sym = this->syms[i];
MachSym &msym = mach_syms[i];
if (sym && !msym.is_undef() && !msym.is_common() && sym->file != this)
Error(ctx) << "duplicate symbol: " << *this << ": " << *sym->file
<< ": " << *sym;
}
}
template <typename E>
InputSection<E> *ObjectFile<E>::get_common_sec(Context<E> &ctx) {
if (!common_sec) {
MachSection *hdr = new MachSection;
common_hdr.reset(hdr);
memset(hdr, 0, sizeof(*hdr));
hdr->set_segname("__DATA");
hdr->set_sectname("__common");
hdr->type = S_ZEROFILL;
common_sec = new InputSection<E>(ctx, *this, *hdr);
sections.emplace_back(common_sec);
}
return common_sec;
}
template <typename E>
DylibFile<E> *DylibFile<E>::create(Context<E> &ctx, MappedFile<Context<E>> *mf) {
DylibFile<E> *dylib = new DylibFile<E>;
dylib->mf = mf;
ctx.dylib_pool.emplace_back(dylib);
return dylib;
};
template <typename E>
void DylibFile<E>::read_trie(Context<E> &ctx, u8 *start, i64 offset,
const std::string &prefix) {
u8 *buf = start + offset;
if (*buf) {
read_uleb(buf); // size
read_uleb(buf); // flags
read_uleb(buf); // addr
this->syms.push_back(get_symbol(ctx, prefix));
} else {
buf++;
}
i64 nchild = *buf++;
for (i64 i = 0; i < nchild; i++) {
std::string suffix((char *)buf);
buf += suffix.size() + 1;
i64 off = read_uleb(buf);
read_trie(ctx, start, off, prefix + suffix);
}
}
template <typename E>
void DylibFile<E>::parse_dylib(Context<E> &ctx) {
MachHeader &hdr = *(MachHeader *)this->mf->data;
u8 *p = this->mf->data + sizeof(hdr);
for (i64 i = 0; i < hdr.ncmds; i++) {
LoadCommand &lc = *(LoadCommand *)p;
switch (lc.cmd) {
case LC_ID_DYLIB: {
DylibCommand &cmd = *(DylibCommand *)p;
install_name = (char *)p + cmd.nameoff;
break;
}
case LC_DYLD_INFO_ONLY: {
DyldInfoCommand &cmd = *(DyldInfoCommand *)p;
if (cmd.export_off)
read_trie(ctx, this->mf->data + cmd.export_off);
break;
}
case LC_DYLD_EXPORTS_TRIE: {
LinkEditDataCommand &cmd = *(LinkEditDataCommand *)p;
read_trie(ctx, this->mf->data + cmd.dataoff);
break;
}
}
p += lc.cmdsize;
}
}
template <typename E>
void DylibFile<E>::parse(Context<E> &ctx) {
switch (get_file_type(this->mf)) {
case FileType::TAPI: {
TextDylib tbd = parse_tbd(ctx, this->mf);
for (std::string_view sym : tbd.exports)
this->syms.push_back(get_symbol(ctx, sym));
install_name = tbd.install_name;
break;
}
case FileType::MACH_DYLIB:
parse_dylib(ctx);
break;
default:
Fatal(ctx) << this->mf->name << ": is not a dylib";
}
}
template <typename E>
void DylibFile<E>::resolve_symbols(Context<E> &ctx) {
for (Symbol<E> *sym : this->syms) {
std::scoped_lock lock(sym->mu);
if (sym->file && sym->file->priority < this->priority)
continue;
sym->file = this;
sym->is_extern = true;
}
}
#define INSTANTIATE(E) \
template class ObjectFile<E>; \
template class DylibFile<E>; \
template std::ostream &operator<<(std::ostream &, const InputFile<E> &)
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+482 -291
View File
File diff suppressed because it is too large. Load diff
-120
View File
@@ -1,120 +0,0 @@
// This file implements the same functionality as elf/output-file.cc.
// We have two different implemetntations for ELF and Mach-O because
// we implemented a special optimization for ELF or Linux.
//
// On Linux, it is faster to write to an existing file than creating a
// new file and writing to it. So, we overwrite an existing executable
// if exists.
//
// On macOS, it looks like the system does not assume that an executable
// is mutated once it is created. Due to some code signing mechanism or
// something, we always have to create a fresh file.
#include "mold.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::macho {
template <typename E>
class MemoryMappedOutputFile : public OutputFile<E> {
public:
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, true) {
std::string tmp_path = filepath(path).parent_path() / ".mold-XXXXXX";
output_tmpfile = (char *)save_string(ctx, tmp_path).data();
i64 fd = mkstemp(output_tmpfile);
if (fd == -1)
Fatal(ctx) << "cannot open " << output_tmpfile << ": " << errno_string();
if (ftruncate(fd, filesize))
Fatal(ctx) << "ftruncate failed: " << errno_string();
if (fchmod(fd, perm) == -1)
Fatal(ctx) << "fchmod failed: " << errno_string();
this->buf = (u8 *)mmap(nullptr, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
::close(fd);
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
munmap(this->buf, this->filesize);
if (rename(output_tmpfile, this->path.c_str()) == -1)
Fatal(ctx) << this->path << ": rename failed: " << errno_string();
output_tmpfile = nullptr;
}
};
template <typename E>
class MallocOutputFile : public OutputFile<E> {
public:
MallocOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, false), perm(perm) {
this->buf = (u8 *)mmap(NULL, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << "mmap failed: " << errno_string();
}
void close(Context<E> &ctx) override {
Timer t(ctx, "close_file");
if (this->path == "-") {
fwrite(this->buf, this->filesize, 1, stdout);
fclose(stdout);
return;
}
i64 fd = ::open(this->path.c_str(), O_RDWR | O_CREAT, perm);
if (fd == -1)
Fatal(ctx) << "cannot open " << this->path << ": " << errno_string();
FILE *fp = fdopen(fd, "w");
fwrite(this->buf, this->filesize, 1, fp);
fclose(fp);
}
private:
i64 perm;
};
template <typename E>
std::unique_ptr<OutputFile<E>>
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
path = ctx.arg.chroot + "/" + path_clean(path);
bool is_special = false;
if (path == "-") {
is_special = true;
} else {
struct stat st;
if (stat(path.c_str(), &st) == 0 && (st.st_mode & S_IFMT) != S_IFREG)
is_special = true;
}
std::unique_ptr<OutputFile> file;
if (is_special)
file = std::make_unique<MallocOutputFile<E>>(ctx, path, filesize, perm);
else
file = std::make_unique<MemoryMappedOutputFile<E>>(ctx, path, filesize, perm);
return file;
}
#define INSTANTIATE(E) \
template class OutputFile<E>
INSTANTIATE(ARM64);
INSTANTIATE(X86_64);
} // namespace mold::macho
+227 -38
View File
@@ -1,6 +1,19 @@
// On macOS, you can pass a test file describing a dylib instead of an
// actual dylib file to link against a dynamic library. Such text file
// should be in the YAML format and contains dylib's exported symbols
// as well as the file's various attributes. The extension of the text
// file is `.tbd`.
//
// .tbd files allows users to link against a library without
// distributing the binary of the library file itself.
//
// This file contains functions to parse the .tbd file.
#include "mold.h"
#include <optional>
#include <regex>
#include <unordered_set>
namespace mold::macho {
@@ -13,6 +26,15 @@ get_vector(YamlNode &node, std::string_view key) {
return {};
}
static std::vector<std::string_view>
get_string_vector(YamlNode &node, std::string_view key) {
std::vector<std::string_view> vec;
for (YamlNode &mem : get_vector(node, key))
if (auto val = std::get_if<std::string_view>(&mem.data))
vec.push_back(*val);
return vec;
}
static std::optional<std::string_view>
get_string(YamlNode &node, std::string_view key) {
if (auto *map = std::get_if<std::map<std::string_view, YamlNode>>(&node.data))
@@ -24,70 +46,233 @@ get_string(YamlNode &node, std::string_view key) {
static bool contains(const std::vector<YamlNode> &vec, std::string_view key) {
for (const YamlNode &mem : vec)
if (const std::string_view *s = std::get_if<std::string_view>(&mem.data))
if (*s == key)
if (auto val = std::get_if<std::string_view>(&mem.data))
if (*val == key)
return true;
return false;
}
static std::optional<TextDylib> to_tbd(YamlNode &node, std::string_view arch) {
template <typename E>
static std::optional<TextDylib>
to_tbd(Context<E> &ctx, 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);
append(tbd.reexported_libs, get_string_vector(mem, "libraries"));
for (std::string_view key : {"exports", "reexports"})
for (YamlNode &mem : get_vector(node, key))
if (contains(get_vector(mem, "targets"), arch))
for (YamlNode &mem : get_vector(mem, "symbols"))
if (auto *sym = std::get_if<std::string_view>(&mem.data))
tbd.exports.push_back(*sym);
auto concat = [&](const std::string &x, std::string_view y) {
return save_string(ctx, x + std::string(y));
};
for (std::string_view key : {"exports", "reexports"}) {
for (YamlNode &mem : get_vector(node, key)) {
if (contains(get_vector(mem, "targets"), arch)) {
append(tbd.exports, get_string_vector(mem, "symbols"));
append(tbd.weak_exports, get_string_vector(mem, "weak-symbols"));
for (std::string_view s : get_string_vector(mem, "objc-classes")) {
tbd.exports.push_back(concat("_OBJC_CLASS_$_", s));
tbd.exports.push_back(concat("_OBJC_METACLASS_$_", s));
}
for (std::string_view s : get_string_vector(mem, "objc-eh-types"))
tbd.exports.push_back(concat("_OBJC_EHTYPE_$_", s));
for (std::string_view s : get_string_vector(mem, "objc-ivars"))
tbd.exports.push_back(concat("_OBJC_IVAR_$_", s));
}
}
}
return tbd;
}
static i64 parse_version(const std::string &arg) {
auto flags = std::regex_constants::ECMAScript | std::regex_constants::optimize;
static std::regex re(R"((\d+)(?:\.(\d+))?(?:\.(\d+))?)", flags);
std::smatch m;
bool ok = std::regex_match(arg, m, re);
assert(ok);
i64 major = (m[1].length() == 0) ? 0 : stoi(m[1]);
i64 minor = (m[2].length() == 0) ? 0 : stoi(m[2]);
i64 patch = (m[3].length() == 0) ? 0 : stoi(m[3]);
return (major << 16) | (minor << 8) | patch;
}
// Dylib can contain special symbols whose name starts with "$ld$".
// Such symbols aren't actually symbols but linker directives.
// We interpret such symbols in this function.
template <typename E>
static TextDylib squash(Context<E> &ctx, std::span<TextDylib> tbds) {
static void interpret_ld_symbols(Context<E> &ctx, TextDylib &tbd) {
std::vector<std::string_view> syms;
syms.reserve(tbd.exports.size());
std::unordered_set<std::string_view> hidden_syms;
auto string_view = [](const std::csub_match &sub) {
return std::string_view{sub.first, (size_t)sub.length()};
};
for (std::string_view s : tbd.exports) {
if (!s.starts_with("$ld$"))
continue;
auto flags = std::regex_constants::ECMAScript | std::regex_constants::optimize;
std::cmatch m;
// $ld$previous$ symbol replaces the default install name with a
// specified one if the platform OS version is in a specified range.
static std::regex previous_re(
R"(\$ld\$previous\$([^$]+)\$([\d.]*)\$(\d+)\$([\d.]+)\$([\d.]+)\$(.*)\$)",
flags);
if (std::regex_match(s.begin(), s.end(), m, previous_re)) {
std::string_view install_name = string_view(m[1]);
i64 platform = std::stoi(m[3].str());
i64 min_version = parse_version(m[4]);
i64 max_version = parse_version(m[5]);
std::string_view symbol_name = string_view(m[6]);
if (!symbol_name.empty()) {
// ld64 source seems to have implemented a feature to give an
// alternative install name for a matching symbol, but it didn't
// work in practice (or I may be using the feature in a wrong way.)
// Ignore such symbol for now.
continue;
}
if (platform == ctx.arg.platform &&
min_version <= ctx.arg.platform_min_version &&
ctx.arg.platform_min_version < max_version) {
tbd.install_name = install_name;
}
continue;
}
// $ld$add$os_version$symbol adds a symbol if the given OS version
// matches.
static std::regex add_re(R"(\$ld\$add\$os([\d.]+)\$(.+))", flags);
if (std::regex_match(s.begin(), s.end(), m, add_re)) {
if (ctx.arg.platform_min_version == parse_version(m[1]))
syms.push_back(string_view(m[2]));
continue;
}
// $ld$hide$os_version$symbol hides a symbol if the given OS version
// matches.
static std::regex hidden_re(R"(\$ld\$hide\$os([\d.]+)\$(.+))", flags);
if (std::regex_match(s.begin(), s.end(), m, hidden_re)) {
if (ctx.arg.platform_min_version == parse_version(m[1]))
hidden_syms.insert(string_view(m[2]));
continue;
}
// $ld$install_name$os_version$name changes the install name to a
// given name.
static std::regex
install_name_re(R"(\$ld\$install_name\$os([\d.]+)\$(.+))", flags);
if (std::regex_match(s.begin(), s.end(), m, install_name_re)) {
if (ctx.arg.platform_min_version == parse_version(m[1]))
tbd.install_name = string_view(m[2]);
continue;
}
}
for (std::string_view s : tbd.exports)
if (!s.starts_with("$ld$") && !hidden_syms.contains(std::string(s)))
syms.push_back(s);
std::erase_if(syms, [](std::string_view s) { return s.starts_with("$ld$"); });
tbd.exports = syms;
}
template <typename E>
static TextDylib parse(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::string_view arch);
template <typename E>
static MappedFile<Context<E>> *
find_external_lib(Context<E> &ctx, std::string_view parent, std::string path) {
if (!path.starts_with('/'))
Fatal(ctx) << parent << ": contains an invalid reexported path: " << path;
for (const std::string &root : ctx.arg.syslibroot) {
if (path.ends_with(".tbd")) {
if (auto *file = MappedFile<Context<E>>::open(ctx, root + path))
return file;
continue;
}
if (path.ends_with(".dylib")) {
std::string stem(path.substr(0, path.size() - 6));
if (auto *file = MappedFile<Context<E>>::open(ctx, root + stem + ".tbd"))
return file;
if (auto *file = MappedFile<Context<E>>::open(ctx, root + path))
return file;
}
for (std::string extn : {".tbd", ".dylib"})
if (auto *file = MappedFile<Context<E>>::open(ctx, root + path + extn))
return file;
}
Fatal(ctx) << parent << ": cannot open reexported library " << path;
}
// A single YAML file may contain multiple text dylibs. The first text
// dylib is the main file followed by optional other text dylibs for
// re-exported libraries.
//
// This fucntion squashes multiple text dylibs into a single text dylib
// by copying symbols of re-exported text dylibs to the main text dylib.
template <typename E>
static TextDylib
squash(Context<E> &ctx, std::span<TextDylib> tbds, std::string_view arch) {
std::unordered_map<std::string_view, TextDylib> map;
TextDylib main = std::move(tbds[0]);
for (TextDylib &tbd : tbds.subspan(1))
map[tbd.install_name] = std::move(tbd);
std::vector<std::string_view> libs;
std::function<void(TextDylib &)> visit = [&](TextDylib &tbd) {
for (std::string_view lib : tbd.reexported_libs) {
auto it = map.find(lib);
for (std::string_view lib : main.reexported_libs) {
auto it = map.find(lib);
if (it != map.end())
append(main.exports, it->second.exports);
else
libs.push_back(lib);
}
if (it != map.end()) {
// The referenced reexported library is in the same .tbd file.
TextDylib &child = it->second;
append(main.exports, child.exports);
append(main.weak_exports, child.weak_exports);
visit(child);
} else {
// The referenced reexported library is a separate file.
MappedFile<Context<E>> *mf =
find_external_lib(ctx, tbd.install_name, std::string(lib));
TextDylib child = parse(ctx, mf, arch);
append(main.exports, child.exports);
append(main.weak_exports, child.weak_exports);
}
}
};
main.reexported_libs = std::move(libs);
visit(main);
sort(main.exports);
sort(main.weak_exports);
remove_duplicates(main.exports);
remove_duplicates(main.weak_exports);
return main;
}
@@ -109,9 +294,13 @@ static TextDylib parse(Context<E> &ctx, MappedFile<Context<E>> *mf,
std::vector<TextDylib> vec;
for (YamlNode &node : nodes)
if (std::optional<TextDylib> dylib = to_tbd(node, arch))
if (std::optional<TextDylib> dylib = to_tbd(ctx, node, arch))
vec.push_back(*dylib);
return squash(ctx, vec);
for (TextDylib &tbd : vec)
interpret_ld_symbols(ctx, tbd);
return squash(ctx, vec, arch);
}
template <>
+34 -19
View File
@@ -1,12 +1,27 @@
// On macOS, a .tbd file can be used instead of a .dylib to link
// against a dynamic library. A .tbd file is a text file in the YAML
// format, so the linker has to be able to parse a YAML file. This
// file implements a YAML parser.
//
// We implemented a YAML parser ourselves instead of using an external
// library. We didn't want to add an dependency to an external library
// just to read .tdb files.
//
// YAML as a format is very complicated. For example, the official
// YAML specification allows embedding a JSON string into YAML; so it
// is strictly larger than JSON. It has surprisingly many features
// that most users are not aware of. Fortunately, we have to support
// only a small portion of the spec to read a .tbd file.
#include "mold.h"
#include <optional>
namespace mold::macho {
struct Token {
enum { STRING = 1, INDENT, DEDENT, END };
enum { STRING = 1, INDENT, DEDENT, END };
struct Token {
u8 kind = 0;
std::string_view str;
};
@@ -39,13 +54,13 @@ std::optional<YamlError> YamlParser::tokenize() {
std::vector<i64> indents = {0};
auto indent = [&](std::string_view str, i64 depth) {
tokens.push_back({Token::INDENT, str});
tokens.push_back({INDENT, str});
indents.push_back(depth);
};
auto dedent = [&](std::string_view str) {
assert(indents.size() > 1);
tokens.push_back({Token::DEDENT, str});
tokens.push_back({DEDENT, str});
indents.pop_back();
};
@@ -63,7 +78,7 @@ std::optional<YamlError> YamlParser::tokenize() {
if (str.starts_with("---")) {
while (indents.size() > 1)
dedent(str);
tokens.push_back({Token::END, str.substr(0, 3)});
tokens.push_back({END, str.substr(0, 3)});
skip_line(str);
return {};
}
@@ -71,7 +86,7 @@ std::optional<YamlError> YamlParser::tokenize() {
if (str.starts_with("...")) {
while (indents.size() > 1)
dedent(str);
tokens.push_back({Token::END, str.substr(0, 3)});
tokens.push_back({END, str.substr(0, 3)});
str = str.substr(str.size());
return {};
}
@@ -217,7 +232,7 @@ YamlParser::tokenize_string(std::string_view &str, char end) {
if (pos == str.npos)
return YamlError{"unterminated string literal", start - input.data()};
tokens.push_back({Token::STRING, str.substr(1, pos - 1)});
tokens.push_back({STRING, str.substr(1, pos - 1)});
str = str.substr(pos + 1);
return {};
}
@@ -228,7 +243,7 @@ YamlParser::tokenize_bare_string(std::string_view &str) {
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-/.");
if (pos == str.npos)
pos = str.size();
tokens.push_back({Token::STRING, str.substr(0, pos)});
tokens.push_back({STRING, str.substr(0, pos)});
str = str.substr(pos);
}
@@ -240,7 +255,7 @@ std::variant<std::vector<YamlNode>, YamlError> YamlParser::parse() {
std::vector<YamlNode> vec;
while (!tok.empty()) {
if (tok[0].kind == Token::END) {
if (tok[0].kind == END) {
tok = tok.subspan(1);
continue;
}
@@ -250,7 +265,7 @@ std::variant<std::vector<YamlNode>, YamlError> YamlParser::parse() {
return *err;
vec.push_back(std::get<YamlNode>(elem));
if (tok[0].kind != Token::END)
if (tok[0].kind != END)
return YamlError{"stray token", tok[0].str.data() - input.data()};
}
return vec;
@@ -258,11 +273,11 @@ std::variant<std::vector<YamlNode>, YamlError> YamlParser::parse() {
std::variant<YamlNode, YamlError>
YamlParser::parse_element(std::span<Token> &tok) {
if (tok[0].kind == Token::INDENT) {
if (tok[0].kind == INDENT) {
tok = tok.subspan(1);
std::variant<YamlNode, YamlError> elem = parse_element(tok);
assert(tok[0].kind == Token::DEDENT);
assert(tok[0].kind == DEDENT);
tok = tok.subspan(1);
return elem;
}
@@ -270,7 +285,7 @@ YamlParser::parse_element(std::span<Token> &tok) {
if (tok[0].kind == '-')
return parse_list(tok);
if (tok.size() > 2 && tok[0].kind == Token::STRING && tok[1].kind == ':')
if (tok.size() > 2 && tok[0].kind == STRING && tok[1].kind == ':')
return parse_map(tok);
return parse_flow_element(tok);
@@ -280,7 +295,7 @@ std::variant<YamlNode, YamlError>
YamlParser::parse_list(std::span<Token> &tok) {
std::vector<YamlNode> vec;
while (tok[0].kind != Token::END && tok[0].kind != Token::DEDENT) {
while (tok[0].kind != END && tok[0].kind != DEDENT) {
if (tok[0].kind != '-')
return YamlError{"list element expected", tok[0].str.data() - input.data()};
tok = tok.subspan(1);
@@ -297,8 +312,8 @@ std::variant<YamlNode, YamlError>
YamlParser::parse_map(std::span<Token> &tok) {
std::map<std::string_view, YamlNode> map;
while (tok[0].kind != Token::END && tok[0].kind != Token::DEDENT) {
if (tok.size() < 2 || tok[0].kind != Token::STRING || tok[1].kind != ':')
while (tok[0].kind != END && tok[0].kind != DEDENT) {
if (tok.size() < 2 || tok[0].kind != STRING || tok[1].kind != ':')
return YamlError{"map key expected", tok[0].str.data() - input.data()};
std::string_view key = tok[0].str;
@@ -319,7 +334,7 @@ YamlParser::parse_flow_element(std::span<Token> &tok) {
return parse_flow_list(tok);
}
if (tok[0].kind != Token::STRING)
if (tok[0].kind != STRING)
return YamlError{"scalar expected", tok[0].str.data() - input.data()};
std::string_view val = tok[0].str;
@@ -332,7 +347,7 @@ YamlParser::parse_flow_list(std::span<Token> &tok) {
std::vector<YamlNode> vec;
const char *start = tok[0].str.data();
while (tok[0].kind != ']' && tok[0].kind != Token::END) {
while (tok[0].kind != ']' && tok[0].kind != END) {
std::variant<YamlNode, YamlError> elem = parse_flow_element(tok);
if (YamlError *err = std::get_if<YamlError>(&elem))
return *err;
@@ -345,7 +360,7 @@ YamlParser::parse_flow_list(std::span<Token> &tok) {
tok = tok.subspan(1);
}
if (tok[0].kind == Token::END)
if (tok[0].kind == END)
return YamlError{"unterminated flow list", start - input.data()};
tok = tok.subspan(1);
+46 -5
View File
@@ -1,8 +1,8 @@
#include "elf/mold.h"
#include "macho/mold.h"
#include "mold.h"
#include <cstring>
#include <signal.h>
#include <tbb/global_control.h>
#ifdef USE_SYSTEM_MIMALLOC
#include <mimalloc-new-delete.h>
@@ -18,6 +18,14 @@ const std::string mold_version =
"mold " MOLD_VERSION " (compatible with GNU ld)";
#endif
namespace elf {
int main(int argc, char **argv);
}
namespace macho {
int main(int argc, char **argv);
}
void cleanup() {
if (output_tmpfile)
unlink(output_tmpfile);
@@ -25,14 +33,47 @@ void cleanup() {
unlink(socket_tmpfile);
}
static void signal_handler(int) {
// mold mmap's an output file, and the mmap succeeds even if there's
// no enough space left on the filesystem. The actual disk blocks are
// not allocated on the mmap call but when the program writes to it
// for the first time.
//
// If a disk becomes full as a result of a write to an mmap'ed memory
// region, the failure of the write is reported as a SIGBUS. This
// signal handler catches that signal and print out a user-friendly
// error message. Without this, it is very hard to realize that the
// disk might be full.
static void sighandler(int signo, siginfo_t *info, void *ucontext) {
static std::mutex mu;
std::scoped_lock lock{mu};
if ((signo == SIGSEGV || signo == SIGBUS) &&
output_buffer_start <= info->si_addr &&
info->si_addr < output_buffer_end) {
const char msg[] = "mold: failed to write to an output file. Disk full?\n";
(void)!write(STDERR_FILENO, msg, sizeof(msg) - 1);
}
cleanup();
_exit(1);
}
void install_signal_handler() {
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
struct sigaction action;
action.sa_sigaction = sighandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGINT, &action, NULL);
sigaction(SIGTERM, &action, NULL);
sigaction(SIGBUS, &action, NULL);
}
i64 get_default_thread_count() {
// mold doesn't scale well above 32 threads.
int n = tbb::global_control::active_value(
tbb::global_control::max_allowed_parallelism);
return std::min(n, 32);
}
} // namespace mold
+157 -22
View File
@@ -1,9 +1,11 @@
#pragma once
#include "big-endian.h"
#include "inttypes.h"
#include <array>
#include <atomic>
#include <bit>
#include <bitset>
#include <cassert>
#include <cstdio>
#include <cstring>
@@ -11,6 +13,7 @@
#include <filesystem>
#include <iostream>
#include <mutex>
#include <optional>
#include <span>
#include <sstream>
#include <string>
@@ -23,12 +26,30 @@
#include <unistd.h>
#include <vector>
#define XXH_INLINE_ALL 1
#include <xxhash.h>
#ifdef NDEBUG
# define unreachable() __builtin_unreachable()
#else
# define unreachable() assert(0 && "unreachable")
#endif
inline uint64_t hash_string(std::string_view str) {
return XXH3_64bits(str.data(), str.size());
}
class HashCmp {
public:
static size_t hash(const std::string_view &k) {
return hash_string(k);
}
static bool equal(const std::string_view &k1, const std::string_view &k2) {
return k1 == k2;
}
};
namespace mold {
using namespace std::literals::string_literals;
@@ -50,11 +71,15 @@ inline char *output_tmpfile;
inline char *socket_tmpfile;
inline thread_local bool opt_demangle;
inline u8 *output_buffer_start = nullptr;
inline u8 *output_buffer_end = nullptr;
extern const std::string mold_version;
std::string_view errno_string();
void cleanup();
void install_signal_handler();
i64 get_default_thread_count();
//
// Error output
@@ -159,25 +184,43 @@ private:
// Utility functions
//
// Some C++ libraries haven't implemented std::has_single_bit yet.
inline bool has_single_bit(u64 val) {
return std::popcount(val) == 1;
}
// Some C++ libraries haven't implemented std::bit_ceil yet.
inline u64 bit_ceil(u64 val) {
if (has_single_bit(val))
return val;
return (u64)1 << (64 - std::countl_zero(val));
}
inline u64 align_to(u64 val, u64 align) {
if (align == 0)
return val;
assert(std::popcount(align) == 1);
assert(has_single_bit(align));
return (val + align - 1) & ~(align - 1);
}
inline u64 align_down(u64 val, u64 align) {
assert(std::popcount(align) == 1);
assert(has_single_bit(align));
return val & ~(align - 1);
}
inline u64 next_power_of_two(u64 val) {
assert(val >> 63 == 0);
if (val == 0 || val == 1)
return 1;
return (u64)1 << (64 - std::countl_zero(val - 1));
inline u64 bit(u64 val, i64 pos) {
return (val >> pos) & 1;
};
// Returns [hi:lo] bits of val.
inline u64 bits(u64 val, u64 hi, u64 lo) {
return (val >> lo) & (((u64)1 << (hi - lo + 1)) - 1);
}
inline i64 sign_extend(u64 val, i64 size) {
return (i64)(val << (63 - size)) >> (63 - size);
};
template <typename T, typename Compare = std::less<T>>
void update_minimum(std::atomic<T> &atomic, u64 new_val, Compare cmp = {}) {
T old_val = atomic;
@@ -215,14 +258,19 @@ inline void sort(T &vec, U less) {
std::stable_sort(vec.begin(), vec.end(), less);
}
inline i64 write_string(u8 *buf, std::string_view str) {
template <typename T>
inline void remove_duplicates(std::vector<T> &vec) {
vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
}
inline i64 write_string(void *buf, std::string_view str) {
memcpy(buf, str.data(), str.size());
buf[str.size()] = '\0';
*((u8 *)buf + str.size()) = '\0';
return str.size() + 1;
}
template <typename T>
inline i64 write_vector(u8 *buf, const std::vector<T> &vec) {
inline i64 write_vector(void *buf, const std::vector<T> &vec) {
i64 sz = vec.size() * sizeof(T);
memcpy(buf, vec.data(), sz);
return sz;
@@ -258,13 +306,16 @@ inline u64 read_uleb(u8 *&buf) {
return val;
}
inline u64 read_uleb(u8 const*&buf) {
return read_uleb(const_cast<u8 *&>(buf));
}
inline i64 uleb_size(u64 val) {
i64 i = 0;
do {
i++;
val >>= 7;
} while (val);
return i;
#pragma unroll
for (int i = 1; i < 9; i++)
if (val < ((u64)1 << (7 * i)))
return i;
return 9;
}
template <typename C>
@@ -305,7 +356,7 @@ public:
void resize(i64 nbuckets) {
this->~ConcurrentMap();
nbuckets = std::max<i64>(MIN_NBUCKETS, next_power_of_two(nbuckets));
nbuckets = std::max<i64>(MIN_NBUCKETS, bit_ceil(nbuckets));
this->nbuckets = nbuckets;
keys = (std::atomic<const char *> *)calloc(nbuckets, sizeof(keys[0]));
@@ -317,7 +368,7 @@ public:
if (!keys)
return {nullptr, false};
assert(std::popcount<u64>(nbuckets) == 1);
assert(has_single_bit(nbuckets));
i64 idx = hash & (nbuckets - 1);
i64 retry = 0;
@@ -376,10 +427,28 @@ private:
};
//
// threads.cc
// output-file.h
//
void set_thread_count(i64 n);
template <typename C>
class OutputFile {
public:
static std::unique_ptr<OutputFile<C>>
open(C &ctx, std::string path, i64 filesize, i64 perm);
virtual void close(C &ctx) = 0;
virtual ~OutputFile() = default;
u8 *buf = nullptr;
std::string path;
i64 filesize;
bool is_mmapped;
bool is_unmapped = false;
protected:
OutputFile(std::string path, i64 filesize, bool is_mmapped)
: path(path), filesize(filesize), is_mmapped(is_mmapped) {}
};
//
// hyperloglog.cc
@@ -407,6 +476,65 @@ private:
std::vector<std::atomic_uint8_t> buckets;
};
//
// glob.cc
//
class Glob {
typedef enum { STRING, STAR, QUESTION, BRACKET } Kind;
struct Element {
Element(Kind k) : kind(k) {}
Kind kind;
std::string str;
std::bitset<256> bitset;
};
public:
static std::optional<Glob> compile(std::string_view pat);
bool match(std::string_view str);
private:
Glob(std::vector<Element> &&vec) : elements(vec) {}
static bool do_match(std::string_view str, std::span<Element> elements);
std::vector<Element> elements;
};
//
// multi-glob.cc
//
class MultiGlob {
public:
bool add(std::string_view pat, u32 val);
void compile();
bool empty() const { return strings.empty(); }
std::optional<u32> find(std::string_view str);
private:
struct TrieNode {
u32 value = -1;
TrieNode *suffix_link = nullptr;
std::unique_ptr<TrieNode> children[256];
};
void fix_suffix_links(TrieNode &node);
void fix_values();
std::vector<std::string> strings;
std::unique_ptr<TrieNode> root;
std::vector<std::pair<Glob, u32>> globs;
std::vector<u32> values;
bool compiled = false;
};
//
// uuid.cc
//
std::array<u8, 16> get_uuid_v4();
//
// filepath.cc
//
@@ -432,7 +560,7 @@ std::string_view demangle(std::string_view name);
class ZlibCompressor {
public:
ZlibCompressor(std::string_view input);
ZlibCompressor(u8 *buf, i64 size);
void write_to(u8 *buf);
i64 size() const;
@@ -590,6 +718,12 @@ public:
// because archive members may have the same name.
return parent->name + ":" + std::to_string(get_offset());
}
if (thin_parent) {
// If this is a thin archive member, the filename part is
// guaranteed to be unique.
return thin_parent->name + ":" + name;
}
return name;
}
@@ -599,6 +733,7 @@ public:
i64 mtime = 0;
bool given_fullpath = true;
MappedFile *parent = nullptr;
MappedFile *thin_parent = nullptr;
int fd = -1;
};
+27 -23
View File
@@ -1,34 +1,34 @@
// This file implements the Aho-Corasick algorithm to match version
// script patterns to symbol strings as quickly as possible.
// This file implements the Aho-Corasick algorithm to match multiple
// glob patterns to symbol strings as quickly as possible.
//
// Here are some examples of version script patterns:
// Here are some examples of glob patterns:
//
// qt_private_api_tag*
// *16QAccessibleCache*
// *32QAbstractFileIconProviderPrivate*
// *17QPixmapIconEngine*
//
// The pattern is a glob pattern, so `*` is a wildcard that matches
// any substring. We sometimes have hundreds of version script
// patterns and have to match them against millions of symbol strings.
// `*` is a wildcard that matches any substring. We sometimes have
// hundreds of glob patterns and have to match them against millions
// of symbol strings.
//
// Aho-Corasick cannot handle complex patterns such as `*foo*bar*`.
// We handle such patterns with GlobPattern. GlobPattern is relatively
// slow, but complex patterns are rare in practice, so it should be OK.
// We handle such patterns with the Glob class. Glob is relatively
// slow, but complex patterns are rare in practice, so it should be
// OK.
#include "mold.h"
#include <queue>
#include <regex>
namespace mold::elf {
namespace mold {
std::optional<u16> VersionMatcher::find(std::string_view str) {
std::optional<u32> MultiGlob::find(std::string_view str) {
assert(compiled);
u32 idx = UINT32_MAX;
if (root) {
std::call_once(once_flag, [&] { compile(); });
// Match against simple glob patterns
TrieNode *node = root.get();
@@ -53,13 +53,13 @@ std::optional<u16> VersionMatcher::find(std::string_view str) {
}
// Match against complex glob patterns
for (std::pair<GlobPattern, u32> &glob : globs)
for (std::pair<Glob, u32> &glob : globs)
if (glob.first.match(str))
idx = std::min(idx, glob.second);
if (idx == UINT32_MAX)
return {};
return versions[idx];
return values[idx];
}
static bool is_simple_pattern(std::string_view pat) {
@@ -82,17 +82,17 @@ static std::string handle_stars(std::string_view pat) {
return "\0"s + str + "\0"s;
}
bool VersionMatcher::add(std::string_view pat, u16 ver) {
bool MultiGlob::add(std::string_view pat, u32 val) {
assert(!compiled);
assert(!pat.empty());
u32 idx = strings.size();
strings.push_back(std::string(pat));
versions.push_back(ver);
values.push_back(val);
// Complex glob pattern
if (!is_simple_pattern(pat)) {
if (std::optional<GlobPattern> glob = GlobPattern::compile(pat)) {
if (std::optional<Glob> glob = Glob::compile(pat)) {
globs.push_back({std::move(*glob), idx});
return true;
}
@@ -114,13 +114,17 @@ bool VersionMatcher::add(std::string_view pat, u16 ver) {
return true;
}
void VersionMatcher::compile() {
fix_suffix_links(*root);
fix_values();
void MultiGlob::compile() {
assert(!compiled);
compiled = true;
if (root) {
fix_suffix_links(*root);
fix_values();
}
}
void VersionMatcher::fix_suffix_links(TrieNode &node) {
void MultiGlob::fix_suffix_links(TrieNode &node) {
for (i64 i = 0; i < 256; i++) {
if (!node.children[i])
continue;
@@ -146,7 +150,7 @@ void VersionMatcher::fix_suffix_links(TrieNode &node) {
}
}
void VersionMatcher::fix_values() {
void MultiGlob::fix_values() {
std::queue<TrieNode *> queue;
queue.push(root.get());
@@ -163,4 +167,4 @@ void VersionMatcher::fix_values() {
} while (!queue.empty());
}
} // namespace mold::elf
} // namespace mold
+27 -30
View File
@@ -1,11 +1,12 @@
#include "mold.h"
#include <fcntl.h>
#include <filesystem>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
namespace mold::elf {
namespace mold {
inline u32 get_umask() {
u32 orig_umask = umask(0);
@@ -23,8 +24,10 @@ open_or_create_file(C &ctx, std::string path, i64 filesize, i64 perm) {
if (fd == -1)
Fatal(ctx) << "cannot open " << path2 << ": " << errno_string();
// Reuse an existing file if exists and writable.
if (rename(path.c_str(), path2) == 0) {
// Reuse an existing file if exists and writable because on Linux,
// writing to an existing file is much faster than creating a fresh
// file and writing to it.
if (ctx.overwrite_output_file && rename(path.c_str(), path2) == 0) {
::close(fd);
fd = ::open(path2, O_RDWR | O_CREAT, perm);
if (fd != -1 && !ftruncate(fd, filesize) && !fchmod(fd, perm & ~get_umask()))
@@ -44,11 +47,11 @@ open_or_create_file(C &ctx, std::string path, i64 filesize, i64 perm) {
return {fd, path2};
}
template <typename E>
class MemoryMappedOutputFile : public OutputFile<E> {
template <typename C>
class MemoryMappedOutputFile : public OutputFile<C> {
public:
MemoryMappedOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, true) {
MemoryMappedOutputFile(C &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<C>(path, filesize, true) {
i64 fd;
std::tie(fd, output_tmpfile) = open_or_create_file(ctx, path, filesize, perm);
@@ -57,9 +60,12 @@ public:
if (this->buf == MAP_FAILED)
Fatal(ctx) << path << ": mmap failed: " << errno_string();
::close(fd);
mold::output_buffer_start = this->buf;
mold::output_buffer_end = this->buf + filesize;
}
void close(Context<E> &ctx) override {
void close(C &ctx) override {
Timer t(ctx, "close_file");
if (!this->is_unmapped)
@@ -71,18 +77,18 @@ public:
}
};
template <typename E>
class MallocOutputFile : public OutputFile<E> {
template <typename C>
class MallocOutputFile : public OutputFile<C> {
public:
MallocOutputFile(Context<E> &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<E>(path, filesize, false), perm(perm) {
MallocOutputFile(C &ctx, std::string path, i64 filesize, i64 perm)
: OutputFile<C>(path, filesize, false), perm(perm) {
this->buf = (u8 *)mmap(NULL, filesize, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (this->buf == MAP_FAILED)
Fatal(ctx) << "mmap failed: " << errno_string();
}
void close(Context<E> &ctx) override {
void close(C &ctx) override {
Timer t(ctx, "close_file");
if (this->path == "-") {
@@ -104,9 +110,9 @@ private:
i64 perm;
};
template <typename E>
std::unique_ptr<OutputFile<E>>
OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
template <typename C>
std::unique_ptr<OutputFile<C>>
OutputFile<C>::open(C &ctx, std::string path, i64 filesize, i64 perm) {
Timer t(ctx, "open_file");
if (path.starts_with('/') && !ctx.arg.chroot.empty())
@@ -121,24 +127,15 @@ OutputFile<E>::open(Context<E> &ctx, std::string path, i64 filesize, i64 perm) {
is_special = true;
}
std::unique_ptr<OutputFile<E>> file;
OutputFile<C> *file;
if (is_special)
file = std::make_unique<MallocOutputFile<E>>(ctx, path, filesize, perm);
file = new MallocOutputFile<C>(ctx, path, filesize, perm);
else
file = std::make_unique<MemoryMappedOutputFile<E>>(ctx, path, filesize, perm);
file = new MemoryMappedOutputFile<C>(ctx, path, filesize, perm);
if (ctx.arg.filler != -1)
memset(file->buf, ctx.arg.filler, filesize);
return file;
return std::unique_ptr<OutputFile<C>>(file);
}
#define INSTANTIATE(E) \
template class OutputFile<E>;
INSTANTIATE(X86_64);
INSTANTIATE(I386);
INSTANTIATE(ARM64);
INSTANTIATE(ARM32);
INSTANTIATE(RISCV64);
} // namespace mold::elf
} // namespace mold
+1 -1
View File
@@ -10,6 +10,6 @@ test: $(TESTS)
# command output. `tail -r` outputs an input from the last line to the
# first.
$(TESTS):
@./$@ 2>&1 | tail -r | tail -r
@set -o pipefail; ./$@ 2>&1 | tail -r | tail -r
.PHONY: test $(TESTS)
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+9 -10
View File
@@ -1,24 +1,23 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
echo 'int main() { return 0; }' > $t/a.c
echo 'int main() { return 0; }' > $t/b.c
echo 'int main() { return 0; }' | $CC -c -o $t/a.o -xc -
echo 'int main() { return 1; }' | $CC -c -o $t/b.o -xc -
! $CC -B. -o $t/exe $t/a.c $t/b.c 2> /dev/null || false
$CC -B. -o $t/exe $t/a.c $t/b.c -Wl,-allow-multiple-definition
$CC -B. -o $t/exe $t/a.c $t/b.c -Wl,-z,muldefs
! $CC -B. -o $t/exe $t/a.o $t/b.o 2> /dev/null || false
$CC -B. -o $t/exe $t/a.o $t/b.o -Wl,-allow-multiple-definition
$CC -B. -o $t/exe $t/a.o $t/b.o -Wl,-z,muldefs
echo OK
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+5 -6
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
@@ -21,7 +20,7 @@ _start:
nop
EOF
"$mold" -o $t/b.so $t/a.o -auxiliary foo -f bar -shared
./mold -o $t/b.so $t/a.o -auxiliary foo -f bar -shared
readelf --dynamic $t/b.so > $t/log
fgrep -q 'Auxiliary library: [foo]' $t/log
+5 -6
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
@@ -18,7 +17,7 @@ mkdir -p $t
echo '.globl _start; _start: jmp loop' | $CC -o $t/a.o -c -x assembler -
echo '.globl loop; loop: jmp loop' | $CC -o $t/b.o -c -x assembler -
"$mold" -static -o $t/exe $t/a.o $t/b.o
./mold -static -o $t/exe $t/a.o $t/b.o
$OBJDUMP -d $t/exe > /dev/null
file $t/exe | grep -q ELF
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+5 -6
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
@@ -26,6 +25,6 @@ EOF
rm -f $t/a.a
ar rcs $t/a.a $t/a.o
"$mold" -o $t/exe $t/a.a
./mold -o $t/exe $t/a.a
echo OK
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+8 -9
View File
@@ -1,22 +1,21 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
(! "$mold" -zfoo) 2>&1 | grep -q 'unknown command line option: -zfoo'
(! "$mold" -z foo) 2>&1 | grep -q 'unknown command line option: -z foo'
(! "$mold" -abcdefg) 2>&1 | grep -q 'unknown command line option: -abcdefg'
(! "$mold" --abcdefg) 2>&1 | grep -q 'unknown command line option: --abcdefg'
{ ./mold -zfoo || true; } 2>&1 | grep -q 'unknown command line option: -zfoo'
{ ./mold -z foo || true; } 2>&1 | grep -q 'unknown command line option: -z foo'
{ ./mold -abcdefg || true; } 2>&1 | grep -q 'unknown command line option: -abcdefg'
{ ./mold --abcdefg || true; } 2>&1 | grep -q 'unknown command line option: --abcdefg'
echo OK
+12 -13
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
@@ -19,16 +18,16 @@ int foo();
int main() { foo(); }
EOF
! "$mold" -o $t/exe $t/a.o --color-diagnostics 2> $t/log
grep -q $'\033' $t/log
! "$mold" -o $t/exe $t/a.o --color-diagnostics=always 2> $t/log
grep -q $'\033' $t/log
! "$mold" -o $t/exe $t/a.o --color-diagnostics=never 2> $t/log
! ./mold -o $t/exe $t/a.o --color-diagnostics 2> $t/log
! grep -q $'\033' $t/log || false
! "$mold" -o $t/exe $t/a.o --color-diagnostics=auto 2> $t/log
! ./mold -o $t/exe $t/a.o --color-diagnostics=always 2> $t/log
grep -q $'\033' $t/log
! ./mold -o $t/exe $t/a.o --color-diagnostics=never 2> $t/log
! grep -q $'\033' $t/log || false
! ./mold -o $t/exe $t/a.o --color-diagnostics=auto 2> $t/log
! grep -q $'\033' $t/log || false
echo OK
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+38
View File
@@ -0,0 +1,38 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
t=out/test/elf/$testname
mkdir -p $t
which dwarfdump >& /dev/null || { echo skipped; exit; }
# Clang may not support -gz=zlib-gnu
echo 'int main() {}' | $CXX -o /dev/null -gz=zlib-gnu -xc++ ->& /dev/null ||
{ echo skipped; exit; }
cat <<EOF | $CXX -c -o $t/a.o -g -gz=zlib-gnu -xc++ -
int main() {
return 0;
}
EOF
cat <<EOF | $CXX -c -o $t/b.o -g -gz=zlib-gnu -xc++ -
int foo() {
return 0;
}
EOF
$CC -B. -o $t/exe $t/a.o $t/b.o
dwarfdump $t/exe > /dev/null
readelf --sections $t/exe | fgrep -q .debug_info
echo ' OK'
+5 -6
View File
@@ -1,22 +1,21 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
which dwarfdump >& /dev/null || { echo skipped; exit; }
cat <<EOF | $CXX -c -o $t/a.o -g -gz=zlib-gnu -xc++ -
cat <<EOF | $CXX -c -o $t/a.o -g -gz=zlib -xc++ -
int main() {
return 0;
}
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+6 -5
View File
@@ -1,19 +1,20 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
which dwarfdump >& /dev/null || { echo skipped; exit; }
cat <<EOF | $CXX -c -o $t/a.o -g -xc++ -
#include <iostream>
struct Foo {
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
t=out/test/elf/$testname
mkdir -p $t
cat <<EOF > $t/a.h
#define A 23
#define B 99
EOF
cat <<EOF | $GCC -o $t/b.o -c -xc - -I$t -g3
#include "a.h"
extern int z();
int main () { return z() - 122; }
EOF
cat <<EOF | $GCC -o $t/c.o -c -xc - -I$t -g3
#include "a.h"
int z() { return A + B; }
EOF
$GCC -B. -o $t/exe $t/b.o $t/c.o
$OBJDUMP --dwarf=macro $t/exe > $t/log
! grep 'DW_MACRO_import -.* 0x0$' $t/log || false
echo OK
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+8 -9
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
@@ -22,13 +21,13 @@ int main() {
EOF
! $CC -B. -o $t/exe $t/a.o -Wl,-no-demangle 2> $t/log || false
grep -q 'undefined symbol: .*: _Z3fooii' $t/log
grep -q 'undefined symbol: _Z3fooii$' $t/log
! $CC -B. -o $t/exe $t/a.o -Wl,-demangle 2> $t/log || false
grep -Eq 'undefined symbol: .*: foo\(int, int\)' $t/log
grep -Eq 'undefined symbol: foo\(int, int\)$' $t/log
! $CC -B. -o $t/exe $t/a.o 2> $t/log || false
grep -Eq 'undefined symbol: .*: foo\(int, int\)' $t/log
grep -Eq 'undefined symbol: foo\(int, int\)$' $t/log
cat <<EOF | $CC -c -o $t/b.o -xc -
extern int Pi;
@@ -38,6 +37,6 @@ int main() {
EOF
! $CC -B. -o $t/exe $t/b.o -Wl,-demangle 2> $t/log || false
grep -q 'undefined symbol: .*: Pi' $t/log
grep -q 'undefined symbol: Pi$' $t/log
echo OK
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+8 -9
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
@@ -27,25 +26,25 @@ foo:
nop
EOF
"$mold" -o $t/exe $t/a.o
./mold -o $t/exe $t/a.o
readelf --symbols $t/exe > $t/log
fgrep -q _start $t/log
fgrep -q foo $t/log
fgrep -q .Lbar $t/log
"$mold" -o $t/exe $t/a.o --discard-locals
./mold -o $t/exe $t/a.o --discard-locals
readelf --symbols $t/exe > $t/log
fgrep -q _start $t/log
fgrep -q foo $t/log
! fgrep -q .Lbar $t/log || false
"$mold" -o $t/exe $t/a.o --discard-all
./mold -o $t/exe $t/a.o --discard-all
readelf --symbols $t/exe > $t/log
fgrep -q _start $t/log
! fgrep -q foo $t/log || false
! fgrep -q .Lbar $t/log || false
"$mold" -o $t/exe $t/a.o --strip-all
./mold -o $t/exe $t/a.o --strip-all
readelf --symbols $t/exe > $t/log
! fgrep -q _start $t/log || false
! fgrep -q foo $t/log || false
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
+4 -5
View File
@@ -1,16 +1,15 @@
#!/bin/bash
export LC_ALL=C
set -e
CC="${CC:-cc}"
CXX="${CXX:-c++}"
GCC="${GCC:-gcc}"
GXX="${GXX:-g++}"
CC="${TEST_CC:-cc}"
CXX="${TEST_CXX:-c++}"
GCC="${TEST_GCC:-gcc}"
GXX="${TEST_GXX:-g++}"
OBJDUMP="${OBJDUMP:-objdump}"
MACHINE="${MACHINE:-$(uname -m)}"
testname=$(basename "$0" .sh)
echo -n "Testing $testname ... "
cd "$(dirname "$0")"/../..
mold="$(pwd)/mold"
t=out/test/elf/$testname
mkdir -p $t
Loaded 100 of 422 files, more files were not shown because too many files have changed in this diff. Show more