Skip to content

Support -m elf_x86_64_sol2#1197

Merged
davidlattimore merged 1 commit into
wild-linker:mainfrom
daniel-levin:main
Oct 13, 2025
Merged

Support -m elf_x86_64_sol2#1197
davidlattimore merged 1 commit into
wild-linker:mainfrom
daniel-levin:main

Conversation

@daniel-levin
Copy link
Copy Markdown
Contributor

@daniel-levin daniel-levin commented Oct 12, 2025

Currently, we have a somewhat janky workaround for getting Wild to work on Illumos: #1173

I've opened a PR in Clang to bring the Solaris (Illumos) driver up to parity with the others: llvm/llvm-project#163000. Once this lands, it will be possible to use the --ld-path argument identically to other platforms. This benefits all linkers - including lld, Gold, Mold and Wild. That is, Clang will start to supply GNU ld-compatible flags if an unknown, non-Solaris Link Editor is supplied.

Of course, we need to then support the "emulation" elf_x86_64_sol2. Testing Wild with this change, and Clang with my change above, I have been able to link a couple of Rust programs, including ripgrep. Excitingly, it also links LLVM itself. Finally, I confirmed with DTrace that the arguments were expected.

❯ readelf -p .comment $(which rg)

String dump of section '.comment':
  [     0]  GCC: (OmniOS 151054/14.2.0-il-1) 14.2.0
  [    29]  @(#)illumos  May 2025
  [    3f]  rustc version 1.90.0 (1159e78c4 2025-09-14)
  [    6b]  Linker: Wild version 0.6.0
❯ readelf -p .comment /home/omnios/llvm-project/build/bin/clang

String dump of section '.comment':
  [     0]  GCC: (OmniOS 151054/14.2.0-il-1) 14.2.0
  [    28]  OmniOS/151054 clang version 20.1.8
  [    4c]  @(#)illumos  May 2025
  [    62]  Linker: Wild version 0.6.0

@mati865
Copy link
Copy Markdown
Member

mati865 commented Oct 12, 2025

Should elf_x86_64_sol2 set the defaults from #1173 or are they supposed to be also set for elf_x86_64 on Solaris/Illumos?

@daniel-levin
Copy link
Copy Markdown
Contributor Author

daniel-levin commented Oct 12, 2025 via email

@mati865
Copy link
Copy Markdown
Member

mati865 commented Oct 12, 2025

Ignoring -C is necessary because you interpose ld which is the Solaris linker, instead of interposing ld.bfd binary. So that one will indeed be fixed by --ld-path.
But the change to Clang doesn't seem to affect --dynamic-linker, so wouldn't that one be still necessary?

@daniel-levin
Copy link
Copy Markdown
Contributor Author

Ah, you are correct. --dynamic-linker would have to stay.

@davidlattimore
Copy link
Copy Markdown
Member

Do you think we should make it so that -m elf_x86_64_sol2 sets the default dynamic linker? From a quick look at the code, it might be simpler if the FromStr implementation for Architecture got deleted. The sub_options in args.rs look like they could handle the different architectures well.

@mati865
Copy link
Copy Markdown
Member

mati865 commented Oct 12, 2025

FWIW, I made this change:

❯ jj show
Commit ID: c3f87297d75106c5ea91d3c51b74647dde2a096b
Change ID: yoxyrupzktlzkrpqvmxzswuupwokynmv
Author   : Mateusz Mikuła <oss@mateuszmikula.dev> (2025-10-12 12:11:30)
Committer: Mateusz Mikuła <oss@mateuszmikula.dev> (2025-10-12 12:11:30)

    (no description set)

Modified regular file libwild/src/args.rs:
    ...
 309  309:             #[cfg(target_os = "illumos")]
 310  310:             dynamic_linker: Some(Path::new("/lib/amd64/ld.so.1").into()),
 311  311:             #[cfg(not(target_os = "illumos"))]
 312  312:             dynamic_linker: NoneSome(Path::new("/lib/ld-linux-x86-64.so.2").into()),
 313  313:             output_kind: None,
 314  314:             time_phase_options: None,
 315  315:             num_threads: None,
    ...

And all our tests and mold tests still pass, so maybe there should be a default value for all platforms?

I have checked Clang binary linked on Linux with ld with --dynamic-linker removed and ld sets interpreter to a non-existent file, while Wild doesn't set it at all:

❯ WILD_REFERENCE_LINKER=/usr/bin/ld.bfd ./run-with ~/Projects/wild/ld
WARNING: wild: --plugin /usr/lib/gcc/x86_64-pc-linux-gnu/15.2.1/liblto_plugin.so is not yet supported
wild: ./bin
ref: ./bin.ref-linker
.dynamic.DT_FLAGS_1.NOW
  wild 1
  ref

section.tdata.alignment
  wild 0x4
  ref 0x8

section.interp
  wild Section missing
  ref OK

dynsym.__bss_start.section
  wild
  ref .bss

dynsym._edata.section
  wild
  ref .data

dynsym._end.section
  wild
  ref .bss

segment.PT_INTERP.alignment
  wild
  ref 0x1

segment.PT_INTERP.flags
  wild
  ref 0x4

[..]

❯ readelf -W -p .interp bin.ref-linker

String dump of section '.interp':
  [     0]  /lib/ld64.so.1

EDIT: In any case, I can align the Linux one and add a test later. I can also refactor it if necessary.

@daniel-levin
Copy link
Copy Markdown
Contributor Author

Do you think we should make it so that -m elf_x86_64_sol2 sets the default dynamic linker?

Yes, I do. While @mati865 has identified a viable alternative for --dynamic-linker, I am certain that there will be some other behavior induced by the -m flag that's worth memorializing in an obviously-discoverable way, like a match somewhere.

@daniel-levin
Copy link
Copy Markdown
Contributor Author

daniel-levin commented Oct 12, 2025

@daniel-levin
Copy link
Copy Markdown
Contributor Author

So does lld: https://github.com/llvm/llvm-project/blob/4c8275470465528c469436d37b9aaf71d6598899/lld/ELF/Driver.cpp#L2209. For the record, I'm not interested in cargo culting the behavior of existing linkers but it does corroborate @davidlattimore instinct that -m is a useful point of departure for all kinds of machine-specific code.

@daniel-levin
Copy link
Copy Markdown
Contributor Author

daniel-levin commented Oct 12, 2025

Aside: 67d998c says that printing supported emulations in --help is necessary for autoconf. Also: https://stackoverflow.com/questions/38951492/linkers-emulation-vs-output-format

@daniel-levin
Copy link
Copy Markdown
Contributor Author

@davidlattimore
Copy link
Copy Markdown
Member

Do you want to make the changes in this PR? I don't think it should be an especially big change - just delete the FromStr implementation, fix up the code that was using it, then when elf_x86_64_sol2 is requested, check if there's already a dynamic-linker set and if not, set the appropriate value.

@daniel-levin
Copy link
Copy Markdown
Contributor Author

Do you want to make the changes in this PR?

Yes, made the small change.

Comment thread libwild/src/args.rs Outdated
"elf_x86_64_sol2",
"x86-64 ELF target (Solaris)",
|args, _modifier_stack, _value| {
args.dynamic_linker = Some(Path::new("/lib/amd64/ld.so.1").into());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we always set args.dynamic linker then that means that --dynamic-linker=/some/path -m elf_x86_64_sol2 will give a different outcome to -m elf_x86_64_sol2 --dynamic-linker=/some/path. i.e. the -m being later will override the earlier --dynamic-linker flag, which I don't think is what we want. Easiest fix is probably to check if args.dynamic_linker is already set and leave it as-is if so.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D'oh. Good catch.

@daniel-levin
Copy link
Copy Markdown
Contributor Author

Unbroke the test (I missed a branch in the old FromStr implementation) and ensured the supplied --dynamic-linker argument is always respected.

@davidlattimore davidlattimore merged commit b91e818 into wild-linker:main Oct 13, 2025
20 checks passed
@mati865
Copy link
Copy Markdown
Member

mati865 commented Oct 14, 2025

I'm not convinced whether it's sensible to complicate the code by setting the default dynamic linker for each architecture. On Linux, passing --dynamic-linker to the linker is crucial for dynamic executables anyway.

Without having a proper interpreter configured, the binary won't run:

❯ ./bin
fish: Job 1, './bin' terminated by signal SIGSEGV (Address boundary error)

❯ ./bin.ref-linker
exec: Failed to execute process './bin.ref-linker': The file exists and is executable. Check the interpreter or linker?

❯ readelf -p .interp bin bin.ref-linker

File: bin
readelf: Warning: Section '.interp' was not dumped because it does not exist

File: bin.ref-linker

String dump of section '.interp':
  [     0]  /lib/ld64.so.1

@daniel-levin
Copy link
Copy Markdown
Contributor Author

I'm not convinced whether it's sensible to complicate the code by setting the default dynamic linker for each architecture

Emphasis mine. I agree. But, we are not setting the default dynamic linker based on architecture. We're setting the default dynamic linker based on the -m flag, which is labelled as "emulation" in the other linkers. It's not a very well-defined concept. I suspect it evolved organically over time as a series of portability hacks. It means binary format + architecture + system-level conventions. For instance, elf_x86_64_sol2 is distinct from elf_x86_64, even though the final binary produced is still an ELF file for x86_64. The naming convention on x86 is not amazing - surely you would expect elf_x86_64 to be right on Solaris? (It is not). The fact that emulations include OS-level configuration is clearer in the ARM names, e.g. aarch64linux.

Indeed, if you run ld --verbose -m $emulation you can see the linker script for each "emulation". The linker script contains default search paths for the dynamic linker. Here's the one for aarch64linux:

OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start)
SEARCH_DIR("=/usr/aarch64-redhat-linux/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/aarch64-redhat-linux/lib"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");

Unfortunately, it seems that the various open-source toolchains have taken a dependency on this convention, and they expect the 'emulation' flag to require the linker to find the right dynamic linker unless otherwise explicitly provided.

@mati865
Copy link
Copy Markdown
Member

mati865 commented Oct 14, 2025

My bad, I meant emulation rather than architecture. So, my point is on x86_64 Linux you cannot assume the linker will use the correct interpreter (because it will not), and the caller has to supply it anyway.
The only difference between providing it or not, is whether you get a nice error or segfault. In any case, the binary won't work, so both GCC and Clang provide the proper interpreter path anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants