30 ms·
Writing C software without the standard library
- capnfantasic 10y agoFantastic until you need to malloc. You're reimplementing libc, but at least you know what's going on at every level.
- DSMan195276 10y ago`malloc`'s not really that bad. There's a few different approaches you can take, but none of them are terribly complicated since the two basic memory allocation interfaces, `sbrk` and `mmap`, are fairly simple in terms of usage for generic allocations. But getting it all working and bug free still takes time. Same with stuff like `printf` and `scanf` (Though I'd actually argue those are harder to write then `malloc` if you're looking to be feature complete. `printf` has billions of features and I'm pretty sure `scanf` requires some extra black-magic internally). There's no doubt that this is a fun project though - if you or someone-else enjoys this type of stuff, you should definitely try your hand at writing a simple Unix kernel or similar, you'd probably enjoy it. On that note though, the writers aversion to inline assembly is unfortunate. It's a necessary evil for this type of programming. The syntax is ugly, but it's not really that hard to get used too (Especially since the large majority of inline assembly is just a few lines long, or even just one line long). In particular, the syscall wrappers can be done in a one-line piece of inline assembly, and then you can avoid the function-call overhead for the syscall by placing the inline assembly in a `static inline` function in your headers (Or a macro if you prefer), as well as avoid the extra .S file (Which IMO is the better part - it's always easier when you don't have to mix different languages like that). I would also add that, while I used to share the aversion for AT&T asm syntax the author does, virtually all of the assembly code out there related to linux is written in AT&T, so it's worth it to get used to it and at least be able to read it. On that note, you can use the Intel syntax in inline assembly though, if you prefer, so even if you hate AT&T with a passion you can still write inline assembly ;)
- std_throwaway 10y agoUnless you also need to free, it's pretty simple.
- capnfantasic 10y agoOf course. When I wrote that comment I asked myself should I have written "malloc" or "malloc/free" - surely one implies the other.
- dom0 10y agoOh well, with 16 GB RAM even in laptops, who needs free anymore? Just restart the program. It's simpler anyway.
- makapuf 10y agoironic for a minimalist/anti bloat pamphlet to start with "with 16GB ..."
- marcosdumay 10y agoNot surely. Many programs are written in a way that allocates all the needed heap space at startup, and just reuse it forever. And those are overrepresented on the minimal-system kinds of environment.
- vidarh 10y agoFor short-lived processes that do lots of allocations and where you can rely on the OS to release resources, just leaving out the deallocations is often faster. Of course, you need to be careful as if you write code like that in a language without garbage collection, it's inherently not reuseable - retrofitting deallocation is often really painful because it gets easy to adopt patterns that make object ownership etc. unclear when you don't have to ensure it's easy to deallocate in the right order.
- shultays 10y agoAlso easy if your free/malloc is just a wrapper around munmap/mmap.
- pg314 10y agoYou can get surprisingly far without using libc's malloc/free. E.g. TeX, the typesetting system by Knuth, implements its own dynamic memory handling. It has a large static array of bytes, and allocates from that when needed.
- majewsky 10y agoThat's an arena allocator: https://en.wikipedia.org/wiki/Region-based_memory_management https://en.wikipedia.org/wiki/Region-based_memory_management Arenas are really nice if you're allocating a lot of objects of the same size, whereas malloc() must be prepared to handle a lot of different memory usage patterns.
- pg314 10y agoI don't think it is. Differently sized objects can be allocated and released individually. Have a look at part 9 of [1]. In an arena based allocator you typically deallocate all the objects in an arena at once. TeX basically uses a special purpose implementation of malloc/free, with a static array as backing instead of memory requested from the OS with mmap(2) or sbrk(2). The main reason is portability (the original version was released in 1978 using WEB/Pascal). [1] http://brokestream.com/tex.pdf http://brokestream.com/tex.pdf
- nuclx 10y agoFreeRTOS also provides a few malloc implementations backed by static arrays (not dependent on sbrk), which can be useful for running malloc-based test code on embedded platforms without native malloc: http://www.freertos.org/a00111.html http://www.freertos.org/a00111.html
- vidarh 10y agoWhile one of the benefits of an arena allocator is to be able to deallocate everything at once, it's not that unusual to have an arena allocator that you can deallocate from "early" if needed.
- pjc50 10y agomalloc() isn't particularly hard; K&R provides a working implementation using a freelist and sbrk in about a page of code. It's printf() that's the horrendous feature-crammed nightmare.
- flukus 10y ago> Executables are incredibly small (the http mirror server for my gopherspace is powered by a 10kb executable). Is this ever an real issue, even on any embedded system in the last 20 years?
- shakna 10y agoAVR atmega based devices generally have about 2kb SRAM, and 32kb flash. Maybe 2kb EEPROM.
- m_eiman 10y agoThis is true, but the stdlib provided by the compilers aimed at these chips is usually very bare bones and size-optimized to begin with. So it's likely hard to save much space by reimplementing subsets of it.
- shakna 10y agoTrue, but you often end up avoiding parts of the stdlib like malloc anyway, because they tend to be heavy handed on the board. (I've never needed to remove stdlib yet).
- targ2002 10y agoI have seen colleagues have to do very similar things on embedded platforms, when there is a large pressure on price, a smaller amount of flash on an MCU will make the MCU cheaper.
- huhtenberg 10y agoIt's not a real issue, no. It's a statement of one's professional competency.
- capnfantasic 10y ago> Is this ever an real issue, even on any embedded system in the last 20 years? Ask Cisco when they cut the Linksys routers' RAM in half a few years ago. Every byte counts. Component cost savings add up when you make a few million of them.
- vxNsr 10y agoHe claims that your code will be easy to port but then goes straight to Linux system calls. Still I like the idea. This is something that should be covered in a CS 102 type course. I know way to many cs guys who have no idea how to debug, let alone how their is being implemented.
- Mithaldu 10y ago> He claims that your code will be easy to port but then goes straight to Linux system calls. Many linux developers believe "code is portable" means "can run on different linux distros". Edit: To be fair, he said architectures, and i think he meant CPU architectures, i.e. AMD64 and i386.
- pdw 10y agoBut still, Linux syscalls are different on each architecture.
- justincormack 10y agoIndeed, stupidly different. Different syscall numbers, different argument orders, different values for constants, different struct layouts, ... Some architectures, like MIPS, are particularly bad, with nods to Irix compatibility thrown in. The BSDs are all exactly the same for every architecture, sane.
- qb45 10y agoDoes that mean you can run Irix binaries on MIPS Linux?
- drewg123 10y agoThis dates back to how Linus did the first non-x86 port of Linux to the DEC Alpha. Rather than copying the x86 linux syscall conventions, he used the DEC OSF/1 syscall conventions to ease bootstrapping. This is a perfectly sane approach. But he probably should have reverted to a native syscall numbering rather than leaving this bootstrapping hack in place.
- gibsjose 10y agoWhile this seems mainly useful as an academic exercise, the `printf "#include <unistd.h>" | gcc -E - | grep size_t` bit to easily grep in header files was worth the read.
- 112233 10y agoindeed. under bash, it can be shortened to cpp <<< "#include <stdio.h>"|grep size_t which is super convenient
- partycoder 10y agoThis is required if you do systems programming (e.g: kernel development).
- lokedhs 10y agoOr demo coding on old hardware. I sometimes write demos for the Atari ST (68000-based home computer launched in 1985), and the modern way of doing that is to develop on a modern computer, and cross-compile to a native ST executable. The main loops are all assembler, but the support code is in C, but the C code is used as a more expressive assembler, and linking with libc requires way too much memory. All this means that I don't even have things like memcpy() available. In a way it's a quite liberating way to program, since you are in full control of the hardware. I guess yesterday's computers is today's embedded hardware.
- DaiPlusPlus 10y agoI'm curious what the use case of memcpy is in highly-optimised software. Are there any scenarios where copying bytes is better than using a char*+length tuple?
- unwind 10y agoThere can be hardware-driven requirements that force you to simply have data in a particular place in memory, and if you want that data to hang around you might need to manually move it somewhere else. There's also the case where your API accepts a pointer and a size, but you don't want to have lingering pointers into the caller's memory, so you have to copy the data over to the "inside" of the API. This kind of design is perhaps less common in demo software, but certainly plausible in embedded products which at least try to be somewhat optimized.
- lokedhs 10y agoThat is exactly it. For example, on the Atari ST you display graphics by copying the bitmaps to the screen address. Much of the C code is used during precomputation of data before the actual time-critical code is run. This involves copying lots of data in order to set it up so that as little computation as possible is performed in the actual time-critical parts.
- nwmcsween 10y agoThe comment section where gcc puts in ident info can be omitted with -fno-ident and syscall(2) is usually a very thin wrapper[0]. If you follow the musl syscall(2) it simply maps errors to errno[1] and uses the fancy count-args-in-macro[2] to call off the respective $arch/syscall_arch.h[3] syscall$n numbered functions. [0] https://git.musl-libc.org/cgit/musl/tree/src/misc/syscall.c https://git.musl-libc.org/cgit/musl/tree/src/misc/syscall.c [1] https://git.musl-libc.org/cgit/musl/tree/src/internal/syscall_ret.c https://git.musl-libc.org/cgit/musl/tree/src/internal/syscal... [2] https://git.musl-libc.org/cgit/musl/tree/src/internal/syscall.h https://git.musl-libc.org/cgit/musl/tree/src/internal/syscal... [3] https://git.musl-libc.org/cgit/musl/tree/arch/x86_64/syscall_arch.h https://git.musl-libc.org/cgit/musl/tree/arch/x86_64/syscall...
- justincormack 10y agoHighly recommend reading the Musl source code if you want to find out how things work. Don't bother trying to look at Glibc.
- shakna 10y agoCannot agree more. glibc's source feels like archeology: there's so much history and remnants of bygone eras. musl feels like a structured, well-engineered and specified piece of architecture. This isn't a knocj against glibc. But it was grown, not built. musl and the team's great documentation have been incredibly handy when I've been building tightly constrained applications.
- Sean1708 10y agoWhy does syscall use varags then assume that there will be 7 arguments?
- dvfjsdhgfv 10y agoThe guy is definitely a fan of old-school minimalism: http://weeb.ddns.net/0/articles/modern_software_is_at_its_worst.txt http://weeb.ddns.net/0/articles/modern_software_is_at_its_wo... I have to say I miss the old days of Gopher, too. It was so much easier to focus on the content back then.
- badsectoracula 10y agoI find myself agreeing with him 100%. Not just on Gopher (although i did write a Gopher client some time ago - http://runtimeterror.com/tools/gopher/ http://runtimeterror.com/tools/gopher/) but on the entire rant about wasting resources, UIs that waste screen real estate and become unusable in smaller resolutions, fonts that only look good with anti-aliasing and have weird misplaced pixels with antialiased disabled (which i also do), websites that make reading things harder and waste unnecessary resources on bling and fluff with all those javascript frameworks slowing them down, pagination often being replaced with "endless scrolling" which makes it hard not only to skip large bits of content but also hard to see how much content is there in the first place. And of course the newest worst trend of all - using an entire web browser as a UI framework for a text editor (i mean honestly, how people decided that HTML and CSS are the best technologies to use as the foundation for user interfaces?). The only bit i'd disagree would be with games, at least on AAA games since i have some experience there and -at least at the engine level- there is still a lot of low level wizardry being done there.
- dewiz 10y ago> I've even seen functionality regression for the sake of modern and > "responsive" design in many cases. > > For example, you used to be able to browse youtube favorites in > pages. Whoops, not anymore! Now you need to scroll down and > painfully wait for the site to make its stupid animation and load > the next page. > What's worse is, you can't just skip through pages. You have to > load EVERYTHING and it all stays loaded, so after 10-20 pages the > browser starts to lag and hog cpu just to scroll. I see this issue more and more, scrolling is broken in so many sites, even popular ones like FB. Plus, often search doesn't look in the "scrollable" content, i.e. you need to load (=scroll) all these "pages" and rely on the browser search function. Many small things like this, e.g. snippets of code that don't fit the DIV box and you need to scroll horizontally. And news pages lagging on a i7 w/ 16Gb or RAM. It's all so sad.
- dispose13432 10y ago> xor rbp,rbp /* xoring a value with itself = 0 */ Is this faster than a (const) mov ?
- capnfantasic 10y agoTraditionally yes. On the latest CPUs - who knows.
- SonOfLilit 10y agoProbably yes, because Intel knows this is the code every compiler outputs for zeroing a register. Also, the reason it is "faster" is that the encoding is 1 byte, vs. 9 bytes (in 64 bit) for "mov rbp, 0" - roughly, 1 for "mov rbp,", 8 more for a 64 bit "0".
- bonzini 10y agoTechnically you could get by with 5 bytes for "mov ebp, 0". Another reason why it was faster was that the processor recognized it and avoided partial flags stalls after an "inc". But in 64-bit code you rarely have "inc" at all, so it matters less. On the other hand, a few years ago XOR had a false dependency on the register you're clearing; I'm not sure it is still that way on more recent processors.
- SonOfLilit 10y agoI tip my hat to you, your analysis is far more interesting than mine.
- bonzini 10y agoWrong too, it's partial register stalls not partial flags stalls.
- vram22 10y agoApart from the replies to your question here, there was another interesting sub-thread on HN on this topic recently: "Also, I like how returning 0 is "xor eax, eax"." https://news.ycombinator.com/item?id=13052503 https://news.ycombinator.com/item?id=13052503 That led to multiple replies on it.
- kriro 10y agoSome of the reasons that he mentions for avoiding the standard library could also be mitigated by using another library like dietlibc (I played around with it back in the day, last release seems to be from 2013): https://www.fefe.de/dietlibc/ https://www.fefe.de/dietlibc/
- DaiPlusPlus 10y agoYour first paragraph makes me wish this site supported Markdown.
- BinaryIdiot 10y agoYeah off topic but if HN supported markdown (and GitHub's flavor of markdown so we could take different types of syntax with their language) would be amazing. There would be a ton more coding examples and discussion, in my opinion, if this were to happen. How do we summon Dang? :)
- posterboy 10y agomy netiquette says, more than about three lines of code should go in a pastebin anyway
- lsaferite 10y agoThen in a few years when someone is reading old posts the links to old services are broken or the service is gone. If the code is central to the comment, why would you put it some place other than the comment? If it's not small enough you can collapse it inline and if it's so large that you don't want it with the comment then perhaps you should be rethinking what you are posting. tldr; practicality and longevity should trump netiquette.
- BinaryIdiot 10y agoI agree and this was my original thought. For example Reddit, for most of its life, relied on imgurl for hosting images. Before the single place images constantly broke after services died. With imgurl being third party even it has led to broken images at times (though with significantly less frequency). Now reddit is doing their own. Hacker News is very technical and code heavy. Seems to make sense to me that some may want to communicate / discuss code itself. I could even see it opening up more conversations like "this is my implementation of X; thoughts?" or "do this in any other language" challenges.
- 10y ago
- thewavelength 10y agoWhat is necessary to do this with C++? Is there a tutorial available on the web?
- posterboy 10y agobehold the demoscene https://in4k.github.io/wiki/c-cpp https://in4k.github.io/wiki/c-cpp focused on windows, because grafix. also, linux guys are more likely to use C anyway. I only remember a comment about avoiding exceptions, which I might have read first in the context of micro controllers. https://www.google.de/search?q=site%3Apouet.net+c%2B%2B+exception https://www.google.de/search?q=site%3Apouet.net+c%2B%2B+exce... https://www.google.de/search?q=site%3Apouet.net+c%2B%2B+stdlib+or+standard+library https://www.google.de/search?q=site%3Apouet.net+c%2B%2B+stdl...
- pawadu 10y agoI think this is unnecessary when you got <stdint.h>: typedef unsigned long int u64; typedef unsigned int u32; ... if you define your own types like this you may need to revise them when you switch architecture or even compiler. Now you could argue that this is part of the standard library, but I actually see it as a part of the standard C language.
- DSMan195276 10y agoYou're getting down-voted (Which is unnecessary IMO) but you're really not wrong. `gcc` itself provides `stdint.h`, it's not actually part of libc - or rather, you can use it without actually having a libc in place. Generally this is a good move, because you can always write a `stdint.h` replacement on arch's that don't have one, but on ones that do you're guaranteed to get the types correct.
- sincerel 10y agoI use stdint.h too, but I'm honestly curious if there is any common platform around today where one of the following asserts fails: int main() { assert(sizeof(signed char) == 1); assert(sizeof(short) == 2); assert(sizeof(int) == 4); assert(sizeof(long long) == 8); return 0; } I'm not interested in the language lawyering, because yes I know the standard provides more freedom to compilers. I just think those definitions are very universal for any real computer that would otherwise run my software. Please don't bring up Windows 3.1, that's about as relevant to most of us a PDP-11. And for what it's worth, using typedefs based on the above provides more readable printf strings. This is hideous: int main() { int64_t portable = 123; printf("Ugly: %" PRId64 "\n", portable); return 0; } Where this is acceptable: int main() { long long palatable = 123; printf("Better: %lld\n", palatable); return 0; }
- jblow 10y agoI think int is 8 bytes on the PS4. (I just got bitten by this...)
- SFJulie 10y agoMyth busted : printf("Hello world") is simple and is a relevant C program for a beginning. The "hello world" example is just the first step to annihilate your capacity of understanding how thinks works by relying on institutional black magic, that maybe wrong (see all the scanf bugs that have been living in C code for so long and all bugs coming from respecting the old's man wisdom)
- rikkus 10y agoIt's interesting to read the sources[1] of lots of djb's[2] code, as he often works around problems with (or perhaps dislikes the style of) standard libraries by re-implementing parts. [1] https://github.com/abh/djbdns/blob/master/str_len.c https://github.com/abh/djbdns/blob/master/str_len.c [2] http://cr.yp.to/djb.html http://cr.yp.to/djb.html
- csl 10y agoOn a side note, isn't the choice of exactly four unrolls very architecture specific? As in, it works, but may be sub-optimal for your specific machine. I've done the exact same thing myself, and IIRC its performance varied a lot between which ISA it was compiled for. This is almost what Duff's device solves, except then you need to know the length beforehand.
- rikkus 10y agoAbsolutely. It's a (possible) optimisation that is either based on evidence (seems likely, because DJB) or hope. Actual behaviour is impossible to predict on untested platforms. My assumption is that DJB tested this locally and found enough of a speedup that it was worth it, considering the very low added complexity and risk of major degradation / defects on untested platforms.
- 00k 10y agoAn essential function of stardard library is to wrapper the syscalls. Besides that, you can make a live without the library. But why would you do that?
- taocipian 10y agothe C standard library is not perfect but good enough
- userbinator 10y agoA value in the range between -4095 and -1 indicates an error, it is -errno. The syscall/errno stuff has always seemed unusual, inelegant, and inefficient --- instead of just returning a negative error code directly, the function returns the vague "an error has occurred" -1, and you have to then check errno separately after that. It only adds insult to injury when you realise that the kernel itself isn't doing it, but the syscall wrappers. And thanks to POSIX standardising this mechanism, the alternative will likely never get much adoption; of course, if you write your own syscall wrappers like this article, then you can skip that bloat. For now this guide is linux-only, but I will be writing a windows version when I feel like firing up a virtual machine. Unfortunately the Windows syscalls are not officially documented and even less stable than on Linux, changing even between service packs. http://j00ru.vexillium.org/ntapi/ http://j00ru.vexillium.org/ntapi/ http://j00ru.vexillium.org/ntapi_64/ http://j00ru.vexillium.org/ntapi_64/ At least on Linux the first few (i.e. the oldest, most common and useful) syscalls have not really moved around over the years: https://filippo.io/linux-syscall-table/ https://filippo.io/linux-syscall-table/
- masklinn 10y ago> At least on Linux the first few (i.e. the oldest, most common and useful) syscalls have not really moved around over the years IIRC raw syscalls are an officially supported kernel API, that's why you can have alternate libc implementations (e.g. musl), and Linux is an oddity in that, on most systems even if the syscalls are fairly stable there are no actual guarantees with respect to them, and the only officially supported interface to the kernel is the standard library. OSX does not allow statically linking libSystem for that reason for instance.
- 0xcde4c3db 10y agoAnother example: for OpenBSD part of the manual upgrade procedure (not the recommended method, but useful if you don't have a console to run the installer) is to make a copy of /sbin/reboot, because there's a good chance that the new /sbin/reboot you're about to install will have the wrong syscalls for the running kernel.
- 10y ago
- WhiteHat1 10y agoVery interesting. How much of a pain is this? Definitely worth looking. Of course there are other solutions to the problems as well. You can speed up linking by using IncrediBuild. But the last reasons (fun challenge!) and understanding the inner workings are useful, especially for software engineers.
- oso2k 10y agoA little self promotion but mostly because it addresses some of the other commenters concerns about malloc (or the lower-level api around sbrk): a couple years ago I wrote rt0 [0], a small (mostly minimal) C runtime for i386 & amd64 that makes it easier to replace libc & crt0 (as long as you have the kernel headers installed). Also, as part of the examples, I wrote wrappers around the sbrk syscall. Pretty easy to do and all documented in the repo. I expect to eventually port the lib to arm (raspberry pi) and aarm64. There's also lots of references to other small c runtimes. I'll be adding this one as well. [0] https://github.com/lpsantil/rt0 https://github.com/lpsantil/rt0
- nathan_f77 10y ago> When we learn C, we are taught that main is the first function called in a C program. In reality, main is simply a convention of the standard library. Well, I've already learned something new. I assumed that convention was from the compiler. This is a great resource.
- bogomipz 10y agoI had a question about this sentence: "It's often necessary to either push useless data or simply align the stack pointer when the pushed values don't happen to be aligned." That's kind of hand-wavy. How do we "simply align the stack pointer"?
- JoeAltmaier 10y agoOn most architectures, by decrementing it appropriately. E.g. subtract 4 to align from a 4-byte to an 8-byte boundary.
- bogomipz 10y agoThanks for the responses.
- david-given 10y agoAll modern platforms have decreasing stacks, so just AND it with ~(alignment-1). Of course, you now need to keep track of the old stack pointer so you can restore it. Most code saves the old stack pointer into the fp, so you can just do sp = fp to undo any pushes without needing to care about how much was pushed; but it's cleaner and more efficient to have the compiler arrange things on the stack so that everything's already aligned and you don't need to do it programmatically. ...why, yes, I have spend the past couple of months with my head buried inside a compiler backend; why do you ask?
- clifanatic 10y agoInteresting - my McAfee web washer blocked this site. Don't know why.
- valarauca1 10y agoA lot of web/internet filters block ASM related content. My company uses Barracuda Networks filter and most ASM references/content are blocked, Reason: Hacking
- mjevans 10y agoThey're not technically wrong... it's just 'hacking' in the traditional black magic/voodoo manipulation of actual systems components sense. That is, literally taking a hacksaw to a circuit board and altering it or making a new board.
- leeter 10y agoA few thoughts: * The space savings are moot as other processes such as the daemons are going to load libc into virtual memory anyway, and the kernel shares libc's page among all processes. * This adds a lot of LOC you have to maintain, instead of shoving it off on the compiler/libc vendor, this increases the chance of bugs. * This will prevent the use of VDSOs to optimize high volume system calls like gettimeofday. * It's still probably good to know how these happen, even if you're not doing them yourself. * The only place this would really see benefit is in a single process environment, however in those cases I would suggest a unikernel anyway for simplicity sake.
- taeric 10y agoI'd be interested to see benchmarks to consider point one. At face value, I fully agree with your point and doubt it has any benefit. However, it is less that has to be swapped in for your program to run. Would be curious if this has an odd cache friendliness for an application. The tooling answer to this, it seems, would be to support statically linked libraries. But again, I would want to see numbers before personally worrying about this.
- leeter 10y agoSo a few things to consider in regards to point 1: * Your executable is going to be loaded as a whole page regardless of how large it is, on most platforms this means you'll need at least 4k of User VM. * You'll need a page table, which has its own overhead. If someone was going to push back on the libc assertion I'd expect it here, a the PTEs for libc and and the VDSOs cannot be shared between processes (as far as I'm aware). * I would expect it to in theory RUN faster assuming it was a small toy program like the example, this is because there is less work to be done even with shared pages.
- taeric 10y agoRight, my question is along the lines of avoiding the pages of libc. An easy question here would be how many pages libc takes up. I'm assuming not many, but more than one. I think there is a strong argument that this page is often already paged into memory from everyone using it. However, if the function you used from it would have fit in the pages you were already using for your application, I could imagine some benefit. I continue to stress, though, that this is just imagined. Numbers would be first thing I would have to collect before acting on this. (And I hope it doesn't sound like I am tasking you or anyone else with this. That is not my intent.)
- coreyp_1 10y agoIt's posts like this (and the accompanying comments) that make me realize how much I still have left to learn! One of the reasons that I love HN is how informative you all are!
- lolisamurai 10y agoThe server is getting hit pretty hard right now, did not expect this much traffic. In the meantime, you can find a bbcode mirror of the guide here: https://ccplz.net/threads/writing-c-software-without-the-standard-library-linux-edition.69623/ https://ccplz.net/threads/writing-c-software-without-the-sta...
- jxy 10y agoIt's a very good learning process. But once your project scales up, you are essentially writing your own libc. And there is no portability. It only works with the specific architecture's calling convention and the specific c compiler.
- eliangidoni 10y agoI can't believe this post has 409 points. Are we in the 80's again ?
- beeforpork 10y agoIf the asm was written a little more cleverly, the syscalls would avoid almost all moves, because the compiler'd put everything in place: _syscall5: mov %r9, %r10 _syscall3: mov %rcx, %rax syscall ret And then: extern unsigned long _syscall3( unsigned long, unsigned long, unsigned long, unsigned long); extern unsigned long _syscall5( unsigned long, unsigned long, unsigned long, unsigned long, unsigned long, unsigned long); #define syscall0(NUM) _syscall3(0,0,0,NUM) #define syscall1(NUM,A) _syscall3(A,0,0,NUM) #define syscall2(NUM,A,B) _syscall3(A,B,0,NUM) #define syscall3(NUM,A,B,C) _syscall3(A,B,C,NUM) #define syscall4(NUM,A,B,C,D) _syscall5(A,B,C,NUM,0,D) #define syscall5(NUM,A,B,C,D,E) _syscall5(A,B,C,NUM,E,D)
- ant6n 10y agoIs that going to be inlined properly?
- sytelus 10y agoIt would be wrap this up in lightweight libc. There is uSTL for C++: https://msharov.github.io/ustl https://msharov.github.io/ustl
- nitwit005 10y agoI've tried this myself. What you'll run into is that you tend to need a few things that are non-trivial: An implementation of malloc/free Functions to parse and print floats (somewhat system dependent) Assembly implementations of any trigonometric functions used While there is code that goes to that effort (The Go runtime comes to mind), it's quite a pain for "normal" code.