ADDED grammar/grammar.gpp Index: grammar/grammar.gpp ================================================================== --- grammar/grammar.gpp +++ grammar/grammar.gpp @@ -0,0 +1,18 @@ +#define flag -#1 +#define exec x +#define mode flag(#1)#2 +#define user U +#define meta M +#define add +#1 +#define del -#1 +#define unix_lf z +#define keepnl n +#define tri #1#2#3 +#define comment c +#define string s +#define quote q +#define eval S +#define evalquote Q +#define stack bal_stack bal_unstack +#define meta_grammar meta_start meta_end meta_arg_start meta_sep meta_arg_end stack +#define user_grammar call_start call_end arg_start arg_sep arg_end stack arg_num_char quote_char ADDED grammar/makefile Index: grammar/makefile ================================================================== --- grammar/makefile +++ grammar/makefile @@ -0,0 +1,2 @@ +${TMP}/%: %.gpp grammar.gpp + ${gpp} $< | tr "\n" ' ' > $@ ADDED grammar/precomp.g.gpp Index: grammar/precomp.g.gpp ================================================================== --- grammar/precomp.g.gpp +++ grammar/precomp.g.gpp @@ -0,0 +1,25 @@ +#include grammar.gpp +#mode comment "//" "\n" +// this file defines the "precomp" grammar, the grammar +// that's used to process C code templates +// vim: ft=c +#define call_start "[" +#define call_end "]" +#define arg_start " " +#define arg_sep ",\\w" +#define arg_end "]" +#define bal_stack "\([" +#define bal_unstack "\)]" +#define arg_num_char "\\$" +#define quote_char "|" +#define meta_start "\\w[" +#define meta_end "]\\w" +#define meta_arg_start " " +#define meta_sep "\\w:\\w" +#define meta_arg_end "]\\W" +add(unix_lf) +add(comment "---" "\\n") +add(comment "(*" "*)") +flag(exec) +mode(user, user_grammar) +mode(meta, meta_grammar) ADDED kcore/__stack_chk_fail.fn.c Index: kcore/__stack_chk_fail.fn.c ================================================================== --- kcore/__stack_chk_fail.fn.c +++ kcore/__stack_chk_fail.fn.c @@ -0,0 +1,3 @@ +#include + +void __stack_chk_fail(void) { kstop(-1); } Index: kcore/boot.rt.x86.lin.64.s ================================================================== --- kcore/boot.rt.x86.lin.64.s +++ kcore/boot.rt.x86.lin.64.s @@ -1,11 +1,11 @@ ; vim: ft=nasm bits 64 %include "../arch/x86.lin.64.s" global _start:function extern _boot -extern entry; +extern entry _start: mov rbp, rsp mov rdi, [rbp + 0] ; argc lea rsi, [rbp + 8] ; argv Index: kcore/core.h ================================================================== --- kcore/core.h +++ kcore/core.h @@ -2,12 +2,10 @@ #define KIcore #include #include #include -static void* const null = (void*)0; - typedef struct kvar { ksraw name; ksraw val; char* platform; } kvar; @@ -17,6 +15,116 @@ kiochan err; sz argc; char** argv; kvar* vars; } kenv; + +/* i'm really sorry okay */ +typedef +#if (__STDC_VERSION__ >= 199901L) + _Bool bool; +#endif +enum +#if !(__STDC_VERSION__ >= 199901L) + bool /* enum bool { */ +#endif +{ + false = 0, no = 0, + true = 1, yes = 1 +} +#if !(__STDC_VERSION__ >= 199901L) + bool /* } bool ; */ +#endif +; + +#ifndef KFclean +# if (__STDC_VERSION__ >= 199901L) ||\ + (__cplusplus >= 201103L) +# define KVvm_args __VA_ARGS__ +# define KVvm_spec ... +# define KFfeat_variadic_macro +# else +# define KVvm_args K_TEMP_M2QD52 +# define KVvm_spec K_TEMP_M2QD52 +# endif +# if defined(__GNUC__) || defined(__clang__) +# define KA(KVvm_spec) __attribute__((KVvm_args)) +# else +# define KA(KVvm_spec) +# endif +# define KAformat(KVvm_spec) KA(format(KVvm_args)) +# define KAexport(KVvm_spec) KA(visibility(KVvm_args)) + +# define KAunused KA(unused) +# define KAnoreturn KA(noreturn) +# define KApure KA(const) +# define KAinline KA(always_inline) +# define KAflatten KA(flatten) +# define KAexport_none KAexport("hidden") +# define KAexport_force KAexport("default") +/* now we define standard version flags, + * to make it easy for the user to write + * more portable code. */ +# if (__STDC_VERSION__ >= 199901L) +# define KFstd_c89 +# define KFstd_c99 +# define KVstd c99 +# else +# ifdef __STDC__ +# define KFstd_c89 +# define KVstd c89 +# else +# define KVstd K&R /* UH OH */ +# endif +# endif +# if (__STDC_VERSION__ >= 201103L) +# define KFstd_c11 +# define KVstd c11 +# endif +# ifdef __cplusplus +# define KFstd_cpp +# define KVstd c++ +# if (__cplusplus >= 201103L) +# define KFstd_cpp11 +# define KVstd c++11 +# endif /* TODO: add more */ +# endif +#endif + +/* hooo boy. null. that one's got a storied + * history across the versions and dialects. + * below, we try to find the ideal way to + * offer a "null" "keyword" depending on + * dialect, version, and whether the user + * has asked for macros to be suspended. + * note that this may result in no "null" + * being defined in C++ or K&R C. */ +#if defined (__cplusplus) && ! defined(KFclean) +# if __cplusplus >= 201103L +# define null nullptr +# else +# define null ((void*)0) +# endif +#elif defined __STDC__ + enum { null = 0 }; + /* believe it or not, this is actually + * completely legal. doesn't even raise + * a single warning. i was surprised too. */ +#elif ! defined(KFclean) +# define null ((void*)0) +#endif + +#ifdef __cplusplus +# define noreturn [[ noreturn ]] +#elif __STDC_VERSION__ >= 201103L +# define noreturn _Noreturn +#else +# define noreturn +#endif + +noreturn void kstop(longstat code); + +#ifdef KFclean +# undef noreturn +#endif + #endif DELETED kcore/def.fbsd.i Index: kcore/def.fbsd.i ================================================================== --- kcore/def.fbsd.i +++ kcore/def.fbsd.i @@ -1,2 +0,0 @@ -#define KVos fbsd -typedef unsigned char stat; DELETED kcore/def.h Index: kcore/def.h ================================================================== --- kcore/def.h +++ kcore/def.h @@ -1,21 +0,0 @@ -#include -/* - * ~ lexi hale - * define flags so we can reason about - * our environment. */ - -#if (KVos == lin) ||\ - (KVos == fbsd) ||\ - (KVos == obsd) ||\ - (KVos == nbsd) ||\ - (KVos == dar) ||\ - (KVos == and) -# define KFenv_unix -#endif - -#if defined(KFenv_unix) ||\ - (KVos == hai) ||\ - (KVos == mgw) -# define KFenv_posix -#endif - ADDED kcore/def.h.m Index: kcore/def.h.m ================================================================== --- kcore/def.h.m +++ kcore/def.h.m @@ -0,0 +1,32 @@ +--- kcore/def.h.m → +--- ~ lexi hale +--- this file gathers information on the environment it's +--- being compiled in, setting macros that other headers +--- need. it will be emitted as . +--- vim: ft=c + +[ifdef atom_target_bits] + [define target: [atom_target_arch].[atom_target_os].[atom_target_bits]] + #define KVbits [atom_target_bits] +[else] + [define target: [atom_target_arch].[atom_target_os]] +[endif] +#define KVtarget [target] +#define KVos [atom_target_os] +#define KVarch [atom_target_arch] +[if [target_unix] == yes] + #define KFenv_unix + #define KFenv_posix +[else] + [if [target_posix] == yes] + #define KFenv_posix + [endif] +[endif] + +#define Kpragma(p) _Pragma(#p) +#if defined(__GNUC__) || defined(__clang__) +# define Kerror(msg) Kpragma(GCC error #msg) +#else +# define Kerror(msg) Kpragma(message #msg) +#endif +#define Knoimpl(fn) Kerror(no implementation of fn for platform [target]) DELETED kcore/def.lin.i Index: kcore/def.lin.i ================================================================== --- kcore/def.lin.i +++ kcore/def.lin.i @@ -1,2 +0,0 @@ -#define KVos lin -typedef unsigned char stat; DELETED kcore/def.win.i Index: kcore/def.win.i ================================================================== --- kcore/def.win.i +++ kcore/def.win.i @@ -1,2 +0,0 @@ -#define KVos win -typedef u32 stat; ADDED kcore/exit.fn.x86.lin.32.s Index: kcore/exit.fn.x86.lin.32.s ================================================================== --- kcore/exit.fn.x86.lin.32.s +++ kcore/exit.fn.x86.lin.32.s @@ -0,0 +1,10 @@ +bits 32 +%include "../arch/x86.lin.32.s" +; vim: ft=nasm +global kio_posix_exit + +kio_posix_exit: + mov sys.reg.0, sys.call.exit + mov sys.reg.1, [esp + 4] ; first C int argument + sys.call + ; does not return ADDED kcore/exit.fn.x86.lin.64.s Index: kcore/exit.fn.x86.lin.64.s ================================================================== --- kcore/exit.fn.x86.lin.64.s +++ kcore/exit.fn.x86.lin.64.s @@ -0,0 +1,11 @@ +bits 64 +%include "../arch/x86.lin.64.s" +%include "../arch/x86.cdecl.64.s" +; vim: ft=nasm + +global kio_posix_exit +kio_posix_exit: + mov sys.reg.1, ccall.reg.0 ;nop - rdi → rdi + mov sys.reg.0, sys.exit + sys.call + ; no return Index: kcore/kcore.md ================================================================== --- kcore/kcore.md +++ kcore/kcore.md @@ -1,7 +1,7 @@ # kcore -**kcore** is the foundation for the rest of libk. it defines types and structs that are needed by every program, and provides the stub that launches a program's "entry" function. +**kcore** is the foundation for the rest of libk. it defines types and structs that are needed by every program, and provides the stub that launches a program's "entry" function. unlike the non-core modules, kcore definitions simply use the prefix `k-`. ## entry when using libk, your program's entry point will not be the `int main(int,char**)` function that libc opens into. libk will call the function `stat entry(kenv)` instead. like libc, the value returned by `entry` will be returned to the host platform. ## types @@ -30,5 +30,36 @@ ### struct kvar `kvar` is a struct that abstracts over platform environment variables. * `kstr name` - the name of an environment variable * `kstr val` - the value of an environment variable * `char* platform` - a pointer into the platform's underlying representation + +## functions + +### kstop() + +`noreturn void kstop(stat)` terminates the program immediately, returning the specified integer to the OS as an exit status. the type of integer it takes depends on your operating system. consider using the `enum kbad` defines in ``, which are designed to standardize exit statuses across different software and are synchronized with a FreeBSD effort to do the same (`` and ``. since libk is designed for new, modern code, breaking old code isn't a concern, and we turn on all these new keywords as soon as you load ``. + +libk attempts to find the best possible definition and implementation for these various types and keywords, abstracting across different C versions and dialects. you can go ahead and use the normal version of the keywords (e.g. `noreturn`, `bool`) no matter what kind of compiler you're using and you're guaranteed that as long as you don't go fiddling with undefined- or implementation behavior, your code will behave the same every time. at worst, it may lack a few optimizations or warnings. + +(the one minor exception is `null`, a new keyword which libk uses in preference to the ugly, hard-to-type C macro `NULL`.) + +`bool` is implemented as a `_Bool` typedef if `_Bool` is available, and as an enum typedef otherwise. either way, and regardless of whether `KFclean` has been defined, booleans can be used and set to the values `true`, `false`, `yes`, or `no`. `yes` and `true` are synonyms, as are `false` and `no`. + +### macros + +if the flag `KFclean` has not been defined, kcore defines a number of macros to abstract over nonstandard C features. for instance, the `KA*` macros can be used to specify GNU/clang attributes without worrying about compatibility, as they'll automatically be blanked under an incompatible compiler. the KA series includes: + + * `KApure` - marks a "pure" function that changes no state and only takes purely numeric arguments, allowing the compiler to avoid repetetive calls to it or even evaluate it at runtime. + * `KAnoreturn` - the GNU/clang-specific version of `noreturn`. + * `KAunused` - acknowledges that a variable is unused and tells the compiler to shut up about it. + * `KAinline` - forces the compiler to inline a function whether it likes it or not. + * `KAflatten` - placed on a function, will force-inline all function calls *made by* that function — sort of like `KAinline` in reverse. + * `KAformat()` - specifies that the function has printf-style syntax for error-checking purposes. worst hack in history. + +if you wish to add more (there are like, hundreds) please consider making a merge request. however, the generic macro KA() is also available in the interim: `KA(unused)`. + +however, unlike C++ attributes, GNU-style attributes can only be placed on a function declaration, *not* its definition. ADDED kcore/magic.h Index: kcore/magic.h ================================================================== --- kcore/magic.h +++ kcore/magic.h @@ -0,0 +1,67 @@ +/* + * ~ lexi hale + * one of libk's biggest goals is standardization across + * software. for this reason, we define here a variety + * of "magical numbers" used to signal or describe + * various states, messages, and errors. suggestions are + * welcome. + */ +#include + +typedef enum kbad { + // values to be returned by an entry function + kbad_ok = 0, + /* the requested operation completed + * successfully */ + + /* these are synchronized with freebsd's existing + * sysexit.h conventions. */ + kbad_usage = 64, + /* # fbsd EX_USAGE + * a usage screen was displayed to the user + * on request or due to improperly formed call */ + kbad_data = 65, + /* # fbsd EX_DATAERR + * data supplied by the user was incorrect in + * some way */ + kbad_no_input = 66, + /* # fbsd EX_NOINPUT + * input to the program is absent */ + kbad_user = 67, + /* # fbsd EX_NOUSER + * these aren't the droids you're looking for */ + kbad_host = 68, + /* # fbsd EX_NOHOST + * that host doesn't exist */ + kbad_avail = 69, + /* # fbsd EX_UNAVAILABLE + * a necessary resource or support system isn't + * available, or something else went wrong */ + kbad_internal = 70, + /* # fbsd EX_SOFTWARE + * "my bad" */ + kbad_os = 71, + /* # fbsd EX_OSERR + * the OS did a fucksy-wucksy */ + kbad_osfile = 72, + /* # fbsd EX_OSFILE + * a system file is fucked up or gone */ + kbad_creat = 73, + /* # fbsd EX_CANTCREAT */ + kbad_io = 74, + /* # fbsd EX_IOERR */ + kbad_try = 75, + /* # fbsd EX_TEMPFAIL + * something went wrong this time. try again + * some other time */ + kbad_proto = 76, + /* # fbsd EX_PROTOCOL + * failure to speak a protocol correctly */ + kbad_perm = 77, + /* # fbsd EX_NOPERM + * you or the program do not have permissions + * to do the thing requested */ + kbad_conf = 78, + /* # fbsd EX_CONFIG + * your configuration is fucked */ +} kbad; Index: kcore/makefile ================================================================== --- kcore/makefile +++ kcore/makefile @@ -1,9 +1,19 @@ ## kcore/makefile # kcore has to include, among other things, a replacement -# for stddef.h, and that can't be written in portable C, +# for stddef.h, and that can't be written in portable C. # so we're generating it at build time. + +# gen-headers = type.h + +include ../modmake + +## the below code predates the introduction of gpp +## to generate these headers from templates instead +## of trying to write one for everypossible arch +## tuple. it is left as a monument to a terrible +## and now blissfully forgotten past. # # look, imma just be straight with you. the mechanism we're # using to generate these headers is unbelievably heinous. # it's inelegant, it's gross, and it's horrible. in the long # term this NEEDS to be replaced with a bespoke solution @@ -12,26 +22,21 @@ # someone more competent than me will someday be interested # in fixing this horrorshow. # # until them: i'm sorry. # very sincerely yours, lexi hale - -gen-headers = type.h - -include ../modmake - -${OUT}/k/type.h: ${TMP}/type.${TARGET}.i - cp $< $@ - -# generating C source in make… yaaay -define arch = -${TMP}/type.$(1).%.$(2).i: type.$(1).$(2).i def.%.i ${TMP} - echo '#ifndef KItype' > $$@ - echo '#define KItype' >> $$@ - cat $$< >> $$@ - cat def.$$*.i >> $$@ - echo '#endif' >> $$@ -endef - -$(eval $(call arch,x86,32)) -$(eval $(call arch,x86,64)) - +# ${OUT}/k/type.h: ${TMP}/type.${TARGET}.i +# cp $< $@ +# +# # generating C source in make… yaaay +# define arch = +# ${TMP}/type.$(1).%.$(2).i: type.$(1).$(2).i def.%.i ${TMP} +# echo '#ifndef KItype' > $$@ +# echo '#define KItype' >> $$@ +# cat $$< >> $$@ +# cat def.$$*.i >> $$@ +# echo '#endif' >> $$@ +# endef +# +# $(eval $(call arch,x86,32)) +# $(eval $(call arch,x86,64)) +# ADDED kcore/old/def.fbsd.i Index: kcore/old/def.fbsd.i ================================================================== --- kcore/old/def.fbsd.i +++ kcore/old/def.fbsd.i @@ -0,0 +1,2 @@ +#define KVos fbsd +typedef unsigned char stat; ADDED kcore/old/def.h Index: kcore/old/def.h ================================================================== --- kcore/old/def.h +++ kcore/old/def.h @@ -0,0 +1,28 @@ +#include +/* + * ~ lexi hale + * define flags so we can reason about + * our environment. */ + +#if (KVos == lin) ||\ + (KVos == fbsd) ||\ + (KVos == obsd) ||\ + (KVos == nbsd) ||\ + (KVos == dar) ||\ + (KVos == and) +# define KFenv_unix +#endif + +#if defined(KFenv_unix) ||\ + (KVos == hai) ||\ + (KVos == mgw) +# define KFenv_posix +#endif + +#define Kpragma(p) _Pragma(#p) +#if defined(__GNUC__) || defined(__clang__) +# define Kerror(msg) Kpragma(GCC error #msg) +#else +# define Kerror(msg) Kpragma(message #msg) +#endif +#define Knoimpl(fn,p) Kerror(no implementation of fn for platform p) ADDED kcore/old/def.lin.i Index: kcore/old/def.lin.i ================================================================== --- kcore/old/def.lin.i +++ kcore/old/def.lin.i @@ -0,0 +1,2 @@ +#define KVos lin +typedef unsigned char stat; ADDED kcore/old/def.win.i Index: kcore/old/def.win.i ================================================================== --- kcore/old/def.win.i +++ kcore/old/def.win.i @@ -0,0 +1,2 @@ +#define KVos win +typedef u32 stat; ADDED kcore/old/type.x86.32.i Index: kcore/old/type.x86.32.i ================================================================== --- kcore/old/type.x86.32.i +++ kcore/old/type.x86.32.i @@ -0,0 +1,14 @@ +#define KVarch x86 +#define KVbits 32 + +typedef unsigned long sz; + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned long u32; +typedef signed long s32; + +typedef u32 word; +typedef s32 sword; ADDED kcore/old/type.x86.64.i Index: kcore/old/type.x86.64.i ================================================================== --- kcore/old/type.x86.64.i +++ kcore/old/type.x86.64.i @@ -0,0 +1,18 @@ +#define KVarch x86 +#define KVbits 64 + +typedef unsigned long long sz; + +typedef unsigned char u8; +typedef signed char s8; +typedef unsigned short u16; +typedef signed short s16; +typedef unsigned long u32; +typedef signed long s32; +typedef unsigned long long u64; +typedef signed long long s64; +typedef __uint128_t u128; +typedef __int128_t s128; + +typedef u64 word; +typedef s64 sword; ADDED kcore/stop.fn.c Index: kcore/stop.fn.c ================================================================== --- kcore/stop.fn.c +++ kcore/stop.fn.c @@ -0,0 +1,17 @@ +/* kcore/stop.fn.c - kstop() + * ~ lexi hale + * this file defines a function that prematurely exits from + * a running libk program with an appropriate exit status. */ + +#include +#include // so we know what system this is +#include + +#ifdef KFenv_posix +# define STOPFN kio_posix_exit + extern void STOPFN(int); +#else + Knoimpl(kstop) +#endif + +noreturn void kstop (longstat code) { STOPFN(code); } ADDED kcore/testbin.exe.c Index: kcore/testbin.exe.c ================================================================== --- kcore/testbin.exe.c +++ kcore/testbin.exe.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include + +kbad entry(kenv e) { + const char msg[] = "hello from libk\n"; + ksraw ptr = { Kmsz(msg), msg }; + + bool maybe = true; + maybe = no; + + if (kiosend(e.std, ptr, null) == kiocond_ok) { + return kbad_ok; + } else { + return kbad_io; + } +} ADDED kcore/type.h.m Index: kcore/type.h.m ================================================================== --- kcore/type.h.m +++ kcore/type.h.m @@ -0,0 +1,63 @@ +--- kcore/type.h.m → +--- ~ lexi hale +--- this file gathers information on the environment it's +--- being compiled in, defining types that our code +--- needs. it will be emitted as . +--- vim: ft=c + +// arch bit length [atom_target_bits] +--- make some gigantic fucking assumptions +[if [atom_target_bits] >= 8] + typedef unsigned char u8; + typedef signed char s8; + [if [atom_target_bits] >= 16] + typedef unsigned short u16; + typedef signed short s16; + [if [atom_target_bits] >= 32] + typedef unsigned long u32; + typedef signed long s32; + [if [atom_target_bits] >= 64] + typedef unsigned long long u64; + typedef signed long long s64; + [if [atom_target_arch] == x86] + #if defined(__GNUC__) || defined(__clang__) + typedef __uint128_t u128; + typedef __int128_t s128; + #endif + [endif] + [endif] + [endif] + [endif] +[endif] + +typedef u[atom_target_bits] sz; +typedef s[atom_target_bits] ssz; + +--- make sure something unlikely doesn't happen +[define [maxtype name, n]: + [if [n] > [atom_target_bits]] + typedef u[atom_target_bits] [name] + [else] + typedef u[n] [name] + [endif]] + +// exit status integer types - pls use kbad in instead +[if [target_posix] == yes] + /* by convention, posix return values are 8-bit, + * but note that many modern UNIXes do technically + * support higher-bit values. for this reason, + * longstat is defined differently under posix. */ + typedef u8 stat; + [maxtype longstat, 32]; +[else] + [if ([atom_target_os] == win) || + ([atom_target_os] == vms)] + [maxtype stat, 32] + [else] + typedef u8 stat; + /* we don't know a specific exit status type + * for your arch so we're going with a sane + * default. if this is wrong, help us fix it! */ + [endif] + typedef stat longstat; +[endif] DELETED kcore/type.x86.32.i Index: kcore/type.x86.32.i ================================================================== --- kcore/type.x86.32.i +++ kcore/type.x86.32.i @@ -1,14 +0,0 @@ -#define KVarch x86 -#define KVbits 32 - -typedef unsigned long sz; - -typedef unsigned char u8; -typedef signed char s8; -typedef unsigned short u16; -typedef signed short s16; -typedef unsigned long u32; -typedef signed long s32; - -typedef u32 word; -typedef s32 sword; DELETED kcore/type.x86.64.i Index: kcore/type.x86.64.i ================================================================== --- kcore/type.x86.64.i +++ kcore/type.x86.64.i @@ -1,18 +0,0 @@ -#define KVarch x86 -#define KVbits 64 - -typedef unsigned long long sz; - -typedef unsigned char u8; -typedef signed char s8; -typedef unsigned short u16; -typedef signed short s16; -typedef unsigned long u32; -typedef signed long s32; -typedef unsigned long long u64; -typedef signed long long s64; -typedef __uint128_t u128; -typedef __int128_t s128; - -typedef u64 word; -typedef s64 sword; ADDED kio/send.c Index: kio/send.c ================================================================== --- kio/send.c +++ kio/send.c @@ -0,0 +1,30 @@ +#include +#include +#include +/* send.c - kiosend() + * ~ lexi hale + * kiosend() writes to a channel with an open out stream + */ + +// we define all platform functions here, +// whether or not they're for the correct +// platform - only the ones actually called +// by the generated code will be linked +extern sz kio_posix_fd_write(int fd, const char* buf, sz len); + +kiocond kiosend(kiochan target, ksraw string, sz* len) { +# ifdef KFenv_posix + sz size = kio_posix_fd_write(target.out.platform_fd, string.ptr, string.size); + if (size == -1) return kiocond_fail; //TODO: retrieve errno and offer more specific errors +# else +# if KVos == win +# error windows IO send function not yet defined +# else + Knoimpl(kiosend,KVos); +# error missing implementation // boring error for plebs +# endif +# endif + + if (len != null) *len = size; + return kiocond_ok; +} Index: libk.md ================================================================== --- libk.md +++ libk.md @@ -27,10 +27,23 @@ 7. **tooling.** libk is intended as more than just a library. it's also intended to work with some basic tooling to automate tasks that current binary tooling is inadequate for -- for instance, embedding binary data into a program binary. (see module [kgraft](kgraft)) 8. **modularity.** libk is not part of the C specification and it isn't always going to be practical for developers to expect the entire library to be present on the end-user's computer. so libk is designed to be usable in many different ways -- as a traditional library, as a static library, in full form or with only components needed by the developer, to be distributed either on its own or as part of a binary. 9. **compatibility.** code that links against libk should be able to compile and run on any operating system. in the ideal case (Linux or FreeBSD) it will be able to do so without touching any other system libraries; for less ideal environments like Windows, libk will when necessary abstract over system libraries or libc itself. 10. **sane error-handling.** every time you type `errno` god murders a puppy. +## dependencies + +libk is designed to be as portable and depedency-free as possible. ideally, it will be possible to compile code against libk using nothing but libk itself. + +compiling libk is also designed to be as easy as possible. it has only two external dependencies, the macro processor [gpp], needed for compile-time header generation , and the [GNU make] utility, whose advanced features are needed to perform the relatively complex task of building all of libk from the ground up. + + [gpp]: http://en.nothingisreal.com/wiki/GPP + [GNU make]: http://www.gnu.org/software/make + +while gpp is a very small program that builds quickly and has no major dependencies of its own, it is an obscure program not likely to be found in any repositories and with an uncertain future. for these reasons, adding m4 translations of the gpp headers should be a long-term priority. being able to be built with both a very small, easily built macro processor, and a very large but extremely well-supported processor, should make libk maximally buildable and future-proof. + +while this project will include gpp tooling and GNU makefiles designed to ease the task of writing and building libk code (as well as tools in many other languages, including native binaries that compile against libk), none of them are required for the task. + ## naming conventions one of the most frustrating things about libc is its complete and total *lack* of a naming convention. in C, every function and global is injected into a single global namespace, including macros. this means that every libc header you include scatters words all over that namespace, potentially clobbering your function with a macro! libk is designed to fix this (in hindsight) glaring error. Index: makefile ================================================================== --- makefile +++ makefile @@ -1,51 +1,72 @@ export OUT = $(PWD)/out +# TODO: calculate these using $(MAKE_HOST) export ARCH = x86 export OS = lin export BITS = 64 export TMP = $(PWD)/tmp -export TARGET = $(ARCH).$(OS).$(BITS) +ifneq ($(BITS),) + export TARGET = $(ARCH).$(OS).$(BITS) +else + export TARGET = $(ARCH).$(OS) +endif +export gpp = gpp export lin-headers = /usr/include/asm export fbsd-headers = /usr/include/sys moddirs = $(wildcard k*) binaries = $(wildcard k*/*.exe.c) functions = $(wildcard k*/*.fn.c) -assemblies = $(wildcard k*/*.fn.${TARGET}.s) +assemblies = $(wildcard k*/*.fn.$(TARGET).s) binmods = $(sort $(dir $(binaries))) # i'm sorry -collect = $(strip $(foreach dir,$(moddirs),$(addprefix $(OUT)/$(dir).,$(notdir $(wildcard $(dir)/$1))))) +collect = $(strip $(foreach dir,$(moddirs),$(wildcard $(dir)/*.$1))) +transform = $(strip $(foreach dir,$(moddirs),$(patsubst $(dir)/%.$1,$(subst @,$(dir),$2),$(wildcard $(dir)/*.$1)))) -cfnsources = $(call collect,*.fn.c) -sfnsources = $(call collect,*.fn.${TARGET}.s) -crtsources = $(call collect,*.rt.c) -srtsources = $(call collect,*.rt.${TARGET}.s) -fnsources = $(cfnsources) $(sfnsources) -rtsources = $(crtsources) $(srtsources) -sources = $(fnsources) $(rtsources) +m-hdr-macs = $(call collect,h.m) +m-c-src-macs = $(call collect,c.m) +m-s-src-macs = $(call collect,$(TARGET).s.m) -cfnobjects = $(cfnsources:%.c=%.o) -sfnobjects = $(sfnsources:%.s=%.o) -crtobjects = $(crtsources:%.c=%.o) -srtobjects = $(srtsources:%.s=%.o) -fnobjects = $(cfnobjects) $(sfnobjects) -rtobjects = $(crtobjects) $(srtobjects) +m-c-sources = $(call transform,c.m,$(TMP)/@.%.c) +m-s-sources = $(call transform,$(TARGET).s.m,$(TMP)/@.%.$(TARGET).s) + +m-headers = $(call transform,h.m,$(OUT)/k/%.h) +m-c-objects = $(call transform,c.m,$(OUT)/@.%.o) +m-s-objects = $(call transform,$(TARGET).s.m,$(OUT)/@.%.$(TARGET).o) + +fnobjects = $(call transform,fn.c,$(OUT)/@.%.fn.o) \ + $(call transform,fn.c.m,$(OUT)/@.%.fn.o) \ + $(call transform,fn.$(TARGET).s,$(OUT)/@.%.fn.$(TARGET).o) \ + $(call transform,fn.$(TARGET).s.m,$(OUT)/@.%.fn.$(TARGET).o) + +rtobjects = $(call transform,rt.c,$(OUT)/@.%.rt.o) \ + $(call transform,rt.c.m,$(OUT)/@.%.rt.o) \ + $(call transform,rt.$(TARGET).s,$(OUT)/@.%.rt.$(TARGET).o) \ + $(call transform,rt.$(TARGET).s.m,$(OUT)/@.%.rt.$(TARGET).o) + objects = $(fnobjects) $(rtobjects) header-dir = /usr/include lib-dir = /usr/lib +unix-oses = lin fbsd dar and posix-oses = lin fbsd dar and hai mgw -ifeq ($(findstring $(OS),$(posix-oses)),$(OS)) -export POSIX = yes +ifeq ($(findstring $(OS),$(unix-oses)),$(OS)) + export UNIX = yes + export POSIX = yes else -export POSIX = no + export UNIX = no + ifeq ($(findstring $(OS),$(posix-oses)),$(OS)) + export POSIX = yes + else + export POSIX = no + endif endif # include libgcc.a in gcc builds, just in case ifeq ($(CC),gcc) export COMPLIB = -lgcc @@ -52,13 +73,13 @@ endif all: $(OUT) defs obj tool lib.static $(OUT)/boot.o lib.shared lib.static: defs obj $(OUT)/libk.a lib.shared: defs obj $(OUT)/libk.so -obj: $(moddirs:%=%.obj) -defs: $(moddirs:%=%.def) -tool: $(OUT)/libk.a $(binmods:%=%.tool) +obj: $(moddirs:%=%.obj) +defs: $(moddirs:%=%.def) +tool: $(OUT)/boot.o $(OUT)/libk.a $(binmods:%=%.tool) clean: rm -rf $(TMP) $(OUT) install: all install -d $(header-dir)/k -o root -g wheel @@ -68,28 +89,31 @@ $(lib-dir)/k/ -o root -g wheel -m 0644 uninstall: $(header-dir)/k $(lib-dir)/k rm -rf $^ -lists = moddirs functions assemblies cfnobjects sfnobjects crtobjects srtobjects rtobjects binaries binmods POSIX +lists = moddirs functions assemblies fnobjects rtobjects binaries binmods POSIX dbg: @echo -e lists: $(foreach var, $(lists), "\\n - \\e[1m$(var)\\e[m = $($(var))") -%.obj: %/makefile ${TARGET}.calls $(OUT) +%.obj: %/makefile $(TMP)/precomp.g $(TARGET).calls $(OUT) cd $* && $(MAKE) obj -%.tool: %/makefile $(OUT) +%.tool: %/makefile $(TMP)/precomp.g $(OUT) cd $* && $(MAKE) tool %.dbg: %/makefile $(OUT) cd $* && $(MAKE) dbg -%.def: %/makefile $(OUT) $(OUT)/k +%.def: %/makefile $(TMP)/precomp.g $(OUT) $(OUT)/k cd $* && $(MAKE) def %.calls: arch/makefile cd arch && $(MAKE) $(TMP)/calls.$*.s + +$(TMP)/precomp.g: grammar/precomp.g.gpp $(TMP) + cd grammar && $(MAKE) $@ $(OUT)/libk.so: $(fnobjects) ld -shared $(COMPLIB) -o $@ $^ @# $(CC) -shared -fPIC -nostdlib $(COMPLIB) -o $@ $(OUT)/*.o @@ -100,7 +124,7 @@ @# using `ar rc` and ranlib here instead of @# `ar rcs` in case `ar` isn't the GNU version ar rc $@ $^ ranlib $@ -$(OUT) $(OUT)/k: +$(OUT) $(OUT)/k $(TMP): mkdir -p $@ Index: modmake ================================================================== --- modmake +++ modmake @@ -2,44 +2,70 @@ # this is the master makefile that controls the building of each # libk module. it is included from each k*/makefile. # vim: ft=make mod = $(notdir $(PWD)) -src = $(wildcard *.c) $(wildcard *.s) +src = $(wildcard *.c) $(wildcard *.s) $(filter-out %.h,$(patsubst %.m,%,$(wildcard *.m))) bare = $(mod:k%=%) -headers = $(wildcard *.h) $(gen-headers) +headers = $(wildcard *.h) $(gen-headers) $(patsubst %.m,%,$(wildcard *.h.m)) tools = $(filter %.exe.c, $(src)) nontools = $(filter-out %.exe.c, $(src)) cobjects = $(filter %.c, $(nontools)) sobjects = $(filter %.${TARGET}.s, $(nontools)) -cflags = -isystem ${OUT} -fPIC -nostdlib ${COMPLIB} -L${OUT} -lk +gpp = gpp +cflags = -std=c11 -isystem ${OUT} -fPIC -nostdlib ${COMPLIB} -L${OUT} + +m-env = atom_target_arch=${ARCH} +m-env += atom_target_os=${OS} +ifneq (${BITS},) #!!! ifdef does NOT work with environment variables + m-env += atom_target_bits=${BITS} +endif +m-env += target_posix=${POSIX} +m-env += target_unix=${UNIX} + +m-grammar = $(file < ${TMP}/precomp.g) +m-comp = $(gpp) $(m-grammar) $(m-env:%=-D%) obj: $(cobjects:%.c=${OUT}/$(mod).%.o) \ $(sobjects:%.s=${OUT}/$(mod).%.o) tool: $(tools:%.exe.c=${OUT}/$(mod).%) \ ${OUT}/libk.a def: $(headers:%=${OUT}/k/%) dbg: + @echo src = $(src) @echo tools = $(tools) @echo TARGET = ${TARGET} @echo cobjects = $(cobjects) @echo sobjects = $(sobjects) @echo headers = $(headers) + @echo m-comp = $(m-comp) + @echo m-grammar = $(m-grammar) + @echo m-env = $(m-env) "$(m-env:%=-D%)" @echo mod = $(mod) -${OUT}/$(mod).%.o: %.c +${OUT}/k/%.h: %.h.m + $(m-comp) $< > $@ + +.PRECIOUS: ${TMP}/$(mod).% +${TMP}/$(mod).%: %.m ${TMP} + $(m-comp) $< > $@ + +${OUT}/$(mod).%.o: ${TMP}/$(mod).%.c + $(CC) $(cflags) -c $< -o $@ + +${OUT}/$(mod).%.o: %.c $(bare).h $(CC) $(cflags) -c $< -o $@ ${OUT}/k/%.h: %.h cp $< $@ ${OUT}/$(mod).%: %.exe.c - $(CC) $(cflags) $< -o $@ + $(CC) $(cflags) $< ${OUT}/libk.a -o $@ ${TMP}: mkdir -p ${TMP} #- assembly @@ -52,42 +78,56 @@ # ${OUT} = ultimate build directory # $(mod) = module name # % = function name # $(1) = arch tuple -arch = ${OUT}/$(mod).%.$(1).o: %.$(1).s +arch = ${OUT}/$(mod).%.$(1).o: $2%.$(1).s # invoke with $(call arch,tuple). do not # put spaces between either term though! ifeq ($(debug),yes) yasm-flags = -gdwarf2 endif +yasm = yasm $(yasm-flags) -f$1 -i${TMP} $< -o $@ + #-- linux # linux uses the ELF{32,64} binary format, and generating these # from yasm is trivial. linux only supports one ABI per format, # at least with ELF, so that's all we need to do. -#${OUT}/$(mod).%.x86.lin.32.o: %.x86.lin.32.s -$(call arch,x86.lin.32) - yasm $(yasm-flags) -felf32 -i${TMP} $< -o $@ + +$(call arch,x86.lin.32,) + $(call yasm,elf32) + +$(call arch,x86.lin.64,) + $(call yasm,elf64) + +$(call arch,x86.lin.32,${TMP}/$(mod).) + $(call yasm,elf32) -#${OUT}/$(mod).%.x86.lin.64.o: %.x86.lin.64.s -$(call arch,x86.lin.64) - yasm $(yasm-flags) -felf64 -i${TMP} $< -o $@ +$(call arch,x86.lin.64,${TMP}/$(mod).) + $(call yasm,elf64) #-- freebsd # the freebsd ABI is different, so it will require different code # (though there might be ways to minimize that). freebsd uses the # same binary format as Linux (though it also supports a.out and # COFF) but because freebsd can interpret multiple different ABIs # the object files need to be "branded" with the correct one # using the tool brandelf (`brandelf -t [ABI]`) -$(call arch,x86.fbsd.32) - yasm -felf32 $< -o $@ +$(call arch,x86.fbsd.32,) + $(call yasm,elf32) + brandelf -t FreeBSD $@ + +$(call arch,x86.fbsd.64,) + $(call yasm,elf64) brandelf -t FreeBSD $@ -$(call arch,x86.fbsd.64) - yasm -felf64 $< -o $@ +$(call arch,x86.fbsd.32,${TMP}/$(mod).) + $(call yasm,elf32) brandelf -t FreeBSD $@ +$(call arch,x86.fbsd.64,${TMP}/$(mod).) + $(call yasm,elf64) + brandelf -t FreeBSD $@