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

Create a separate libc_types crate for basic C types #1783

Closed
wants to merge 4 commits into from

Conversation

Amanieu
Copy link
Member

@Amanieu Amanieu commented Nov 3, 2016

# Drawbacks
[drawbacks]: #drawbacks

- Adds an additional crate to the standard library.
Copy link
Member

Choose a reason for hiding this comment

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

Would this be the standard library or the libc on crates.io?

Copy link
Member Author

Choose a reason for hiding this comment

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

Both in a sense, since libc is a (hidden) dependency of the standard library. But you are right, from a user's point of view this is just another crate on crates.io.

pub type uint8_t;
pub type uint16_t;
pub type uint32_t;
pub type uint64_t;
Copy link
Contributor

Choose a reason for hiding this comment

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

Are fixed-width C types ever useful in Rust?
It's always more convenient to write native Rust uN instead of uintN_t.

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 included these because libc exports these types as well.

Copy link
Contributor

Choose a reason for hiding this comment

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

I expected this, but I thought may be there was some other reasons.

Copy link
Contributor

Choose a reason for hiding this comment

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

In the hypothetical future situation where Rust knows how to talk directly to a C++ library instead of going through C wrapper functions, it will be necessary to match the mangled names of the C++ functions, and that is likely to involve making a distinction between uNN and uintNN_t, because in C land the uintNN_t types are "just" typedefs for whichever of the unpredictably-sized primitive integer types is the right match, and the name mangling uses the primitives, so for instance the mangled name of

extern "C++" foo(x: libc::uint64_t);

is _Z3foom on x86_64-linux but _Z3fooy on i686-linux.

@steveklabnik steveklabnik added the T-libs-api Relevant to the library API team, which will review and decide on the RFC. label Nov 3, 2016
@alexcrichton
Copy link
Member

I'm not really sure I quite understand the motivation for specifically the crate split itself here. Presumably crates could just use the libc crate without actually using the functions, right? If we wanted more principled access then we could perhaps leverage scenarios to gate access to APIs in a more statically-checked fashion.

I guess put another way, I'm not particularly clear on what the downsides of using libc are today. Could you clarify in the RFC what these downsides are?

As a side note, as well the drawbacks and alternatives sections here are particularly bare, and the alternatives section doesn't seem to have a lot of thought put into it. Perhaps these sections could be fleshed out a bit more with some more information? For example "adds an additional crate to the standard library" to me is a pretty massive drawback as an incredibly large hammer. Why not crates.io? Why not part of libcore? Why not in libc itself? (for example...)

@Amanieu
Copy link
Member Author

Amanieu commented Nov 3, 2016

The main motivation is being able to use the C types without linking to libc. This allows them to be used in environments that cannot use libc (kernels) or don't have a libc implementation (bare metal).

As mentioned in the comment above, it seems that I didn't word the drawback correctly. Like libc this new crate would only be accessible by users from crates.io, but it would still be one of the crates distributed along with the standard library since the standard library depends on libc (although that crate would be hidden behind feature(libc)). In practice I expect this crate to live within the libc repository.

pub type c_longlong;
pub type c_ulonglong;
pub type intmax_t;
pub type uintmax_t;
Copy link
Member

Choose a reason for hiding this comment

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

In case intmax_t is some primitive type not supported by rustc, what happens (i.e. what’s the behaviour of current liblibc)?

@nagisa
Copy link
Member

nagisa commented Nov 4, 2016

I would really like this to happen and my exact usecase for such a split is the -sys crates which provide bindings to some C API, but don’t want to link to liblibc, which is quite a big crate itself. I would feel much better about introducing a dependency on a small crate with standard C types only.

Currently I prefer using std::os::raw, but that precludes making the crate no_std and still introduces a transitive dependency on liblibc.

@comex
Copy link

comex commented Nov 4, 2016

What happened to c_char? C makes it implementation defined whether it's signed or unsigned.

c_char is in the existing liblibc, but some other C types specified by the standard aren't:

@Amanieu
Copy link
Member Author

Amanieu commented Nov 4, 2016

@comex Ah, nice catch, I seem to have forgotten about c_char.

_Bool is currently layout-compatible with the Rust bool type, but whether we should guarantee this is the topic of a separate issue: rust-lang/rust#14608

As for the other types, they simply aren't currently supported by Rust.

@alexcrichton
Copy link
Member

The main motivation is being able to use the C types without linking to libc. This allows them to be used in environments that cannot use libc (kernels) or don't have a libc implementation (bare metal).

I don't fully agree with the motivation behind this point, depending on what's happening. If libc is available, there's no detriment linking against it. It'll get stripped if it's not used. If libc is not available, however, then you're on a whole separate target, in which case the libc crate itself can just give you a different API (because no library exists).

That is, in my mind libc doesn't link to the actual libc for any platform that doesn't have one, so this problem wouldn't exist.

Like libc this new crate would only be accessible by users from crates.io, but it would still be one of the crates distributed along with the standard library

These are just type definitions, why would it need to be included in the standard library? Why not ship a no_std crate on crates.io that has these types?

I would really like this to happen and my exact usecase for such a split is the -sys crates which provide bindings to some C API, but don’t want to link to liblibc, which is quite a big crate itself

Technically, this is incorrect. If you have a crate that binds a C API crate, then that native library requires libc, so that dependency needs to be expressed. If the liblibc crate is "too big", then that's a problem that needs to be solved.

@FenrirWolf
Copy link

FenrirWolf commented Nov 5, 2016

I think the motivation here is that the libc crate in its current form expects that your target has a full and complete version of glibc or musl available, and that is not always the case with embedded systems.

Let's say your target has a toolchain based on a minimal version of libc like newlib. If you try to link the crate solely for its type definitions, you will fail at compile-time if target_os = none (as there are missing typedefs for the none case). If you set target_os = linux, then libc will compile, but it will fail to link because the libc crate will automatically try to bring in librt and libutil, and there are no such libraries as part of newlib.

The case above is one that I'm personally familiar with, and perhaps that particular case could be addressed with explicit newlib support in the libc crate. But I wouldn't be surprised if there were other cases similar to this involving other obscure versions of libc.

These are just type definitions, why would it need to be included in the standard library?

I think what he means is that libstd depends on libc, which itself would hypothetically depend on libc_types

Why not ship a no_std crate on crates.io that has these types?

That's what he's proposing.

@joshtriplett
Copy link
Member

joshtriplett commented Nov 9, 2016

I'd like to see this as well, for building core-only programs with no libc that have FFI calls between Rust and C.

As a minor nit/bikeshed, can we call it something shorter than libc_types that doesn't have libc in it, since it doesn't have much to do with libc, just C? Perhaps just ctypes?

@SimonSapin
Copy link
Contributor

Is this different from std::os::raw? Perhaps by being available to #![no_std] crates? If that’s the only reason, what about moving it?

@Amanieu
Copy link
Member Author

Amanieu commented Nov 11, 2016

@SimonSapin Yes, this is basically a no_std version of std::os::raw. I wouldn't mind simply moving it to core, but the consensus on the internals thread seems to favor using a separate crate instead.

@alexcrichton
Copy link
Member

Hm so I may not quite be being clear here, so let me try again! The purpose of the libc crate is to provide the types and definitions of what the underlying platform exposes exactly. That is, it theoretically contains all C types and definitions for interoperating with the underlying system. This implies that a libc_types is not necessary because it's what the libc crate is supposed to do already.

Comments have been made about how libc today "requires too many libraries" or "pulls in too many symbols". This is not an inherent limitation to libc, but rather because libc is being lied to about its platform. The libc crates has been ported to a set of platforms, and it provides absolutely no guarantee of working on any other platform. If you've got a platform that doesn't have libc/librt/libutil then libc has not yet been ported to your platform. If such a platform masquerades as x86_64-unknown-linux-gnu then it's misleading to libc as the libc API is then specifically tailored to what that platform actually is.

The intent of the libc crate design was specifically to avoid crates like libc_types. If the underlying platform in fact has no C library, then the libc crate will expose that and not give you any function delcarations or library linkage, just type declarations. This has not been implemented in libc yet, but that's just because an effort hasn't been made just yet to do so.

Does that make sense? This is what I mean by the alternative above of just emptying libc of function declarations and just having types for these sorts of platforms. For example I could imagine an alternative to this RFC along the lines of:

If cfg(target_os = "none"), then the public API of the libc crate will only be the standard C type definitions.

That is, if cfg(target_os = "none"), then the libc crate looks exactly like the libc_types crate being proposed here.


@FenrirWolf

I think the motivation here is that the libc crate in its current form expects that your target has a full and complete version of glibc or musl available, and that is not always the case with embedded systems.

It does indeed! I explained this above, but to reiterate here this is not a limitation of the libc crate. The libc crate has only been ported to platforms with libraries like glibc/musl, it just hasn't been ported to any other platforms (but it certainly can be!)

Let's say your target has a toolchain based on a minimal version of libc like newlib. If you try to link the crate solely for its type definitions, you will fail at compile-time if target_os = none (as there are missing typedefs for the none case).

These problems are all indicative of the work has not been done to port libc. That work can certainly be done, and it can be done at any time in the libc crate. Attempting to masquerade as a different platform and then seeing failures to me isn't a reason to abandon the libc crate entirely.

Why not ship a no_std crate on crates.io that has these types?

That's what he's proposing.

To clarify, this crate is being proposed to ship with the distribution, not on crates.io. Shipping a crate on crates.io doesn't require an RFC.

@Amanieu
Copy link
Member Author

Amanieu commented Nov 18, 2016

@alexcrichton

I would argue that the target_os is a distinct concept from whether you want your application to link to libc. On Linux for example, there are several use cases where you need very fine grained control over your address space and which syscalls are used. In such cases you would still generate a binary targeting Linux, but you would not link to glibc and would instead issue syscalls directly (using the syscall crate).

The idea of libc_types on the other hand is to expose only the type definitions needed for a freestanding C environment. These types are needed to link with other freestanding code, or for the various structure types used in system calls. libc_types provides these types without needing to link to the system libc.

To clarify, this crate is being proposed to ship with the distribution, not on crates.io. Shipping a crate on crates.io doesn't require an RFC.

Not exactly. What I am proposing here is a crate on crates.io, however it is important that libc re-exports these types instead of using its own to avoid conflicting definitions of c_void. The libc_types crate will become part of the distribution only as a side effect of being a dependency of libc, and will be mostly invisible since users will use the crates.io version.

@steveklabnik
Copy link
Member

@alexcrichton so does this mean libc will take PRs for any platform at all? Like, if I wanted to "implement" libc for intermezzOS, with just the types and none of the functions, that'd be an acceptable PR? Or would it be expected that smaller platforms (for some definition of small) should use some sort of forked libc? That's going to reintroduce the conflict, though.

@parched
Copy link

parched commented Nov 18, 2016

@Amanieu I think @alexcrichton's proposal sounds fine if you replace cfg(target_os = "none") with cfg(target_env = "none")

@alexcrichton
Copy link
Member

@Amanieu

The idea of libc_types on the other hand is to expose only the type definitions needed for a freestanding C environment.

My point is that the purpose of libc is to be precisely what you want libc_types to be on the platforms you're thinking of. If you want to use libc in a freestanding environment, then it just needs to be ported to that environment (which involves probably only exposing types).

My idea of target_os was just an example, we could use basically anything (cargo features, target_env like @parched suggested, etc). The intention is that a "freestanding libc crate" is precisely the same thing as libc_types proposed here.


@steveklabnik

so does this mean libc will take PRs for any platform at all? Like, if I wanted to "implement" libc for intermezzOS, with just the types and none of the functions, that'd be an acceptable PR?

Indeed!

@Ericson2314
Copy link
Contributor

@Amanieu if you could blacklist the relevant hosted features/scenarios/whatever for libc, that would solve the problem, right?

@cuviper
Copy link
Member

cuviper commented Nov 30, 2016

My point is that the purpose of libc is to be precisely what you want libc_types to be on the platforms you're thinking of. If you want to use libc in a freestanding environment, then it just needs to be ported to that environment (which involves probably only exposing types).

What if you need libc to serve both sides? You may want to have just the types for your FooOS kernel, but still have a full interface of types and functions for FooOS userspace programs.

@codyps
Copy link

codyps commented Nov 30, 2016

In the case where the types are mixed into the libc crate, how should the crate author communicate that they only depend on the types, and not on the library functionality? Or should there not be any way to write that into a crate spec?

@alexcrichton
Copy link
Member

@cuviper I would assert that those are two separate targets, not one. In that sense libc would compile itself separately for both targets.

@jmesmon there shouldn't be any reason to not want functions from libc. If functions don't exist on a platform then libc was miscompiled (or not ported). If the functions do exist then there's no cost to compiling them in.

@briansmith
Copy link

briansmith commented Dec 15, 2016

  1. There's no reason, AFAICT, to prefix the names of the types with c_. In ring we define ring::c::{int, long, size_t} with no c_ prefix and it works fine. So, I suggest dropping the c_ prefix.

  2. The use of these types from the libc crate should be deprecated/discouraged in favor of using them from this new crate.

  3. In the ring project, we have tests that verify the size and alignment of the Rust alias and the C type correspond. See https://github.com/briansmith/ring/blob/master/src/c.rs and https://github.com/briansmith/ring/blob/5c4627a8499e3489a466e280bb606fbed6cc3e67/crypto/crypto.c#L80-L102. I suggest similar tests should be added. Having such tests has definitely prevented serious bugs in ring.

@briansmith
Copy link

@jmesmon there shouldn't be any reason to not want functions from libc. If functions don't exist on a platform then libc was miscompiled (or not ported). If the functions do exist then there's no cost to compiling them in.

I disagree. It is useful to document statically in a toolchain-enforced way that no C library functions are being used by a crate, when that is the case. When looking at just the dependencies of a libc-based crate, one cannot tell whether it depends on C library functions or not. This matters for some embedding use cases.

@aturon
Copy link
Member

aturon commented Mar 29, 2017

The libs team discussed this RFC again during triage yesterday. The current consensus is to offer a canonical way of producing an "unknown, opaque type" (a better c_void), possible along the lines of #1861, or possibly just as a single type defined in libcore (with newtyping done separately). These options have been discussed up-thread already.

Once we make such a move, we'll be in a position to (1) redefine c_void everywhere in terms of such a type, making all instances interchangeable, (2) deprecate std::os::raw, which would then be purely type aliases, and (3) introduce a ctypes crate on crates.io which provides canonical (and compatible) type aliases.

As such, I'm going to propose to close this RFC for the time being, and encourage further discussion on #1861. We can reopen the avenue proposed by this RFC if the opaque type direction doesn't pan out.

@rfcbot fcp close

@rfcbot
Copy link
Collaborator

rfcbot commented Mar 29, 2017

Team member @aturon has proposed to close this. The next step is review by the rest of the tagged teams:

No concerns currently listed.

Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

See this document for info about what commands tagged team members can give me.

@jethrogb
Copy link
Contributor

jethrogb commented Mar 29, 2017

Can we move #1861 into FCP as well then? There hasn't been any on-topic activity there in over a month.

@rfcbot rfcbot added the final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. label Apr 15, 2017
@rfcbot
Copy link
Collaborator

rfcbot commented Apr 15, 2017

🔔 This is now entering its final comment period, as per the review above. 🔔

@rfcbot
Copy link
Collaborator

rfcbot commented Apr 25, 2017

The final comment period is now complete.

@alexcrichton
Copy link
Member

Looks like nothing new came up during FCP, so closing.

@SimonSapin
Copy link
Contributor

Once we make such a move, we'll be in a position to (1) redefine c_void everywhere in terms of such a type

I’m late to the party, but c_void cannot be made an extern type because that would change it from Sized (with size 1 byte) to !Sized, which would be a breaking change.

@jethrogb
Copy link
Contributor

jethrogb commented Jun 26, 2018

@SimonSapin we already have different incompatible definitions of c_void: libc::c_void and std::os::raw::c_void. The plan is to introduce a new canonical c_void for all users, deprecate std::os::raw and point libc::c_void at that new type. We should be able to use the “compatibility trick” to update libc without too many issues.

Edit: Although that last part might not be possible because of what you mentioned. Which means we'd need yet another breaking libc update? That probably won't fly...

@SimonSapin
Copy link
Contributor

New RFC to propose moving c_void (only, for now) to libcore: #2521

@jethrogb
Copy link
Contributor

jethrogb commented Aug 26, 2018

For people still following this thread: #2521 is now in FCP.

@elichai
Copy link

elichai commented Feb 18, 2019

I know this is pretty old and was argued to death.
But there is a problem, and that's C FFI bindings that compiles to wasm32 with no-std. this means no-std and no libc right now.

@briansmith
Copy link

But there is a problem, and that's C FFI bindings that compiles to wasm32 with no-std. this means no-std and no libc right now.

I agree. People are now asking me to add support to my crate for platforms that libc doesn't support, and for which libc refuses to add support. I think this RFC should be reopened since it solves exactly the problem that needs to be solved here for these embedded platforms and wasm32-unknown-unknown.

@lygstate
Copy link

Still need as without ctype, it's create a gap between std and other no_std library.
As ctype is a de facto standard for FFI, creating lib_ctypes between core and std is necessary.

@Amanieu
Copy link
Member Author

Amanieu commented Nov 15, 2021

Now that we have core::ffi we should just add the C types there.

@lygstate
Copy link

Now that we have core::ffi we should just add the C types there.

Do I need create new PR for rfcs or just create a PR is enough?

@Amanieu
Copy link
Member Author

Amanieu commented Nov 15, 2021

A PR is enough.

@SimonSapin
Copy link
Contributor

Now that we have core::ffi we should just add the C types there.

I’m ok with that but I thought the reason this hadn’t happened a long time ago was desire to keep core OS-independent, but the definitions of C integer types are OS-dependent.

Also, assuming all of core::ffi is reexported at std::ffi, would we have both std::ffi::c_int and std::os::raw::c_int defining the same thing?

@lygstate
Copy link

Now that we have core::ffi we should just add the C types there.

I’m ok with that but I thought the reason this hadn’t happened a long time ago was desire to keep core OS-independent, but the definitions of C integer types are OS-dependent.

Also, assuming all of core::ffi is reexported at std::ffi, would we have both std::ffi::c_int and std::os::raw::c_int defining the same thing?

yeap,its os dependes, but its only about ABI, not about runtime, there is no function linkage, so I think thats not an issue, of cuase, std.os.rae should re export core.ffi

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
final-comment-period Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised. T-libs-api Relevant to the library API team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.