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
Contributor

Amanieu commented Nov 3, 2016

Rendered

# Drawbacks
[drawbacks]: #drawbacks
- Adds an additional crate to the standard library.

This comment has been minimized.

@sfackler

sfackler Nov 3, 2016

Member

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

This comment has been minimized.

@Amanieu

Amanieu Nov 3, 2016

Author Contributor

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;

This comment has been minimized.

@petrochenkov

petrochenkov Nov 3, 2016

Contributor

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

This comment has been minimized.

@Amanieu

Amanieu Nov 3, 2016

Author Contributor

I included these because libc exports these types as well.

This comment has been minimized.

@petrochenkov

petrochenkov Dec 20, 2016

Contributor

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

This comment has been minimized.

@zackw

zackw Jan 25, 2017

Contributor

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 label Nov 3, 2016

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Nov 3, 2016

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

This comment has been minimized.

Copy link
Contributor 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;

This comment has been minimized.

@nagisa

nagisa Nov 4, 2016

Contributor

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

@nagisa

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Contributor 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

This comment has been minimized.

Copy link
Member

alexcrichton commented Nov 4, 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).

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

This comment has been minimized.

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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Nov 11, 2016

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

This comment has been minimized.

Copy link
Contributor 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

This comment has been minimized.

Copy link
Member

alexcrichton commented Nov 18, 2016

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

This comment has been minimized.

Copy link
Contributor 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

This comment has been minimized.

Copy link
Member

steveklabnik commented Nov 18, 2016

@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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Member

alexcrichton commented Nov 18, 2016

@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

This comment has been minimized.

Copy link
Contributor

Ericson2314 commented Nov 18, 2016

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

@cuviper

This comment has been minimized.

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.

@jmesmon

This comment has been minimized.

Copy link

jmesmon 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

This comment has been minimized.

Copy link
Member

alexcrichton commented Nov 30, 2016

@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

This comment has been minimized.

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

This comment has been minimized.

Copy link

briansmith commented Dec 15, 2016

@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.

@Ericson2314

This comment has been minimized.

Copy link
Contributor

Ericson2314 commented Jan 5, 2017

I don't really follow this logic. I feel like the concept of a system ABI is fairly fundamental

To me the mixed ownership of the definitions of int, short, long etc between C, the cpu arch, and the OS, is nothing but a big legacy mess to avoid in the clean-slate systems I like to toy with in Rust.

I'm not convinced that the fact that they are isolated in another crate makes a successful static check any more likely -- the crate seems highly likely to appear in the dep-graph for any non-trivial system anyway.

Once can use [replace] in the workspace root to replace a disliked crate today with something that won't build. (I'd be tempted to do this for alloc with std-aware cargo for a hard real time system, too.) If it becomes idiomatic we could add a notion of a client-side yank/blacklist to make it more intuitive and not resolve blacklisted crates in build plans in the first place.

@aturon

This comment has been minimized.

Copy link
Member

aturon commented Jan 6, 2017

@jminer

The argument against adding C types to core to keep it pure doesn't seem very strong to me in this case

I agree with your comment, personally.


I'm not sure how to make progress on this RFC. In particular, there's a disconnect between the stated motivations, the mechanisms proposed, and the additional motivations being discussed on this thread. Partly because of this, the discussion is starting to go in circles.

I think we need to narrow our focus. To my mind, the most immediate pain point in the discussion that requires some kind of structural change to fix is the c_void problem (and as @jethrogb, the solution there would inform the other aspects of this RFC). So I'd propose we try to narrow the discussion down to just that point.

Would a proponent of defining c_void in libcore (or of defining it via an alias) be willing to write an RFC focused just on that problem? If we can reach consensus around such a design, I believe that would obviate the need for this RFC, and solve the more pressing problem at the same time.

@briansmith

This comment has been minimized.

Copy link

briansmith commented Jan 19, 2017

I tried to code up something for this, but IMO there's no good way to define a c_void type in Rust, for the reasons I mentioned in this very long internals thread: https://internals.rust-lang.org/t/recent-change-to-make-exhaustiveness-and-uninhabited-types-play-nicer-together/4602.

@jethrogb

This comment has been minimized.

Copy link
Contributor

jethrogb commented Jan 23, 2017

See also #1861

@Ericson2314

This comment has been minimized.

Copy link
Contributor

Ericson2314 commented Jan 25, 2017

In particular, if we go this route---#1861 (comment), then std and libc can just alias that. I feel a lot better about putting that in core---it has nothing C specific in its name, and practically the effect is the same.

@liigo

This comment has been minimized.

Copy link
Contributor

liigo commented Jan 30, 2017

These types should be in libcore. They're not really (or not only) C types, they are FFI/ABI types, which may also be used without libc.


update (two months later):

C types are complex. You can't simply define/declare them in several lines of code. For example, c_long, is very os/architecture dependent. I don't want to add many if(cfg!(...))s to libcore any more.

@aturon

This comment has been minimized.

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

This comment has been minimized.

Copy link

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

This comment has been minimized.

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

This comment has been minimized.

Copy link

rfcbot commented Apr 15, 2017

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

@rfcbot

This comment has been minimized.

Copy link

rfcbot commented Apr 25, 2017

The final comment period is now complete.

@alexcrichton

This comment has been minimized.

Copy link
Member

alexcrichton commented Apr 25, 2017

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

@jethrogb jethrogb referenced this pull request Mar 18, 2018

Open

libc dependencies #13

@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Jun 26, 2018

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

This comment has been minimized.

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...

@jethrogb jethrogb referenced this pull request Jul 19, 2018

Open

Tracking issue for RFC 1861: Extern types #43467

1 of 3 tasks complete
@SimonSapin

This comment has been minimized.

Copy link
Contributor

SimonSapin commented Aug 9, 2018

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

@jethrogb

This comment has been minimized.

Copy link
Contributor

jethrogb commented Aug 26, 2018

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

@elichai

This comment has been minimized.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.