Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a binutils module #6063

Open
dcbaker opened this issue Oct 17, 2019 · 14 comments
Open

Add a binutils module #6063

dcbaker opened this issue Oct 17, 2019 · 14 comments
Assignees

Comments

@dcbaker
Copy link
Member

dcbaker commented Oct 17, 2019

I think having a module for dealing with the tools provided by gnu binutils (and alternate implementations like the llvm ones) would be pretty useful.

In my case I'm using objcopy, and being able to have some of the arguments about the host and build machine populated automatically would be pretty handy.

There's also #5995, and having some sort of binutils.configure_linker_script seems useful.

I'm planning to get around to this myself, but there are some other things I need to get to first, including fixing regressions in 0.50. The main purpose of this issue is to collect other ideas that should be added to this module and solicit feedback.

@dcbaker dcbaker self-assigned this Oct 17, 2019
@jpakkane
Copy link
Member

Which features are those? IIRC the name-prefixed versions have (most of) the settings already so specifying objcopy in your cross file's executable section makes find_program('objcopy') work without additional setup (granted by usage of it has been fairly simple).

@dcbaker
Copy link
Member Author

dcbaker commented Oct 18, 2019

in particular the --binary-architecture and --output arguments to objcopy. Having to have logic to figure out what goes there for each supported architecture is a pain. being able to just have something like:

o = binutils.objcopy(
  output : 'foo.o',
  input : 'input.file',
  target : 'host' | 'build' # target?
  args : ['some', 'other' args'],
)

would be much nicer than what I have now, which is:

if host_machine.system() == 'linux' # TODO: solaris, bsd, etc
  if host_machine.cpu_family() == 'x86_64'
    b_arch = 'i386:x86_64'
    b_type = 'elf64-x86-64'
  elif host_machine.cpu_fmaily() == 'x86'
    b_arch = 'i386'
    b_type = 'elf32-i386'
  endif
endif
# TODO: all the other things

p = find_program('objcopy')
o = custom_target(
  ...
  command : [
    p, '--input', 'binary', '--output', b_type, '--binary-architecture', b_arch, '@INPUT@', '@OUTPUT@'
  ]
)

At least on my system there is no i686-linux-gnu-objcopy, in fact, the only "cross" objcopy's I have are mingw ones, even my arm toolchains don't have their own objcopy, just the x86_64-linux-gnu one.

@bwidawsk
Copy link

Would there be some way to specify the binutils, like llvm vs. gnu?

@jpakkane
Copy link
Member

For comparison, Debian does have an i686-linux-gnu-objcopy.

@marc-hb
Copy link

marc-hb commented Oct 19, 2019

I believe Meson doesn't let the user invoke the assembler or the linker directly, correct? The only two options are

  1. to rely on the compiler front-end (e.g.: [binaries].c -Wl,--script ... -Wl,--etc ...), or
  2. to step out of Meson and do everything "manually" with a custom_target( command: "ld <flags>...", )

Meson is still relatively new to me, sorry if I missed something here.

While C and C++ are highly portable languages, assembly and linker code are very far from it (and will probably never be). Consider the EDK2 project for instance: it supports a large and diverse set of operating systems and toolchains. However for x86 assembly it supports only one assembler, which I think rules out option 1.

So I hope this "Add a binutils module" issue #6063 is among others about adding new (and of course optional) as and ld fields in cross files and corresponding logic behind them. Is it?
https://mesonbuild.com/Cross-compilation.html#defining-the-environment

@jpakkane
Copy link
Member

You can already do that. The binaries entry overrides all lookups via find_program, not just compilers. If you have a program called flibflob, you can override that to whatever you want by putting a declaration for flibflob in your cross file.

@marc-hb
Copy link

marc-hb commented Oct 29, 2019

You can already do that.

Was this an answer to "Would there be some way to specify the binutils, like llvm vs. gnu" ?

@marc-hb
Copy link

marc-hb commented Oct 29, 2019

  1. to step out of Meson and do all the linking "manually" with a custom_target()

This proved easier than I thought, not the least thanks tofake_lib.extract_all_objects(), see pseudo-code below. This lets me point Meson at a very specific linker [script] without forcing any specific compiler or assembler. Freedom from the (tool)chain!
It's early days but seems to work for now; should I expect any issue or important feature(s) missing because I'm hiding from meson the true linking nature of bare_metal's generation? Is this a hack and if yes is there any more "mesonic" way to do this?

# This library is not used, this is just a convenient way to
# compile everything in one step.
fake_lib = static_library(...
   '1.c', '2.S', ... , 
    c_args: [ '-fno-pic', ....]
 )

# fake_lib also solves this circular dependency problem  \o/
# https://github.com/mesonbuild/meson/pull/6061#issuecomment-547138357
#
# '@' is not allowed in linker scripts;
# search and replace 'fakelib@sta' with 'fakelib?sta'
# https://github.com/mesonbuild/meson/pull/291
lib_dir = '?'.join(fake_lib.get_id().split('@'))
very_not_portable_linker_script = configure_file(...
   configuration : { 'OBJSDIR': lib_dir }
)

very_specific_linker = find_program('my-ld')

bare_metal = custom_target(...
   input: fake_lib.extract_all_objects(),
   # [2019-11-12 comment]
   # Ideally, linker scripts should be part of the "input:", however:
   # 1. prefixing files("myscript.lds")[n] with '--script=' doesn't seem
   #    possible: "error: can only concatenate str (not "File") to str"
   # 2. prefixing only _some_ of the @INPUTn@ and not the object files
   #    seems difficult too.
   # So let's pretend linker scripts are "depend_files:" not part of the
   # "command:" and then sneak them into the command: behind
   # meson's back.
   depend_files: very_not_portable_linker_script,
   command: [ very_specific_linker ] + [ '@INPUT@' ]
           + [ '--script=' + very_not_portable_linker_script ]
           + [ '-o', '@OUTPUT@' ]
   ...
)

@marc-hb
Copy link

marc-hb commented Oct 29, 2019

  1. to rely on the compiler front-end (e.g.: [binaries].c -Wl,--script ... -Wl,--etc ...),

I tried this with XCode 11 (clang v10) and it was a disaster. At first sight -fuse-ld=/my/home/built/linker seems to work but in fact not because this LLVM instance insists on passing a few macOS-specific options like -macosx_version_min 123 or -lto_library something.dylib - even when appending and prepending -fno-lto to the clang command line!

Granted this is just one front-end example, however I suspect most low-level / bare metal development requires invoking the linker directly because of the fine level of control required. I mean I suspect the front-end layer of indirection typically assumes too much and that gets in the way. I'm of course not considering targets that depend on a single, vendor-specific toolchain. Who cares about those :-)

Curious how high is "bare-metal" on the list of Meson requirements/priorities?

Found these:
https://reviews.llvm.org/D25932 "Unconditionally pass -lto_library to the linker on Darwin"
https://lists.llvm.org/pipermail/llvm-dev/2018-February/121135.html
"[llvm-dev] how to avoid linking with libLTO?"
The clang -mlinker-version=... workaround suggested at https://lists.llvm.org/pipermail/llvm-dev/2018-January/120215.html does remove a couple ld64-specific flags. Then it just fails on the next ld64 flags

linux/scripts/link-vmlinux.sh invokes ${LD} directly.

@jpakkane
Copy link
Member

Bare metal is important to us. There are in fact commercial products that ship with firmwares built with Meson (or, at least, this seems very strongly to be the case given certain bugs and discussions I have had with people, but sadly there is no public info I can point people to).

@marc-hb
Copy link

marc-hb commented Nov 8, 2019

The binaries entry overrides all lookups via find_program, not just compilers. If you have a program called flibflob, you can override that to whatever you want by putting a declaration for flibflob in your cross file.

Thanks! However it's more subtle and took me some time to figure it out. While powerful, I think it's very unusual for a string in any language to be both a symbol and a literal!

# 'ld' is *both* a property name and a literal fallback. find_program()
# gives precedence to whatever is in the last --cross-file that defines
# ld=... When not found, it falls back on searching for 'ld' in the
# PATH. Definitions in other --cross-file earlier on the command line
# are totally ignored.
# https://mesonbuild.com/Reference-manual.html#find_program
my_ld = find_program('ld')

@marc-h38
Copy link
Contributor

marc-h38 commented Jan 9, 2020

While C and C++ are highly portable languages, linker scripts are very far from it ...

At first sight -fuse-ld=/my/home/built/linker seems to work

In theory, -fuse-ld= allows you to decouple the choice of the linker from the choice of the compiler. After a bit more research and practice, usage of -fuse-ld= seems rare and most of the time it's only to switch between linkers "close" to each other like bfd and gold (which are even part of the same package). That flexibility doesn't seem to get that much testing in practice.

@dcbaker of Add a way to select the dynamic linker #6207 fame, any "real-world" experience of -fuse-ld= to share? From what you've seen, how is -fuse-ld= typically used and what for?

@marc-h38
Copy link
Contributor

Seems related to Solving the LD issue #6442

@jhgorse
Copy link

jhgorse commented Dec 18, 2022

When invoked by clang the linker will do what it thinks is correct. For native macos builds, it has macos things:

# builtin ld
% clang -Wl,--version -v
Apple clang version 14.0.0 (clang-1400.0.29.102)
Target: arm64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
 "/Library/Developer/CommandLineTools/usr/bin/ld" -demangle -lto_library /Library/Developer/CommandLineTools/usr/lib/libLTO.dylib -dynamic -arch arm64 -platform_version macos 12.0.0 12.3 -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -o a.out -L/usr/local/lib --version -lSystem /Library/Developer/CommandLineTools/usr/lib/clang/14.0.0/lib/darwin/libclang_rt.osx.a
ld: unknown option: --version
clang: error: linker command failed with exit code 1 (use -v to see invocation)

# homebrew clang
% /opt/homebrew/opt/llvm/bin/lld
lld is a generic driver.
Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld (WebAssembly) instead

# homebrew ld.lld for unix builds
% clang -Wl,--version -fuse-ld=/opt/homebrew/opt/llvm/bin/ld.lld -v
Apple clang version 14.0.0 (clang-1400.0.29.102)
Target: arm64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
 "/opt/homebrew/opt/llvm/bin/ld.lld" -demangle -lto_library /Library/Developer/CommandLineTools/usr/lib/libLTO.dylib -dynamic -arch arm64 -platform_version macos 12.0.0 12.3 -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -o a.out -L/usr/local/lib --version -lSystem /Library/Developer/CommandLineTools/usr/lib/clang/14.0.0/lib/darwin/libclang_rt.osx.a
ld.lld: error: unknown argument '-dynamic', did you mean '-Bdynamic'
ld.lld: error: unknown argument '-arch'
ld.lld: error: unknown argument '-platform_version'
ld.lld: error: unknown argument '-syslibroot'
Homebrew LLD 15.0.6 (compatible with GNU linkers)
clang: error: linker command failed with exit code 1 (use -v to see invocation)

For cross builds, it removes the macos things. For example, --target=arm-linux-gnu -mfloat-abi=hard for 32 bit arm linux target. Which I am exploring here: mesonbuild/wrapdb#830

% clang -Wl,--version -fuse-ld=/opt/homebrew/opt/llvm/bin/ld.lld --target=arm-linux-gnu -mfloat-abi=hard -v
Apple clang version 14.0.0 (clang-1400.0.29.102)
Target: arm-unknown-linux-gnu
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
 "/opt/homebrew/opt/llvm/bin/ld.lld" -EL -X --eh-frame-hdr -m armelf_linux_eabi -dynamic-linker /lib/ld-linux-armhf.so.3 -o a.out crt1.o crti.o crtbegin.o -L/usr/lib/../lib -L/Library/Developer/CommandLineTools/usr/bin/../lib -L/usr/lib --version -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed crtend.o crtn.o
Homebrew LLD 15.0.6 (compatible with GNU linkers)

Cheers,
Joe

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

No branches or pull requests

6 participants