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 freestanding target #106

Closed
wants to merge 3 commits into from
Closed
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions active/0000-freestanding-target.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
- Start Date: 2014-06-03
- RFC PR #: (leave this empty)
- Rust Issue #: (leave this empty)

# Summary

Add support for the "unknown" OS in target triples, and disable split stack
checking on them by default.

# Motivation

One of Rust's important use cases is embedded, OS, or otherwise "bare metal"
software. At the moment, we still depend on LLVM's split-stack prologue for
stack safety. In certain situations, it is impossible or undesirable to
support what LLVM requires to enable this (on x86, a certain thread-local
storage setup). We also link to some libraries unconditionally, making it
difficult to produce freestanding binaries without stubbing them out.

# Detailed design

A target triple consists of three strings separated by a hyphen, with a
possible fourth string at the end preceded by a hyphen. The first is the
architecture, the second is the "vendor", the third is the OS type, and the
optional fourth is environment type. In theory, this specifies precisely what
platform the generated binary will be able to run on. All of this is
determined not by us but by LLVM and other tools. When on bare metal or a
similar environment, there essentially is no OS, and to handle this there is
the concept of "unknown" in the target triple. When the OS is "unknown" many
features can be assumed to be missing, such as dynamic linking (which requires
surprisingly complicated runtime support), thread-local storage (threads are
inherently an OS concept), presence of any given IO routines, etc.

When the compiler encounters such a target, it will never attempt to use
dynamic linking, will disable emitting the split stack prologue, and will not
link to any support libraries. These targets, of the form `*-unknown-unknown`,
will never be a valid platform for rustc itself to run on, and can only be
used for cross-compiling a crate. Statically linking to other crates will
still be valid.
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps we could not explicitly deny dynamic linking, but rather let the linker duke it out? If the system linker does in fact support dynamic linking, it seems like we shouldn't prevent you from doing so. It often doesn't make sense in a "freestanding setting", but linker error messages will make that pretty apparent pretty quickly.

Copy link
Member Author

Choose a reason for hiding this comment

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

That's not always true. You could be using ELF but it dies at runtime on the dynamic object.

Copy link
Member

Choose a reason for hiding this comment

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

I don't quite understand. Each dynamic library has implicit dependencies for various symbols (or so I am led to believe) which are resolved by system libraries. When you you successfully link a dynamic library, and then fail to use it at runtime?

I'm basically thinking that this require explicit code in the compiler to reject this situation, when it should naturally be rejected by the linker as it will ultimately have more knowledge than we do.

Copy link
Member Author

Choose a reason for hiding this comment

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

If we support dynamic linkers, we're going to need to provide a way to set the ELF interpeter at the very least... and I don't really want to dig into the nitty-gritty of supporting dynamic executables for All The Things we could support.

Copy link
Member

Choose a reason for hiding this comment

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

I'm still not understanding what you're saying here. What is an ELF interpreter? How are we supporting dynamic linkers? Is a regular linker different from a dynamic linker? How is this digging into the nitty gritty of supporting dynamic executables?

I don't understand why specifically blacklisting particular platforms is more nitty-gritty than not doing anything at all.

Copy link
Member Author

Choose a reason for hiding this comment

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

The dynamic linker being the runtime relocator (I'll just call it rtld to avoid confusion in the future), the interpreter being the .interp section which is just a string that specified the path to the rtld to use. I don't even know what PE/COFF or Mach-O do. If there isn't an OS, it doesn't make a lot of sense to be using /lib64/ld-linux-x86-64.so.2 as the rtld, for example. I suppose not doing anything at all is fine. Especially if it were combined with -C linker.

Copy link
Member

Choose a reason for hiding this comment

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

It sounds like things can go wrong very quickly, but I'm still not seeing what we're losing by saying "let the linker duke it out?"

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it's something annoying that we could avoid entirely on our end, with a nicer user experience.


"Support libraries" means libmorestack and compiler-rt. The crate author will
need to provide compiler-rt or a similar library themselves if they use a
feature requiring it, and libmorestack is just not required without split
stacks. The goal is to have 0 dependencies on the environment, at compile time
and runtime, reducing unnecessary surprises for the freestanding author and
allowing the use of only rustc to compile the crate, rather than manually
assembling and linking LLVM bitcode. Providing linker arguments manually is
probably unavoidable in these cases.
Copy link
Member

Choose a reason for hiding this comment

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

My previous discussion has now been folded away by github, so I'll reiterate my point here original link.

I don't understand the use case for not wanting to link to compiler-rt. Is there a concrete reason for why this is undesirable?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yet another thing you need to download and build, for features you're probably not using (floats, 64-bit math on 32-bit targets, emulating atomics with locks, fixed point arith, ...)

Copy link
Member

Choose a reason for hiding this comment

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

I'd like to talk concretely here. The compiler-rt library is tiny and very easy to build. It has one function per object, so the linker strips out everything you don't use.

Without a conrete reason as to why, I don't see why the compiler should stop linking to compiler-rt.

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree there are instances where you probably do want compiler-rt, but that those would be outweighed by the cases where you don't. (Some fun reading, though only slightly topical: http://yarchive.net/comp/linux/libgcc.html)

Copy link
Member Author

Choose a reason for hiding this comment

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

The concrete reason being it's just another dependency you need. I'm not going to argue that it's large or a runtime burden (because that would just be false). Should we instead add build system support for building and distributing it for *-unknown-unknown? I wouldn't be against that.

Choose a reason for hiding this comment

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

My experience with compiler-rt was a complete nightmare. So if it is possible to go without it - it's better to go without it.

Avoiding compiler-rt means providing all of the support functions expected by LLVM's code generation on your own. You can almost get away with using libgcc_s, but it's missing functions required by Rust's code generation.

  1. While working on proper backport I've "worked" with one of code owners. Worked in this case means I've pinged him every 2-3 days for about a month until full despair.

That's pretty much how the LLVM project works right now.

Copy link

Choose a reason for hiding this comment

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

I haven't run into any of these support functions being missing on arm-none-eabi (thumb2).

Choose a reason for hiding this comment

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

I haven't run into any of these support functions being missing on arm-none-eabi (thumb2).

The features provided by compiler-rt are well known and we don't need to rely on anecdotes to know that significant portions of the language are missing without it.

Choose a reason for hiding this comment

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

I haven't run into any of these support functions being missing on arm-none-eabi (thumb2).

What do you mean by "being missing"? I do link to libgcc.a for some of those on thumbv7m, as I don't use compiler-rt. They are definitely required in lots of cases.

Copy link

Choose a reason for hiding this comment

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

My apologies - the build system is linking against libgcc.a, so I must be getting them from there.


# Drawbacks

All function calls become unsafe in such a target, since guards against stack
overflow are not inserted by the compiler. The crate author must be careful
not to overflow the stack, or set up their own stack safety mechanism.

# Alternatives

We could allow disabling split stacks on a per-crate basis. Then calling
functions from that crate is then unsafe, and we would need to compensate for
this somehow. We would still need to add a way to not link to
libc/libm/copmiler-rt.

Choose a reason for hiding this comment

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

Spelling.