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

The thumb* target family (which have newlib libc) are not supported #375

Closed
SimonSapin opened this issue Sep 1, 2016 · 31 comments
Closed

Comments

@SimonSapin
Copy link
Contributor

I have embedded hardware without an OS. I build Rust code for it with --target thumbv7em-none-eabi and this thumbv7em-none-eabi.json file:

{
    "arch": "arm",
    "cpu": "cortex-m4",
    "data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
    "disable-redzone": true,
    "executables": true,
    "llvm-target": "thumbv7em-none-eabi",
    "morestack": false,
    "os": "none",
    "relocation-model": "static",
    "target-endian": "little",
    "target-pointer-width": "32",
    "no-compiler-rt": true,
    "pre-link-args": [
        "-mcpu=cortex-m4", "-mthumb",
        "-Tlayout.ld"
    ],
    "post-link-args": [
        "-lm", "-lgcc", "-lnosys"
    ]
}

If I add a Cargo dependency on the libc crate and build with this target, I get many build errors. The first and last are below. The entire thing is at https://gist.github.com/anonymous/780fe74b2ae2834fc9c738dbff60acc0

   Compiling libc v0.2.15
error[E0412]: type name `c_char` is undefined or not in scope
   --> /home/simon/tmp/cargo-home/registry/src/github.com-1ecc6299db9ec823/libc-0.2.15/src/lib.rs:153:35
    |
153 |     pub fn fopen(filename: *const c_char,
    |                                   ^^^^^^ undefined or not in scope
    |
    = help: no candidates by the name of `c_char` found in your project; maybe you misspelled the name or forgot to import an external crate?


// Removed: ~4000 more lines


error[E0204]: the trait `Copy` may not be implemented for this type
   --> /home/simon/tmp/cargo-home/registry/src/github.com-1ecc6299db9ec823/libc-0.2.15/src/macros.rs:44:9
    |
44  |         impl ::dox::Copy for $i {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ field `rlim_cur` does not implement `Copy`
    | 
   ::: /home/simon/tmp/cargo-home/registry/src/github.com-1ecc6299db9ec823/libc-0.2.15/src/lib.rs
    |
261 | cfg_if! {
    | - in this macro invocation

error: aborting due to 5 previous errors

error: Could not compile `libc`.
@SimonSapin
Copy link
Contributor Author

While it is possible to make a "bare metal" program with nothing but Rust and a linker, the arm-none-eabi-gcc toolchain does have a libc called newlib.

@alexcrichton
Copy link
Member

This is pretty standard for most new platforms, I think. I don't think liblibc will ever compile for an arbitrary new platform because it has to know what the actual definitions are. The theory is that the backing libc implementation will almost always have bindings, however. (although right now we don't have newlib bindings in tree)

@FenrirWolf
Copy link
Contributor

Any chance of newlib support being added at some point?

@posborne
Copy link
Contributor

Any chance of newlib support being added at some point?

The newlib API surface is going to be quite small compared to other libc implementations, but I'm guessing the answer would be yes so long as people are willing to contribute and it can be tested by CI. I have used newlib on embedded systems before and can see cases where having libc provide access would be useful.

For pure-rust embedded systems, you might want to check out rlibc which provides bare bones required functions: https://github.com/alexcrichton/rlibc

@kristate
Copy link
Contributor

Trying to clean-up issues: is this still an issue @SimonSapin ? (Thanks for your work on Servo!)

@SimonSapin
Copy link
Contributor Author

Yes.

The thumbv7m-none-eabi target is now supported by rustc, thought xargo is needed to build libcore. This issue can be reproduced by making a no_std crate, adding libc = {version = "*", default-features = false} to its dependencies, and running xargo build --target thumbv7m-none-eabi

   Compiling libc v0.2.37
error[E0412]: cannot find type `c_char` in this scope
   --> /home/simon/tmp/cargo-home/registry/src/github.com-1ecc6299db9ec823/libc-0.2.37/src/lib.rs:171:43
    |
171 |             pub fn fopen(filename: *const c_char,
    |                                           ^^^^^^ did you mean `c_schar`?

error[E0412]: cannot find type `c_char` in this scope
   --> /home/simon/tmp/cargo-home/registry/src/github.com-1ecc6299db9ec823/libc-0.2.37/src/lib.rs:172:39
    |
172 |                          mode: *const c_char) -> *mut FILE;
    |                                       ^^^^^^ did you mean `c_schar`?

[…]

error[E0412]: cannot find type `c_long` in this scope
   --> /home/simon/tmp/cargo-home/registry/src/github.com-1ecc6299db9ec823/libc-0.2.37/src/lib.rs:278:39
    |
278 |             pub fn labs(i: c_long) -> c_long;
    |                                       ^^^^^^ not found in this scope

error: aborting due to 76 previous errors

If you want more information on this error, try using "rustc --explain E0412"
error: Could not compile `libc`.

@SimonSapin SimonSapin changed the title Doesn’t build on a custom target The thumb* target family (which have newlib libc) are not supported Feb 28, 2018
@FenrirWolf
Copy link
Contributor

There are some newlib bindings in-tree for ARM targets now, which should get selected if you compile with the target-env set to newlib in some fashion (adding "env": "newlib" to a custom target spec would do the trick). But there would probably have to be some extra changes made to add thumb support and possibly to account for toolchain differences.

@SimonSapin
Copy link
Contributor Author

A first step would be defining c_char and other primitive type aliases for those targets. Even if there isn’t bindings for newlib functions yet, this would already help with FFI. (std::os::raw is not available in core.)

@gbip
Copy link

gbip commented Oct 14, 2018

Is there someone tackling this issue ? This is a blocking issue for an embedded project. If it is just defining type aliases I can take care of it 😄

@SimonSapin
Copy link
Contributor Author

Since this issue was opened, some thumb* targets have been added to be built-in in the compiler: https://forge.rust-lang.org/platform-support.html. I think it makes sense to make the libc crate compile out of the box for those targets, and provide at least the c_* type aliases. @gbip, wanna make a PR?

@gbip
Copy link

gbip commented Oct 15, 2018

Yes I will try to tackle this issue within the end of this week !
Can anyone give me a little insight on where should I look ?

@SimonSapin
Copy link
Contributor Author

@FenrirWolf In this repository, newlib seems to be only mentioned under src/unix/ which is under #[cfg(unix)] which presumably should not apply to microcontrollers without a kernel / OS.

@gbip Steps to reproduce, in this repository:

rustup target add thumbv7em-none-eabi
cargo build --target thumbv7em-none-eabi --no-default-features

You should see a bunch of errors like:

error[E0412]: cannot find type `c_char` in this scope
   --> /home/simon/tmp/cargo-home/registry/src/github.com-1ecc6299db9ec823/libc-0.2.43/src/lib.rs:172:43
    |
172 |             pub fn fopen(filename: *const c_char,
    |                                           ^^^^^^ did you mean `c_schar`?

They are all caused by c_char, c_long, c_ulong, or wchar_t not being defined. For other platforms, these type aliases are defined in platform-specific files that are included from this part at the bottom of src/lib.rs:

libc/src/lib.rs

Lines 298 to 320 in 41944d5

cfg_if! {
if #[cfg(windows)] {
mod windows;
pub use windows::*;
} else if #[cfg(target_os = "redox")] {
mod redox;
pub use redox::*;
} else if #[cfg(target_os = "cloudabi")] {
mod cloudabi;
pub use cloudabi::*;
} else if #[cfg(target_os = "fuchsia")] {
mod fuchsia;
pub use fuchsia::*;
} else if #[cfg(target_os = "switch")] {
mod switch;
pub use switch::*;
} else if #[cfg(unix)] {
mod unix;
pub use unix::*;
} else {
// Unknown target_family
}
}

https://doc.rust-lang.org/reference/attributes.html#conditional-compilation documents what’s available to cfg* macros. I think cfg(target_arch = "arm", target_os = "none") would be relevant here, but I’m not sure if there’s a single definition of all those aliases that would be correct for all such targets…

@SimonSapin
Copy link
Contributor Author

I’ve filed rust-embedded/wg#238 to ask for guidance from the Embedded WG.

@FenrirWolf
Copy link
Contributor

Yeah, it probably would make sense to move the newlib stuff out from under the unix folder.

@gbip
Copy link

gbip commented Oct 15, 2018

It seems like that all the newlib stuff isn't really related to bare metals programs.

@FenrirWolf
Copy link
Contributor

FenrirWolf commented Oct 15, 2018

newlib exists in a funny spot. its default configuration is aimed towards bare-metal systems without OS features, but it's also designed to have its functionality extended by end-users to support things like stdio and file io for systems where those make sense. the main place I've encountered newlib is in toolchains for game console homebrew, and those target everything from the Game Boy Advance (basically bare metal) to the Nintendo Switch (not bare metal at all).

@gbip
Copy link

gbip commented Oct 15, 2018

I think if we go with cfg(target_arch = "arm", target_os = "none") this should make FFI in the bare metal world possible, without closing the door for someone to implement more fonctionalities for other arm based plateforms.

Would you be ok if I create an arm module containing the none module with type aliases for bare metals arm plateforms.

If someone would like to add features for game consoles (or any arm based plateform) they would just have to create a new module next to the none one and they could start working.

@FenrirWolf
Copy link
Contributor

I'm completely fine with that. I'd only added the existing bindings under unix because it suited my own purposes at the time, but in retrospect that was a mistake. it makes much more sense to put the bindings in a more sensible location that serves a wider variety of needs.

@gbip
Copy link

gbip commented Oct 15, 2018

Maybe it is better suited to leave them there and to create a none module for different bare metals system ?

@FenrirWolf
Copy link
Contributor

FenrirWolf commented Oct 15, 2018

That might work. To clarify, my own case is running homebrew on the Nintendo 3DS, and while the system itself isn't actually a unix-alike, the newlib-based toolchain that I use in tandem with Rust code has extensions for things like file systems, stdout, network IO, and some other functionality that makes it so you can interact with the system with a very unix-like interface. And @ischeinkman has been doing the same thing with a similar toolchain for the Nintendo Switch.

So making a new set of bindings for target_os = "none" while leaving the existing ones in place might be a good solution, but if not then I can probably deal with adjusting for deeper breaking changes. The setup tends to break with every significant change in nightly rust anyway =P

@ischeinkman
Copy link
Contributor

Would it make sense to gate newlib extensions behind feature flags for os="none" targets and making the family="unix" case just be the enabling of all the gated features?

For example the stdio functions can be gated behind #[cfg(any(feature="newlib-stdio", unix))] so that a) no existing targets are broken and b) any embedded targets can mix and match the functionality they need without having to have a separate manually-crafted binding list for every target possible?

@gbip
Copy link

gbip commented Oct 16, 2018

I think this is a very good way to do it.
I will tackle this implementation before the end off the week if the maintainer are ok with it !

@gnzlbg
Copy link
Contributor

gnzlbg commented Oct 29, 2018

This might be a dumb question, but couldn't we just add a thumbv7m-newlib-eabi target that has ""newlib as a platform"" instead ? (or target_os = newlib or similar).

That might be something that's easier to support from libc in the sense that we wouldn't be making up stuff for thumbv7m-none-eabi targets, which technically, might not be using newlib but something else.

@gbip
Copy link

gbip commented Oct 29, 2018

I think you should take a look at #891 #753, the libc team doesn't plan on supporting bare-metals targets that doesn't have a libc.

@gnzlbg
Copy link
Contributor

gnzlbg commented Oct 29, 2018

My question was whether it is a dumb idea to just add a bare-metal target that has newlib as its libc (e.g. thumbv7m-newlib-eabi). That means, that instead of trying to polyfill parts of libc generically for none targets, which is controversial because those targets do not necessarily have a libc, we would just need to add FFI bindings for newlib for those targets, which should be fairly uncontroversial since those targets do have a libc (newlib). Does that make sense?

@FenrirWolf
Copy link
Contributor

Usually the libc variant corresponds to target_env rather than target_os, so maybe the triple would be thumbv7m-none-newlibeabi?

@alexcrichton
Copy link
Member

Ah yeah what @gnzlbg mentioned is what we've done historically, although we don't need to necessarily add a target to rustc. We can add support in the libc crate for target_env = "..." custom things, and then custom target specs could set that for example

@japaric
Copy link
Member

japaric commented Nov 4, 2018

Is there someone tackling this issue ? This is a blocking issue for an embedded project. If it is just defining type aliases I can take care of it

@gbip what's your use case? If you only need type aliases for the C types (e.g. for bindgen) you can use the cty crate.

libc for bare metal (target_os / target_env = none) targets doesn't make much sense to me. The libc crate is for programs that link to libc.{a,so} (libc appends -lc to the linker flags) and bare metal programs don't link to libc.a. If you are not linking to libc.a then none of the bindings in this crate (e.g. fopen) will work. This crate would should up as empty when compiled for target_{os,env} = none.

Supporting arm+newlib for custom targets like the GBA and the DS makes total sense but sounds like a separate issue.

@gbip
Copy link

gbip commented Nov 5, 2018

Thanks a lot, I was not aware that this crate existed !

@gnzlbg
Copy link
Contributor

gnzlbg commented Nov 22, 2018

@japaric

libc appends -lc to the linker flags

Where does this happen? The libc crate does not have a links = ... field in its Cargo.toml, and its build.rs does not append any link flags AFAICT.

If anything, rustc might append -lc to the linker flags depending on the target, but that happens independently of whether the libc crate is linked or not, right?

@gnzlbg
Copy link
Contributor

gnzlbg commented Nov 22, 2018

So I'm going to close this issue for two reasons:

  • since libc 0.2.44 libc is now empty for unsupported targets, so compiling it for a new target should always succeed

  • we already have a certain level of newlib support: https://github.com/rust-lang/libc/tree/master/src/unix/newlib - if this is not enough, please fill in a more focused issue to address this, this issue has derailed a bit.

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 a pull request may close this issue.

9 participants