Cosmopolitan Libc: build-once run-anywhere C library
justine.lol
justine.lol
1) How does this interact with a boot loader like GRUB? Or does it come with its own boot loader? But in this case, shouldn't this go in a specific location on the boot disk?
2) How does it handle io in bare metal mode? Specifically: sockets.
Basically, ape/ape.S is a traditional BIOS bootloader. It also embeds a GRUB stub, which basically just switches back to the real mode BIOS bootloader entrypoint. So it should work with or without GRUB. The boot sector loads the rest of your executable of disk and prints "actually portable executable" to your display. It then configures standard i/o to use serial uart, sets up the page tables, enters long mode, and calls _start(). That's all I've got working so far. I would love to have e1000 and virtio though!
To try it out, here's how you build the included PC BIOS system emulator and boot an x86-64 ape executable that prints a spinning deathstar:
m=tiny; make -j12 MODE=$m o/$m/tool/build/blinkenlights.com o/$m/tool/viz/deathstar.com &&
o/$m/tool/build/blinkenlights.com -rt o/$m/tool/viz/deathstar.com
Video screencast: https://justine.lol/cosmopolitan/deathstar.htmlSee also: https://justine.lol/blinkenlights/manual.html
Enjoy!
sudo systemctl mask proc-sys-fs-binfmt_misc.automount
...and this to disable it for just the current boot: sudo bash -c 'echo 0 > /proc/sys/fs/binfmt_misc/status' sudo sh -c "echo ':APE:M::MZqFpD::/bin/sh:' >/proc/sys/fs/binfmt_misc/register"
That command should allow Actually Portable Executables to co-exist with WINE, which assumes the MZ prefix, since APE binaries always use the longer MZqFpD prefix.I also tried adding the line to a new file /etc/binfmt.d/APE.conf, but it didn't seem to work (after a reboot); after renaming the file to zz_APE.conf, it did work after a reboot. systemd's binfmt.d registers files in alphabetical filename order, so I'm guessing it's important that /usr/lib/binfmt.d/wine.conf is registered before the APE handler, meaning its filename must be alphabetically after "wine.conf".
Interesting approach for a universal binary.
It's a little bit more nuanced than that, depending on the platform. You don't necessarily need to have a shebang. This behaviour is guaranteed in Linux, and traditional on BSD, but not POSIX:
> If the header of a file isn't recognized (the attempted execve(2) failed with the error ENOEXEC), these functions will execute the shell (/bin/sh) with the path of the file as its first argument. (If this attempt fails, no further searching is done.)
Which means that it _should_ work outside a shell on those systems.
POSIX has similar wording:
> In the cases where the other members of the exec family of functions would fail and set errno to [ENOEXEC], the execlp() and execvp() functions shall execute a command interpreter and the environment of the executed command shall be as if the process invoked the sh utility using execl() as follows:
execl(<shell path>, arg0, file, arg1, ..., (char *)0);
> where <shell path> is an unspecified pathname for the sh utility, file is the process image file, and for execvp(), where arg0, arg1, and so on correspond to the values passed to execvp() in argv[0], argv[1], and so on.https://pubs.opengroup.org/onlinepubs/9699919799/functions/e...
I think most modern C and C++ compilers know that memcpy is special and optimize accordingly. I believe some C++ compilers recognize when you are using memcpy just to do bit casting to different representations and avoid doing a memory copy at all.
FWIW my experience has been has clang produces a superior development experience for c++ code, but that gcc is better for c. Llvm does have a more modular architecture than gcc, but it's not really fair to say that it's more modern overall. Gcc has gotten several features ahead of llvm, like hot/cold LTO separation and parallel compilation (the former is now in llvm and the latter apparently is impractical due to architectural problems).
> The compilers are only good for short memcpy's. For the rest the asm hinders modern compiler optimizations
Also not sure what you mean by that. TFA isn't performing memcpys with inline assembly. They're using inline assembly to perform a call to the exact same ‘memcpy’ function you would have called anyway, but also to inform the compiler that memcpy clobbers fewer functions. So it's true that this will mainly improve performance for copies of small memory regions; but the performance of large copies will be unaffected.
Aww, I was hoping you would use the evil ICE_P - https://lkml.org/lkml/2018/3/20/805
I don't think the approach of making a call from inline asm is safe. In particular, it clobbers the redzone [1], which the compiler may have used and expect to be intact after the call.
In general, at least some compilers (e.g., gcc) assume there are no calls in an inline asm block.
---
Even then, I am not quite sure: redzone is just an example of one of the assumptions that might be violated when calls are made from an asm block, but at least some gcc devs are on record saying that calls from inline asm are not supported: the main problem is that leaf functions can be compiled differently than non-leaf, and a hidden call breaks this (i.e., a non-leaf function may appear as leaf to the compiler).
Note that one way to get the code size wins for the caller in a compiler-blessed way is to use an alternative calling convention, like Clang's preserve_most; this isn't as good as explicitly providing a clobber list, but allows a library to provide a stable interface.
While this is awesome, it can easily break (zsh already seems to do so) and, as the author states, it is not meant for GUI apps or programs relying deeply on OS APIs, which includes a lot of apps.
Additionaly, it does not solve an important problem. Doing a build for each supported OS is not that hard and easily automated. The actual problem is to be able to reuse code and this is solved for the most part.
As for GUIs, while Cosmpolitan isn't intended to write complex GUI apps (since I normally build web apps for that) it still can be used to build WIN32 GUIs. See https://justine.lol/apelife/index.html as an example of Conway's Game of Life built with Cosmopolitan, where it manages to embed a UNIX TUI and a WIN32 GUI in the same binary! Even if you turn the WIN32 off, it'll still run the TUI inside the Windows Command Prompt.
As for breakages, Cosmopolitan only depends on stable kernel ABIs with long term stability promises. The binaries you build will work on all Linux distros dating back to RHEL5, Windows versions dating back to Vista, etc. There's no reason to believe there will be any radical breaking changes to these ABIs. Why is it so stable? Alignment of self-interest. By using canonical kernel interfaces, we ensure platforms can't break us without first breaking themselves.
Unfortunately, on macOS a stable kernel ABI does not exist. macOS has had kernel ABI breakage before, even recently, and they explicitly warn against making your own system calls. On Windows things are more stable but that's only because Windows cares about backwards compatibility to the point of preserving the behavior of even the most badly written software.
Linux is the only one of the three that promises a stable kernel ABI. On macOS and Windows the system libc is the only safe way to talk to the kernel.
Not sure what you mean by ‘on Windows thing are more stable’; syscall numbers change all the time. See https://j00ru.vexillium.org/syscalls/nt/64/
Mac however is a different story. One of the reasons Apple came back is they adopted the UNIX interface. They literally copied the magic numbers right out of the UNIX codebase. They shouldn't have the right to stand on the shoulders of giants, pull an about face, and then declare it their own internal API. Apple actually broke every single Go application a few years ago by doing that, and I'm astounded that Google didn't do more to push back. We need to give Apple as much feedback as possible about keeping the SYSCALL interface stable. It's the right thing to do.
That doesn't mean it is going to stay like that forever, the UNIX network stack is already considered legacy for modern code.
https://developer.apple.com/videos/play/wwdc2017/707/
https://developer.apple.com/videos/play/wwdc2017/709/
Also I have some doubts about support in non-POSIX OSes like IBM i, z/OS, ClearPath MCP, or RTOS like INTEGRITY.
In practice it is pretty stable, and many use it. Many things cannot be changed without causing massive internal refactors and app compat issues. But it is regarded as internal.
Edit: Ah, maybe it's bypassing ntdll and syscalls changed?
CGI support. Maybe also an option to embed node or deno, or even tcc for directly executing C source as CGI scripts.
Embedded writable SQLite DBs. Appending would clearly be tricky but probably doable.
PWA support, giving you native notifications, launcher icons, and top-level windows sans browser chrome just like a native GUI app.
Option to morph into various app formats (apk, appx, ipa) and instant install on a connected phone or upload to app stores. (Wait, actually all of these formats are based on zip! So maybe this wouldn't even need that much morphing? Just some extra cruft in the zip file, some code signing BS, and maybe an embedded copy of adb.)
With all these features it might not be tiny anymore but it could make a pretty complete cross platform native app dev environment contained in a single file. Still way smaller than Electron.
So while there's probably some interesting historical reasons that explain why there's no "JVM for C++", Splunk is a good example of a modern technical / business model where conquering platform fragmentation was feasible.
> If it's this easy, why has no one done this before? The best answer I can tell is it requires an minor ABI change, where C preprocessor macros relating to system interfaces need to be symbolic. This is barely an issue, except in cases like switch(errno){case EINVAL:...}. https://justine.lol/ape.html
It also took an act of willpower. This is a novel approach to building software that says the conventional wisdom is wrong. When I first stumbled upon the idea, I couldn't believe it was even possible, until I actually did it. Now I'm glad that I did. Actually Portable Executable has been so useful for my work. I used to work on the TensorFlow team, so I've seen the worst c/c++ build toil imaginable, and looking back at those experiences I just can't believe how I managed to get by for so long without having portable build-once binaries without interpreters.
For example, Linux introduces some fixes for Y2038 problem during the past few years. I suppose the community of this C library should do something for those changes, right?
Not to diminish the achievement at all, but a significant limitation to be noted (and not a very surprising limitation).
I also wonder about things like the sizeof(long). On 64-bit linux, this is 8, but on windows, it's 4. Well, "on windows"... maybe this throws all the APIs that make such assumptions, uh, out the window, (puts on sunglasses).
As for sizeof(long) it's 64-bit with Cosmopolitan, which uses an LP64 data model. That hasn't impacted its ability to use the WIN32 API. See https://github.com/jart/cosmopolitan/tree/master/libc/nt where WIN32 API functions are declared using uint32_t, int64_t, etc. It works great. I compile my Windows apps on Linux and it feels wonderful to not have to touch MSVC. Cosmopolitan even takes care of the chore of translating your UTF-8 strings into UTF-16 when polyfilling functions like open() which delegates to CreateFile() on Windows.
> and web browsers do a great job.
Suppose one had a native linux game that uses POSIX, OpenGL and SDL. Is there any hope?
And SDL2 has already been ported to Emscripten/WASM after all, and OpenGL is really no trouble, since it's usually accessed through a mostly platform-agnostic C API wrapper already (see for example, https://github.com/Dav1dde/glad ).
And maybe you could use MinGW to support POSIX on Win32...
(Although adding that to the library would add considerable bloat, and cross-platform runtime dynamic linking or a "dynamic polyfill" might be a bit beyond the scope of this)
Or raw X11 protocol, of course (WSL made X11 servers on Win10 quite common, and . If that wouldn't be so horrible, it would be interesting how small one could get basic widgets starting from scratch.
Please don't do this for any application with a wide audience. Small, simple widgets are missing many important things such as accessibility and internationalization.
Re-inventing the UI wheel seems somewhat common within the Go and Rust communities (as is re-inventing everything else), this would just have raw X11 protocol messages via socket as the foundation layer, not Vulkan/GL/SDL. And there just enough to be useful, e.g. text, input, buttons, canvas…
For something more serious, I suggest re-inventing PostScript/NeWs as a service, of course.
Sure. I just worry that one person's clever hack will then become another person's indispensable tool, which they'll then impose on their colleagues, employees, or contractors, blocking a third person from doing their job. I know a blind person who temporarily lost his job because his employer's largest client required an application that didn't work with screen readers. So I just want to make sure developers are aware of accessibility, and in general, the risks of reinventing things that are necessarily complex.
That shortcoming of the MSVC runtime has been the bane of multiple projects I've done on Windows. Even without the portable executable trick, Cosmo might be useful as an alternative Windows CRT that can be used with GCC or clang. (Edit: Except that ARM64 Windows is now a thing, whereas Cosmo seems to assume the continued hegemony of x86-64.)
That doesn't even tell the whole story; there's a ton of path dependence, and the operand format is quite complicated and has tons of special cases.
return INSTRUCTION_SIZE;
I don’t know any other that has more than a few cases for immediate constants and jump addresses.Also, picking a real CPU is only worth it for running on that CPU, and that particular one. If they had picked x86 in 1996, they wouldn’t even have supported MMX.
In a platform-independent design, using n registers in the virtual CPU is only a good idea for n = 0 (i.e. a stack machine, as in the JVM, .NET CLR and Web Assembly) and n = ∞ (as in LLVM).
If you pick any other number your platform-independent code needs to handle both the case where the number of op real registers is smaller (so you need to have a register allocator) and the case where it is larger (there, you could just ignore the additional registers, but that means giving up a lot of performance, so you have to somehow figure out which data would have been in registers if the CPU had more of them).
Why write and optimize both of these complex problems, if you can pick a number of registers at either end of the scale, and spend all your resources on one of them?
And that’s even more true for x86-64, which doesn’t have a fully orthogonal instruction set, has I don’t know how many ways to do vector instructions, none of which allow you to clearly express how long the vectors you’re iterating over are (making it hard to optimally map them to other CPUs or newer vector instructions), has 80-bit floats, etc.
There’s a reason Google stopped work on https://en.wikipedia.org/wiki/Google_Native_Client in favor of Web Assembly.
For example, dynamic dispatch is handled by the JIT, it's not encoded into low-level Java bytecode instructions, so if the JIT can see there's only one class loaded that implements a certain interface, it can generate code that directly invokes methods of that implementing class, without going through a vtable. It can do this even across library boundaries. That wouldn't be possible (or at least, would be greatly complicated) if Java bytecode provided pre-baked machine code.
Modern JVMs also have pretty deep integration of the GC and the JIT, if I understand correctly. The Java bytecode format is high level so the JIT is quite free to implement memory-management however it likes. If the JVM took a truly low-level approach to its IR, we'd presumably be stuck with 90's GC technology.
I imagine it would also have implications for the way the JVM handles concurrency. It seems right that it defines its own memory model with its own safety guarantees, rather than defining its model as whatever x86 does.
It's telling that .Net took the same high-level approach that Java bytecode did.
That's before you get into whether you can aesthetically convince everyone that x86-64 is good, actually.
Still very interesting though, especially compared to the usual Show HN crap.
When software was deployed on floppy disks and there were 10k architectures going around, portability was critical to expand the market reach of any distributed software.
But in the modern age when the software runs "in the cloud" and there are dominant platforms out there, turns out that portability doesn't matter - nobody is changing platforms when they don't need to.
So this makes this achievement a pretty cool demo without any industry impact, since people don't distribute C binaries anymore.
The idea behind it and the technology developed is pretty cool :)
This is nothing new, IBM and Unisys mainframes have been doing it since the 60's.
The x86-64 instruction set is not a good portable IR. Some well-known problems:
* It's much more irregular to decode than other instruction sets/IRs
* There is a truly vast set of instructions, very many of which are almost never used. So in practice you need to define the subset of x86-64 you're using. E.g. does your subset contain AVX-512? AVX2? AVX? BMI? X87? MMX? XGETBV? etc etc etc. These decisions will impact the performance of your code on actual x86 CPUs as well as non-x86 CPUs.
* x86-64 assumes the TSO memory model which is horribly expensive to support on CPUs whose native model is weaker (e.g. ARM) (which is why Apple added a TSO mode for M1; no other ARM chips have this)
Honestly, declaring x86-64 your portable IR and then claiming that as a technological breakthrough sounds like a trick to me. I'd agree it's a breakthrough if you define your x86-64 subset, show your x86-to-ARM compiler, and show that it produces code competitive with your competitors (e.g. WASM compilers).
According to this blog post, it's not just "much more irregular to decode", instruction deciding is the Achilles Heel of x86 which allows M1 to be so fast by comparison.
https://debugger.medium.com/why-is-apples-m1-chip-so-fast-32...
This doesn't sound like a fair comparison. WASM isn't just providing portability, it's also providing security and runtime safety. Cosmopolitan doesn't, it presumably requires you to trust the codebase, and doesn't protect you from C's undefined behaviour. Of course, WASM also imposes a considerable performance penalty, I presume the Cosmopolitan approach easily outperforms WASM.
> This is probably how Java would have been designed, if Intel's instruction set hadn't been encumbered by patents at the time.
To mirror the comment by Someone, I sincerely doubt this. Java bytecode is a very high level stack-based IR, nothing like a CPU ISA. They could easily have made it resemble a CPU ISA if they'd wanted to. The lesser-known GNU Lightning JIT engine takes this approach, for instance.
The relevant Wikipedia article is named Sandbox (computer security).
> nor it means runtime safety nor protection from undefined behavior
It puts stronger constraints on what mischief undefined behaviour can lead to, and guarantees that various runtime errors are handled with traps. [0] This isn't the same as a hard guarantee that execution will terminate whenever undefined behaviour is invoked, but it's still a step up.
* https://00f.net/2018/11/25/webassembly-doesnt-make-unsafe-la...
* https://www.usenix.org/system/files/sec20-lehmann.pdf
(eg. Under WebAssembly's memory model, dereferencing NULL=0 won't lead to a segfault.)
Thanks that's a curious one. It won't always lead to a segfault with a conventional compiler either, undefined behaviour being what it is. [0][1] Fortunately GCC can be asked to add such checks at runtime, [2] this approach could also be taken with WebAssembly.
[0] https://devblogs.microsoft.com/oldnewthing/20140627-00/?p=63...
[1] https://blog.regehr.org/archives/213
[2] https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.h...
"We find that the mean slowdown of WebAssembly vs. native across SPEC benchmarks is 1.55× for Chrome and 1.45× for Firefox, with peak slowdowns of 2.5× in Chrome and 2.08× in Firefox."
Even Apple Rosetta takes roughly a 1.25x-1.50x hit on SPEC compared to native [1], and that’s with ahead-of-time compilation and no software sandboxing, whereas WebAssembly VMs typically use JIT compilation and do provide software sandboxing, both of which come with overhead. In contrast, inNative [2] is a WebAssembly compiler that has ahead-of-time compilation and no software sandboxing, and it can supposedly reach 95% of native performance (no idea if the benchmarks are cherry-picked though). This makes sense: WebAssembly is higher-level, closer to a compiler IR, whereas x86 assembly has already decided on low-level details (like register allocation and calling conventions) that are suboptimal to emulate on another architecture.
And other x86 emulators will do worse than Rosetta. For one thing, Rosetta can take advantage of the Total Store Ordering mode added to M1 chips for the specific purpose of emulating x86 faster. An emulator running on pretty much any other CPU has to emulate a stronger memory model in software on top of a weaker one, which comes with a massive unavoidable penalty for multithreaded workloads. Also, most x86 emulators are just not as efficient as Rosetta, adding an additional penalty.
[1] https://www.anandtech.com/show/16252/mac-mini-apple-m1-teste... [2] https://innative.dev/news/
Imagine I have a three byte buffer, n is 3 and I copy in “foo”. The result of the MIN is going to be three. And d[3] is the fourth byte of my three byte buffer.
https://github.com/freebsd/freebsd/blob/master/sys/libkern/s...
Cosmopolitan implements a simplified version of the Address Sanitizer (ASAN) runtime. See https://github.com/jart/cosmopolitan/blob/master/libc/log/as... I've been systematically working towards using it to vet the entire codebase. It's helped to address more than a few memory issues so far. However more work needs to be done polishing the ASAN code so it can be bootstrapped for the purposes of having memory safety in the lowest level libraries like LIBC_STR, due to the way that the Makefile requires acyclicity between libraries.
This has been less of an issue, because bugs are normally very evident in functions like strcpy(). The BSD version is an exception though, since it was only used to port programs from the OpenBSD codebase which were used sparingly. But in either case, the real focus here is having continual incremental improvements over time, and I'm very committed to addressing any and all concerns.
Cosmopolitan also has extensive unit tests. This is something that C libraries have traditional done somewhat sparingly, if at all, since C library authors conventionally rely on an ecosystem of ported software that very quickly breaks if everything isn't perfect. If you build the Cosmopolitan repository, you'll notice there's 194 independent test executables in the o/test folder. That's one of the great things about Actually Portable Executable. It makes binaries so tiny that we can have lots of tests, completely isolated from each other!
Also, this effectively makes the body of the function a part of its public ABI, which makes it morally equivalent to inlining; it requires users of the function to be recompiled each time the body (and in consequence the clobber set) is changed. But since this seems to be static-linking-only library, this seems not that much of a problem in this case.
Still, I’m expecting improvements to whole-program LTO and inter-procedural optimisations to render this hack obsolete.
https://sagargv.blogspot.com/2014/09/on-building-portable-li... and
https://blog.ksub.org/bytes/2016/07/23/ld.so-glibcs-dynanic-...
Watch out for the implications of licenses etc.
I don't want to have to explain what I'm doing from the beginning every time I have a complex question about how to make a function call from inline assembly. It has really turned me off from stackoverflow as a whole. Sorry for the rant.
I think your project is awesome, and really shows that we can improve the base upon which everything rests.
Is there an incentive from the real world to answer questions because so many beginner try it?
Apparently so - if you look at the “Customer Questions and Answers” section of Amazon product pages, there is a remarkably high proportion of answers like, “I don’t know, I haven’t opened it yet” and “I’m not sure, I bought it as a gift”.
If you bought product X and some months or years later “Sam” posts a question on Amazon that isn’t answered, Amazon will send you an email with a subject line that is a variation of “Sam has a question for you about Product X, can you help?” and the body would be something along the lines of “Dear foo, Sam wants to know if <insert question regarding Product X here>? Click here to respond to Sam and help her out!”
In that context, you can easily see why people reply with such otherwise useless “answers.” When someone personally asks you a question you don’t know the answer to, responding with “sorry, I bought it for my husband as a gift and I don’t know,” is not just perfectly valid, it’s also the polite thing to do. But that answer is posted to the website in the genera Q&A as of you had viewed the question and then instead of passing on a question you had no answer for, you chose to reply with a useless answer.
It was down voted to a negative score initially. People posted irrelevant/unhelpful comments that I had to hurriedly refute before the question was closed and deleted.
To be fair, I have tweaked the question since, so it probably makes more sense than it did when I initially asked it. But I feel that is something the community should help with.
If you are working in a language which has good documentation (say Python or Java) you are best off learning how to look up the right answers in the official manual quickly and let your competitors waste time with stack overflow.
Once you are #1 in the Google Economy there are many reasons to say "why try harder?" Questions might have better answers some place else but there is no one site that threatens the SO hegemony. Also, a site like SO has a proven compatibility with Google SEO -- if you made any big changes to a site like that there is a high risk that your traffic would drop catastrophically.
What I would do:
StackOverflow Q/A are all available by a "cc-by-sa" license, so it would be a good starting place for something better: an "antisocial" project curated by a small group of people. Take the top 0.1% of questions from SO and only the best answer and strip away the "socialjunk" (e.g. see "chartjunk") and you are starting to get there.
A system like that could justify itself it were your own personal knowledge base, but to make something generally useful I'd expect to curate 100 questions a day for 3 months or so: about 10,000 questions in all.
There are a number of methods that could speed up that curation a lot, not least of which is that you'll find some authors who consistently write great answers and others who consistently get upvoted with mediocre answers, etc. No amount of analytics will make up for zero manual curation work, you could be maybe 5x as productive once you've curated 10,000 questions and developed some automation.
I had the notion their membership was limited, like a country club or fraternity or something, to a fixed number. To join, someone else had to first leave. I can't now confirm this, so I probably misremembered. Regardless, I've always liked this idea.
Wiki claims a distinction between virtual communities and social medias. The Well, MetaFilter, Ravelry and others have membership fees. Some have verified identities. Those frictions would certainly deter most dysfunction, antisocial behaviors now plaguing social medias.
I think curated social media is mostly fruitless. Having done a lot of online moderation, I honestly don't know how u/dang and others pull it off.
Culture and civility is much easier with preventative measures, like verified identity.
Often I see some weird way to implement something in the .NET world, especially Core/.NET 5, search Microsoft docs and find an 5-10-min read article updated last Wednesday that explains in-depth (relatively to a SO post) three different newer way of doing it.
A big weakness of SO, as I see it is aging. Most upvotes and marked-as-answer is most likely also the oldest.
Maybe the page could warn for old answers or highlight newer answers that are gaining traction.
Maybe questions and answers should be encouraged to specify version of tools. Asking identical questions for V1 and V2 of a tool should not be met by "POSSIBLE DUPLICATE" warning or get downvoted to oblivion. Posts shouldn't need "EDIT 1 updated for V1", "EDIT 2 updated for V2", as this is OK with 2 versions but what about 10?
Maybe points (or some score system) should decay over time? Idk. But I think something needs to be done.
SO has become a mound of garbage people dig through to find some piece of garbage solution to their garbage problems
By not marking all memory as clobbered for a call to memcpy, couldn't the memcpy overwrite a local variable, in which case reads post memcpy could miss the updated value?
> The above two compiles used the same gcc flags
Why does the before have frame pointers and post does not?
Polyglot files are not a particularly new invention, but devising a reproducible process to generate those can be quite tricky, so most don’t bother unless there’s a special need for it. (For example, the GRUB4DOS fork of GRUB contains a ‘bootlace’ executable that is simultaneously executable as a DOS .COM and an ELF file.)
The libc itself contains a number of specially-crafted functions and header files that expose the functions’ clobbered register set as part of the functions’ public ABI, which allows the compiler to use that knowledge to better allocate registers and optimise more aggressively. The downside is that if the clobbered register set changes, it requires everything using the function to be recompiled.
int ftruncate(int fd, int64_t length) {
if (!IsWindows()) {
return ftruncate$sysv(fd, length);
} else {
return ftruncate$nt(fd, length);
}
}
You get the idea. The OS is detected at startup and then checked each time a function is invoked.You can however tune the build so the `IsWindows()` branch is dead code eliminated anyway:
make MODE=tiny CPPFLAGS=-DSUPPORT_VECTOR=0b11111011
Basically unsetting the Windows bit causes the Windows polyfills to not be linked in your binary. It's only useful for making binaries tinier. So rest assured, if you find your 16kb APE .com binary to be too bloated, you can always use SUPPORT_VECTOR to squeeze it down to the order of a 4kb ELF executable. See: https://github.com/jart/cosmopolitan/blob/1df136323be2ae806f... /usr/bin/ld: crt.o: unable to initialize decompress status for section .debug_aranges
This on a WSL2 Ubuntu 18.04 install with gcc 7.5.0. Any ideas what the problem could be?https://wiki.gentoo.org/wiki/Binutils_2.32_upgrade_notes/elf...
I have been unable to find a backported package for 2.32 or better as a deb or ppa and there's no ways I'm installing binutils from source in case I clobber my build system. Any ideas?
> you won't need to configure a CI system to build separate binaries for each operating system
If that's where the DevOps part comes from, CI predates DevOps. DevOps is just a general strategy for coordinating work that incorporates CI among other things. And you'd still use CI with this because CI is more about an SDLC than portable builds.
I like the idea of the project. Is there a list of apps that use it?
C:\Users\Foo>emulator.com -t life.com
"This app can't run on your PC"
Maybe I missed something?
Bob Barton is surely turning in his grave.
So there's nothing stopping multi-os viruses right now.
I can foresee Morris Worm v2 coming up sooner rather than later but this time it won't just targeting UNIX systems. It is the obvious killer application for WORA library that use C, whether we like it or not.
On the bright side, for a start this probably can solve the many redundant VC++ compiler versions installation in a single computer just before we get to the 2030 VC++ compiler versions :-)
Anyway kudos to the library's author, when the late Dennis Ritchie invented C for portable OS, he had probably never envision how far it can be made portable.