Index: arch/makefile ================================================================== --- arch/makefile +++ arch/makefile @@ -1,13 +1,19 @@ ${TMP}: mkdir -p ${TMP} -${TMP}/calls.x86.lin.32.s: ${lin-headers}/unistd_32.h ${TMP} - grep "#define __NR_" $< | sed 's;^#define __NR_;%define sys.;' > $@ -${TMP}/calls.x86.lin.64.s: ${lin-headers}/unistd_64.h ${TMP} - grep "#define __NR_" $< | sed 's;^#define __NR_;%define sys.;' > $@ -${TMP}/calls.x86.fbsd.%.s: ${fbsd-headers}/syscall.h ${TMP} - grep "#define SYS_" $< | sed 's;^#define SYS_;%define sys.;' > $@ +${TMP}/calls.x86.lin.32.tbl: ${lin-headers}/unistd_32.h ${TMP} + grep "#define __NR_" $< | sed 's;^#define __NR_;;' > $@ +${TMP}/calls.x86.lin.64.tbl: ${lin-headers}/unistd_64.h ${TMP} + grep "#define __NR_" $< | sed 's;^#define __NR_;;' > $@ +${TMP}/calls.x86.fbsd.%.tbl: ${fbsd-headers}/syscall.h ${TMP} + grep "#define SYS_" $< | sed 's;^#define SYS_;;' | sed 's;[\t ]\+; ;' > $@ + +${TMP}/calls.s: ${TMP}/calls.${TARGET}.tbl + awk -f syscall.awk -v out=asm <$< >$@ +${TMP}/calls.h: ${TMP}/calls.${TARGET}.tbl + awk -f syscall.awk -v out=header <$< >$@ + ${TMP}/typesize: typesize.c $(CC) -std=c11 $< -o $@ ${TMP}/typesize.def: ${TMP}/typesize $< > $@ ADDED arch/posix.h Index: arch/posix.h ================================================================== --- arch/posix.h +++ arch/posix.h @@ -0,0 +1,37 @@ +/* arch/posix.h - posix constants + * ? this file defines posix magic numbers + * needed in syscalls, both cross-platform + * ones and os-dependent ones. note that + * the values may change depending on the + * OS specified! */ + +#include +#include + +enum posix_prot { + posix_prot_none = 0, + posix_prot_read = 1 << 0, + posix_prot_write = 1 << 1, + posix_prot_exec = 1 << 2 +}; + +enum posix_map { + posix_map_shared = 1, + posix_map_private = 2 +}; + +enum posix_flag { + posix_flag_fixed = 0x10, +#if KVos == KA_os_lin + posix_flag_anonymous = 0x20, +#elif KVos == KA_os_fbsd + posix_flag_anonymous = 0x1000, +#endif + + /* platform flags */ + posix_flag_linux_hugetlb = 0x40000 +}; + +struct kposix_syscall_result { long ret, error; } + +kposix_syscall(enum kposix_syscall syscall, sz argct, long args[]); ADDED arch/syscall.awk Index: arch/syscall.awk ================================================================== --- arch/syscall.awk +++ arch/syscall.awk @@ -0,0 +1,17 @@ +BEGIN { + if (out == "header") { + print "#ifndef KIplatform_syscalls" + print "#define KIplatform_syscalls" + print "enum /* syscall numbers */ {" + } +} + +out == "header" { print "\tk_platform_syscall_"$1" = "$2"," } +out == "asm" { print "%define sys."$1" "$2 } + +END { + if (out == "header") { + print "}" + print "#endif" + } +} Index: arch/typesize.c ================================================================== --- arch/typesize.c +++ arch/typesize.c @@ -26,10 +26,14 @@ if (!found_ofs && sizeof(type) == sizeof(ptrdiff_t)) \ found_ofs = 1, sflag(type_offset, "signed " #type); \ } int main() { + unsigned char etest[sizeof(int)] = {0xFF,0x00}; + if ((*(int*)etest) == 0xFF) sflag(prop_endian,"low"); + else sflag(prop_endian,"high"); + int found_sz = 0, found_ofs = 0, found_type = 0; iflag(arch_byte_bits,CHAR_BIT); describe_integral(char,char); describe_integral(short,short); describe_integral(int,int); Index: arch/x86.fbsd.32.s ================================================================== --- arch/x86.fbsd.32.s +++ arch/x86.fbsd.32.s @@ -1,10 +1,10 @@ ;; abi definition file for x86 linux 64-bit ; vim: ft=nasm ; syscall numbers - syscall table must be created first! -%include "calls.x86.fbsd.32.s" +%include "calls.s" ; extremely stupid freebsd-ism: expects the syscall to ; come from a function _syscall: int 0x80 ret Index: arch/x86.lin.32.s ================================================================== --- arch/x86.lin.32.s +++ arch/x86.lin.32.s @@ -1,10 +1,10 @@ ;; abi definition file for x86 linux 32-bit ; vim: ft=nasm ; syscall32 numbers - syscall table must be created first! -%include "calls.x86.lin.32.s" +%include "calls.s" ; syscall32 registers %define sys.reg.n 6 %define sys.reg.0 eax %define sys.reg.1 ebx Index: arch/x86.lin.64.s ================================================================== --- arch/x86.lin.64.s +++ arch/x86.lin.64.s @@ -1,9 +1,9 @@ ;; abi definition file for x86 linux 64-bit ; vim: ft=nasm ; syscall64 numbers - syscall table must be created first! -%include "calls.x86.lin.64.s" +%include "calls.s" ; linux uses the common x86-64 ABI %include "x86.syscall.64.s" ADDED kbuild/kbuild.md Index: kbuild/kbuild.md ================================================================== --- kbuild/kbuild.md +++ kbuild/kbuild.md @@ -0,0 +1,25 @@ +# kbuild + +**kbuild** is a small build tool designed to maximize the ease of writing portable libk applications. kbuild is included because most C compilers automatically assume their input should be linked with libc, inserting several implicit flags that need to be changed or removed for libk. while it is fully possible to build libk programs without using kbuild, it can be somewhat tedious, especially for code too small to warrant a makefile. + +kbuild is not intended to replace complex build systems like gmake, nor is it intended to be used only with libk binaries. + +unlike most build systems, kbuild does not use a makefile or project file. kbuild is invoked simply by passing it a list of C files, which it will build and link together. kbuild recursively calculates dependencies for each file, by parsing `#include` directives and kbuild-specific pragmas. + +kbuild passes a series of variables through to the preprocessor, providing information about the current system that can be useful for conditional compilation. + +these are the phases of the kbuild process: + + +# pragmas + +to support automatic linking, kbuild handles the following pragmas. + + * `#pragma k lib` adds a library name to the list of libraries to include. on linux, pkg-config will be used to locate the library and add the appropriate flags to the command line. link flags will be applied to the final binary, while all other flags will apply only to the compilation of the source unit; for this reason, library pragmas should precede `#include` directives. `#pragma k lib c` can be used to link the standard C library; if it is present, libk will not be linked unless it is explicitly named (`#pragma k lib k`). + * `#pragma k flag` indicates that a file should be compiled with a specific flag. this will be passed directly to the compiler, so it should be used within `#if` statements to detect the operating system and/or compiler being used. + * `#pragma k link` adds a flag to the final linker command line + * `#pragma k env` requests a specific environment variable be passed to the compiler with the -D flag; `#pragma k env EDITOR` will create a macro `KE_EDITOR` if `$EDITOR` is set in the user's environment. on encountering an `env` predicate for a variable it has not already passed, `kbuild` will terminate parsing and re-run cpp with the new variable set. this allows the use of environment variables to make first-pass decisions, though its effects may be somewhat unintuitive - a `#pragma k env` directive can affect the evaluation of a macro expression earlier in the same file. `#pragma k env` without an argument requests all host environment variables be passed through to the program. + +pragmas are scanned from the preprocessed file, meaning `#if` blocks can be used to control whether a library is linked or flag is passed. + +the `_Pragma` form is not currently supported, but supporting it is a long-term goal. ADDED kbuild/makefile Index: kbuild/makefile ================================================================== --- kbuild/makefile +++ kbuild/makefile @@ -0,0 +1,1 @@ +include ../modmake ADDED kcore/old/def.h.m Index: kcore/old/def.h.m ================================================================== --- kcore/old/def.h.m +++ kcore/old/def.h.m @@ -0,0 +1,36 @@ +--- 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 +#ifndef KIdef +#define KIdef + +[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]) + +#endif ADDED kcore/old/type.h.m Index: kcore/old/type.h.m ================================================================== --- kcore/old/type.h.m +++ kcore/old/type.h.m @@ -0,0 +1,182 @@ +--- 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 +#ifndef KItype +#define KItype + +/* we define 64-bit types first due to an oddity in how + * 128-bit types are handled: we want kc_?big to reference + * the absolute largest type available to the compiler, + * but in some cases, 128-bits may not be among the + * standard C types despite being supported by the + * compiler. to work around this, we first check whether + * 64-bit types are available (__int128_t only works on + * 64-bit systems) and then whether the compiler is one + * that supports the 128-bit extension - but only if a + * native 128-bit type is not available. + * + * once this is done, we can be certain that u128 will + * reference the largest available integer type and can + * safely define kc_?big by reference to it. */ + +[ifdef type_bit64] + typedef unsigned [type_bit64] u64; + typedef signed [type_bit64] s64; + [ifndef type_bit128] + /* even if no native type is 128 bits long, clang and + * gcc have extensions to support 128 bit arithmetic + * on 64-bit hardware */ +# if defined(__GNUC__) || defined(__clang__) + typedef unsigned __int128_t u128; + typedef signed __int128_t s128; +# else + /* if we don't have access to that extension + * or native 128-bit types, then we just use + * the largest native type specified in the + * C standard */ + typedef unsigned long long u128; + typedef signed long long s128; +# endif + [endif] +[else] + typedef unsigned long long u64; + typedef signed long long s64; + + typedef u64 u128; + typedef s64 s128; +[endif] + +[ifdef type_bit128] + typedef unsigned [type_bit128] u128; + typedef signed [type_bit128] s128; +[endif] + +typedef unsigned char ubyte; +typedef signed char sbyte; +typedef u128 ubig; +typedef s128 sbig; + +[ifdef type_bit8] + typedef unsigned [type_bit8] u8; + typedef signed [type_bit8] s8; +[else] + typedef ubyte u8; + typedef sbyte s8; +[endif] + +[ifdef type_bit16] + typedef unsigned [type_bit16] u16; + typedef signed [type_bit16] s16; +[else] + typedef ubig u16; + typedef sbig s16; +[endif] + +[ifdef type_bit32] + typedef unsigned [type_bit32] u32; + typedef signed [type_bit32] s32; +[else] + typedef ubig u32; + typedef sbig s32; +[endif] + +enum /* max-min values of each type */ { + byte_bits = [byte_bits], + + u8_min = 0, u8_max = ((u8)-1), + u16_min = 0, u16_max = ((u16)-1), + u32_min = 0, u32_max = ((u32)-1), + u64_min = 0, u32_max = ((u64)-1), + u128_min = 0, u128_max = ((u128)-1), + + /* assuming two's complement. TODO: check math */ + [define merge: $1$2] + [define [sspec type]: + [merge [type],_min] = 0 - ((1 << sizeof([type]) * kc_byte_bits) / 2), + [merge [type],_max] = (1 << sizeof([type]) * kc_byte_bits) / 2 - 1] + + [sspec s8], [sspec s16], [sspec s32], + [sspec s64], [sspec s128], + + kc_uchar_min = 0, kc_uchar_max = [type_max_u_char], + kc_ushort_min = 0, kc_ushort_max = [type_max_u_short], + kc_uint_min = 0, kc_uint_max = [type_max_u_int], + kc_ulong_min = 0, kc_ulong_max = [type_max_u_long], + kc_ullong_min = 0, kc_ullong_max = [type_max_u_llong], + + kc_schar_min = [type_min_s_char], kc_schar_max = [type_max_s_char], + kc_sshort_min = [type_min_s_short], kc_sshort_max = [type_max_s_short], + kc_sint_min = [type_min_s_int], kc_sint_max = [type_max_s_int], + kc_slong_min = [type_min_s_long], kc_slong_max = [type_max_s_long], + kc_sllong_min = [type_min_s_llong], kc_sllong_max = [type_max_s_llong], + + ubig_min = u128_min, ubig_max = u128_max, + sbig_min = s128_min, sbig_max = s128_max, + + ubyte_min = kc_uchar_min, ubyte_max = kc_uchar_max, + sbyte_min = kc_schar_min, sbyte_max = kc_schar_max, +}; + +[ifdef type_sz] + typedef [type_sz] sz; +[else] +# ifdef __cplusplus + /* C++ gives us a clean, standardized way to do this */ + typedef decltype (sizeof(char)) sz; +# else +# if defined(__GNUC__) || defined(__clang__) + typedef __typeof__ (sizeof(char)) sz; +# else + /* we're stumped - set sz to the safest possible value under + * the circumstances, and warn the user. */ +# warning no authoritative sz (size_t) type definition \ + available; defaulting to largest unsigned integer type + typedef ubig sz; +# endif +# endif +[endif] + +[ifdef type_offset] + typedef [type_offset] offset; +[else] +# ifdef __cplusplus + /* C++ gives us a clean, standardized way to do this */ + typedef decltype (((void*)-1) - 1) offset; +# else +# if defined(__GNUC__) || defined(__clang__) + typedef __typeof__ (((void*)10) - ((void*)5)) offset; +# else + /* no dice - set offset to the safest possible value under + * the circumstances, and warn the user. */ +# warning no authoritative offset (ptrdiff_t) type definition \ + available; defaulting to largest unsigned integer type + typedef sbig offset; +# endif +# endif +[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; + typedef u32 stat_long; +[else] + [if ([atom_target_os] == win) || + ([atom_target_os] == vms)] + typedef u32 stat; + [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 stat_long; +[endif] + +#endif Index: kcore/testbin.exe.c ================================================================== --- kcore/testbin.exe.c +++ kcore/testbin.exe.c @@ -19,17 +19,15 @@ if (kiosend(e.std, ptr, null) == kiocond_ok) { /* great, continue */ } else { return kbad_io; } - kmptr object = kmheapao(sizeof (struct object) * 16); - if (object.kind == kmkind_fail) return kbad_mem; + + void* region = kmheapa(2048); + if (region == null) return kbad_mem; + + kmzero(region,2048); - /* struct object* block = kmheapa(sizeof (struct object) * 4); */ - - struct object* block = object.ref; - block[5].a = 5; - - if (kmfree(object) != kmcond_ok) return kbad_mem; + if (kmheapf(region) >= kmcond_fail) return kbad_mem; return kbad_ok; } Index: kcore/type.h.m ================================================================== --- kcore/type.h.m +++ kcore/type.h.m @@ -81,10 +81,17 @@ typedef signed type_bit32 s32; ”,“ typedef ubig u32; typedef sbig s32; ”)dnl + +define(“cat”, “$1$2”) +typedef enum kcendian { + kcendian_high, + kcendian_low, + kcendian_system = cat(kcendian_,prop_endian), +} kcendian; enum /* max-min values of each type */ { byte_bits = arch_byte_bits, u8_min = 0, u8_max = ((u8)-1), ADDED kdbg/dbg.h Index: kdbg/dbg.h ================================================================== --- kdbg/dbg.h +++ kdbg/dbg.h @@ -0,0 +1,15 @@ +#ifndef KIdbg +#define KIdbg +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void kdbg_hexdump(kiochan, void*, sz); + +#ifdef __cplusplus +} +#endif +#endif ADDED kdbg/kdbg.md Index: kdbg/kdbg.md ================================================================== --- kdbg/kdbg.md +++ kdbg/kdbg.md @@ -0,0 +1,5 @@ +# kdbg +kdbg is a module offering convenience functions, useful primarily for debugging purposes. + +## functions + * `kdbg_hexdump(kiochan,void*,sz) → void` prints a canonical hexdump of a specified region of memory to the specified `kiochan`. ADDED kdbg/makefile Index: kdbg/makefile ================================================================== --- kdbg/makefile +++ kdbg/makefile @@ -0,0 +1,1 @@ +include ../modmake ADDED kmem/free.fn.c Index: kmem/free.fn.c ================================================================== --- kmem/free.fn.c +++ kmem/free.fn.c @@ -0,0 +1,22 @@ +#include +#include +#include +#include + +/* free.fn.c - kmfree() "arbitrary free" + * ~ lexi hale + * kmfree() frees memory allocated in any manner. + * it ignores non-dynamically allocated memory, + * returning kmcond_unnecessary. to check for + * success, compare result < kmcond_fail. + */ + +kmcond kmfree(kmptr ptr) { + if (ptr.kind <= kmkind_fail) return kmcond_unnecessary; + switch (ptr.kind) { + case kmkind_heap: return kmheapf(ptr.ref); + } + + return kmcond_unhandled; +} + ADDED kmem/heapao.fn.c Index: kmem/heapao.fn.c ================================================================== --- kmem/heapao.fn.c +++ kmem/heapao.fn.c @@ -0,0 +1,17 @@ +#include +#include +/* heapao.fn.c - kmheapao() "allocate heap object" + * ~ lexi hale + * kmheapao() allocates a region in heap memory + * and returns a kmptr struct referencing that + * newly allocated region. + */ + +kmptr kmheapao(sz size) { + void* ptr = kmheapa(size); + kmptr p = { + .kind = (ptr != null ? kmkind_heap : kmkind_fail), + .ref = ptr, + .shred = false, + }; return p; +} ADDED kmem/heapf.fn.c Index: kmem/heapf.fn.c ================================================================== --- kmem/heapf.fn.c +++ kmem/heapf.fn.c @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +/* heapf.c - kmheapf() "heap free" + * ~ lexi hale + * kmheapf() frees a region on the heap à la libc free() + * see also: kmheapa() "heap alloc" + */ + +/* 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, + * linker errors are our friend here! */ +extern int kmem_platform_munmap(void* addr, unsigned long sz); + +kmcond kmheapf(void* ptr) { + /* take an object allocated on the heap and free it, + * returning kmcond_ok on success or an appropriate + * value on failure. */ + struct kmbox* header = (kmbox*) + (((ubyte*)ptr) - sizeof (kmbox)); + sz const total = header -> size + sizeof (kmbox); + + if (header -> kind != kmkind_heap) return kmcond_mismatch; +# ifdef KFenv_posix + /* currently allocation is handled on posix by naive use + * of MAP_ANONYMOUS. munmap needs to be told the size of + * the region to unmap (free), which kmheapa() stores at + * (ptr - sizeof sz). see kmheap.c for details. */ + + if(kmem_platform_munmap(header, total) == -1) { + /* we don't need to bother recovering errno; + * there's only one possible munmap error */ + return kmcond_bad_address; + } + +# else + Knoimpl(kmheapa,KVos); +# error missing implementation +# endif + return kmcond_ok; +} ADDED kmem/platform.munmap.fn.x86.lin.64.s Index: kmem/platform.munmap.fn.x86.lin.64.s ================================================================== --- kmem/platform.munmap.fn.x86.lin.64.s +++ kmem/platform.munmap.fn.x86.lin.64.s @@ -0,0 +1,18 @@ +bits 64 +%include "../arch/x86.lin.64.s" +%include "../arch/x86.cdecl.64.s" +; vim: ft=nasm + +global kmem_platform_munmap +kmem_platform_munmap: + ; to call munmap, we need to translate the cdecl64 + ; register arguments to their appropriate syscall64 + ; registers. all those that matter are the same. + mov sys.reg.1, ccall.reg.0 ;nop - rdi → rdi + mov sys.reg.2, ccall.reg.1 ;nop - rsi → rsi + + mov sys.reg.0, sys.munmap + sys.call + + mov ccall.reg.ret, sys.reg.ret ; rax → rdi + ret ADDED kmem/zero.fn.c Index: kmem/zero.fn.c ================================================================== --- kmem/zero.fn.c +++ kmem/zero.fn.c @@ -0,0 +1,29 @@ +#include +#include +#include + +/* zero.fn.c - kmzero() "zero region" + * ~ lexi hale + * kmzero() zeroes a region of memory. it is + * libk's equivalent to libc's bzero(). + */ + +#define canzero(bits) (_canzero(sizeof(u##bits), bits, size)) +static bool _canzero(sz type, u8 bits, sz size) { + return (sizeof (u64) * byte_bits == 64 && size >= sizeof (u64)); +} + +void kmzero(void* memory, sz size) { + ubyte* elt = memory, + * const end = elt + size; + + if canzero(64) { + u64* p = (u64*) elt; + sz segments = (end - elt) / sizeof (u64); + + for(;(p - (u64*)elt) < segments; ++p) *p = 0; + elt = (ubyte*) p; + } + + for (;elt < end; ++elt) *elt = 0; +} ADDED kstr/ks_to_int.c Index: kstr/ks_to_int.c ================================================================== --- kstr/ks_to_int.c +++ kstr/ks_to_int.c @@ -0,0 +1,11 @@ +#include +#include +#include + +kscond ks_to_int(ksraw str, enum ksconv mode, u8* dest, sz size) { + u8 base = mode & 0xFF; + kcendian endian = (mode & ksconv_endh ? kcendian_high : + mode & ksconv_endl ? kcendian_low : + kcendian_system); + return kscond_unimplemented; +} Index: kstr/str.h ================================================================== --- kstr/str.h +++ kstr/str.h @@ -20,10 +20,41 @@ typedef struct ksmut { sz size; char* ptr; } ksmut; +typedef enum kscond { + kscond_ok, + kscond_fail, + kscond_unimplemented, + kscond_nonnumeric, +} kscond; + +enum ksconv { + ksconv_default = 0, + + ksconv_raw = 1, + ksconv_bin = 2, + ksconv_oct = 8, + ksconv_dec = 10, + ksconv_hex = 16, + ksconv_b32 = 32, + + ksconv_partial = 1 << 7, + ksconv_nopfx = 1 << 8, + ksconv_endh = 1 << 9, + ksconv_endl = 1 << 10, +}; + +kscond ks_to_int(ksraw str, + enum ksconv mode, + u8* dest, sz size); + +kscond ks_of_int(u8* number, sz size, + enum ksconv mode, + char* bufstart, sz bufsize); + #ifdef __cplusplus } #endif #endif Index: makefile ================================================================== --- makefile +++ makefile @@ -94,11 +94,11 @@ 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)/calls.h $(TMP)/calls.s $(OUT) cd $* && $(MAKE) obj %.tool: %/makefile $(OUT) cd $* && $(MAKE) tool @@ -106,15 +106,16 @@ cd $* && $(MAKE) dbg %.def: %/makefile $(TMP)/typesize.def $(OUT) $(OUT)/k cd $* && $(MAKE) def -%.calls: arch/makefile - cd arch && $(MAKE) $(TMP)/calls.$*.s +.PRECIOUS: $(TMP)/calls.% +$(TMP)/calls.%: arch/makefile + $(MAKE) -C arch $@ $(TMP)/typesize.def: arch/makefile $(TMP) - cd arch && $(MAKE) $@ + $(MAKE) -C arch $@ $(OUT)/libk.so: $(fnobjects) ld -shared $(COMPLIB) -o $@ $^ @# $(CC) -shared -fPIC -nostdlib $(COMPLIB) -o $@ $(OUT)/*.o