Build once, run anywhere - cosmopolitan libc?

Hi team,

I came across Cosmopolitan libc recently and, while I've found some attempts to use it with Rust, I think it deserves much more focused discussion :slight_smile: Someone succeeded with Nim, apparently.

I've been building static binary with musl for some time. Achieved multi-platformity by compiling for windows and running on linux with wine... yack, but it worked. Now this cute honey badger is promising much cheaper and cleaner solution: build-once-run-anywhere.

I would very much appreciate a tutorial, get some insight into what works and what doesn't. Could we achieve that multi-arch support by embedding the emulator? Could this potentially replace cross-compiling altogether, or at least for some use cases?

main.rs (credit to snakehand on reddit):

#![feature(lang_items, start)]
#![no_std]

use core::panic::PanicInfo;

extern "C" {    
    fn printf(format: *const u8, args: u64);
}

#[start]
fn main(_argc: isize, _argv: *const *const u8) -> isize {
    unsafe {
        printf(&*"Hello World\r\n\0".as_bytes().get_unchecked(0), 0);
    }
    0
}

#[lang = "eh_personality"]
extern "C" fn eh_personality() {}

#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
    loop {}
}
$ rustc +nightly --emit obj src/main.rs -o target/main.o

$ gcc -g -Os -static -fno-pie -mno-red-zone -nostdlib -nostdinc \
    -o target/main.com target/main.o  \
    -Wl,--oformat=binary -Wl,--gc-sections \
    -Wl,-z,max-page-size=0x1000  \
    -Wl,-T,../cosmopolitan/o/ape/ape.lds \
    -include ../cosmopolitan/o/cosmopolitan.h ../cosmopolitan/o/libc/crt/crt.o \
     ../cosmopolitan/o/ape/ape.o ../cosmopolitan/o/cosmopolitan.a

This succeeded, but running the target/main.com gave me

Segmentation fault

The author jart tried to address this but stumbled over using nightly.

4 Likes

At a glance, if main is being directly called by the C runtime, I’d expect i32 rather than isize for argc and the return value. Whether that could cause a segfault with those compiler options and replacement C lib I’ve no idea, but it could be a stack frame size and return value address disagreement perhaps.

2 Likes

I did not get the idea following the links given.

The author seemed to suggest x86 was the be all and end all, final, instruction set architecture we will ever need. God forbid.

Mainly I did not see how a single executable was supposed to run on all architectures, like Java, without some interpreter/emulator built in.

And how is that supposed to be fast?

1 Like

It looks like an interesting hack to play with for people interested in learning about crt.o equivalents and executable formats etc. I don’t think it’s suitable for real-world software development and distribution though!

I know iOS will compile multiple architectures into a single "fat" binary, but that's not really what they're talking about. Their front page makes it sound like a magic bullet which will make all your cross-platform/cross-compiling issues go away.

2 Likes

Hi, to be precise x86_64. On one hand, it is a very widely used, well established architecture. Curiously, I hear that patents around x86_64 are expiring, though that may not apply to later extensions. On the other hand, I hear x86_64 is a terrible common ground, though it could be emulated, as demonstrated by cosmopolitan on ARM architecture.

Let's not get burned in a flame war :slight_smile: and explore the possibilities here. While critical thinking is awesome, do we have some creative thinking, too?

I imagine, for a start, compatibility on other architectures could be emulated, and later extended with proper fat binary approach. Consider, that the project is so young :slight_smile: though the general concept is pretty old.

I imagine that for some mainstream users and use cases, the advantage of reducing complexity of targetting multiple platforms regardless of having to target multiple architectures would be good enough. Think about cloud, k8s, ci/cd...

I imagine that Rust community could take this on board like musl and even help drive it to a production class solution.

Tried that, the compiler (the authority as far as I know :D) said:

expected isize, found i32

This is a pretty cool project but I don't think it's the kind of thing most users would want to run in production:

  • Reimplementing libc is fine on Linux where the kernel syscall interface is well documented and guaranteed to be stable but that isn't true on other OS's. On Windows for example, bypassing "libc" isn't supported and syscall numbers can change at any time including a routine Windows update.

  • I suspect this will pretty quickly break your diagnostic and troubleshooting tools. Not being able to attach a debugger and get useful information or even print a backtrace when things break is going to be very painful.

  • For non x86_64 systems, if you're willing to accept the overhead of emulation, you're probably better off just using some kind of JIT'd language to begin with.