libk  Check-in [a14ceee056]

Overview
Comment:development milestone
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: a14ceee05626ca2753894004bbfa8747de98b37c29a0f92c83687fdd93e76db2
User & Date: lexi on 2019-06-27 21:39:17
Other Links: manifest | tags
Context
2019-06-27
22:14
fixes for shared building check-in: ec9b2b74b3 user: lexi tags: trunk
21:39
development milestone check-in: a14ceee056 user: lexi tags: trunk
12:21
more fixes check-in: 93014cedbc user: lexi tags: trunk
Changes

Modified arch/makefile from [657c34aa94] to [0ea82c6f52].

1





2
3
4
5


linux-headers = /usr/include/asm/





calls.x86.lin.32.s: $(linux-headers)/unistd_32.h
	grep "#define __NR_" $< | sed "s;^#define __NR_;%define sys.;" > $@
calls.x86.lin.64.s: $(linux-headers)/unistd_64.h
	grep "#define __NR_" $< | sed "s;^#define __NR_;%define sys.;" > $@


|
>
>
>
>
>
|
|
|
|
>
>
1
2
3
4
5
6
7
8
9
10
11
12
lin-headers = /usr/include/asm
fbsd-headers = /usr/include/sys

${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.;' > $@

Modified arch/x86.lin.64.s from [2b600a2718] to [f2e5b4d4ec].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
;; 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"

; syscall ops
%define sys.call syscall

; register order for syscall convention
%define sys.reg.n 7
%define sys.reg.ret rax
%define sys.reg.0 rax
%define sys.reg.1 rdi
%define sys.reg.2 rsi
%define sys.reg.3 rdx
%define sys.reg.4 r10
%define sys.reg.5 r8
%define sys.reg.6 r9

; register order for ccall convention
%define ccall.reg.ct 6
%define ccall.reg.ret rdi
%define ccall.reg.0 rdi
%define ccall.reg.1 rsi
%define ccall.reg.2 rdx
%define ccall.reg.3 rcx
%define ccall.reg.4 r8
%define ccall.reg.5 r9

%macro sys 1-8
; syscall64 wrapper, ex. `sys sys.write, 1, msg, msg.len`
	%assign i 0
	%rep %0
		mov sys.reg. %+ i, %1 ; i'm actually shocked this worked
		%rotate 1
		%assign i i+1
	%endrep 
	syscall
%endmacro

%macro sys.prep 1-8
	; for when we need to modify parameters before we
	; make the actual call.
	%assign i 0
	%rep %0
		mov sys.reg. %+ i, %1
		%rotate 1
		%assign i i+1
	%endrep
%endmacro

%macro ccall 1-*
	%if %0 > ccall.reg.ct
		%assign ct ccall.reg.ct
	%else
		%assign ct %0-1
	%endif
	%assign i 0
	%rotate 1
	%rep ct
		; if the function is well-behaved, all its arguments fit
		; in registers. if not, things get ugly. see below.
		mov ccall.reg. %+ i, %1
		%assign i i+1
		%rotate 1
	%endrep
	%if %0 > ccall.reg.ct
		; if there are more parameters to a C function than the
		; number of permitted registers, they must be pushed in
		; reverse order to the stack.
		; keep your function signatures under control, people.
		%assign ct (%0-ct)-1
		%rotate ct
		%rep ct
			%rotate -1
			push %1
		%endrep
		%rotate ct
		push rsp ; it's our responsibility to preserve the stack
	%endif
	call %1
	%if %0 > ccall.reg.ct
		; the extra arguments are still on the stack; time to
		; dump them back into the Garbage Zone
		pop rsp
	%endif
%endmacro






|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
1
2
3
4
5
6
7































8

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"

; linux uses the common x86-64 ABI































%include "x86.syscall.64.s"

















































Modified kcore/boot.c from [878278ef2e] to [6007ea2ce5].

1
2
3
4
5






6
7
8
#include "core.h"
extern stat entry(kenv);

stat _boot(unsigned int argc, char** argv) {
	kenv e = { argc, argv };






	return entry(e);
}
	
|



|
>
>
>
>
>
>



1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <k/core.h>
extern stat entry(kenv);

stat _boot(unsigned int argc, char** argv) {
	kenv e = { 
		// todo: determine terminal class and set term vs ansi correctly!
		{ {kiostream_term, 0}, {kiostream_term, 1} }, // chan std
		{ {kiostream_closed},  {kiostream_term, 2} }, // chan err
		argc, argv,
		null // no environment yet
	};
	return entry(e);
}
	

Modified kcore/core.h from [bb457bb91b] to [cb5067b74b].

1
2



3
4
5

6






7
8


9

10
11
12
#ifndef KIcore
#define KIcore




typedef unsigned long long sz;
typedef unsigned char stat;








typedef struct kenv {
	sz argc;


	char** argv;

} kenv;

#endif


>
>
>

<
<
>

>
>
>
>
>
>

<
>
>
|
>



1
2
3
4
5
6


7
8
9
10
11
12
13
14
15

16
17
18
19
20
21
22
#ifndef KIcore
#define KIcore
#include <k/type.h>
#include <k/io.h>
#include <k/str.h>



static void* const null = (void*)0;

typedef struct kvar {
	ksraw name;
	ksraw val;
	char* platform;
} kvar;

typedef struct kenv {

	kiochan std;
	kiochan err;
	sz argc; char** argv;
	kvar* vars;
} kenv;

#endif

Modified kcore/kcore.md from [28c0315020] to [d664110812].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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.

## 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
kcore contains fixed-width integer types. note that the available of each depends on your platform; compilation will fail if e.g. you try to use a u64 or a u128 on a 32-bit platform, so where exact lengths are not required, you may wish to use the built-in C types instead.

 * `u8` - an unsigned 8-bit integer
 * `s8` - a signed 8-bit integer
 * `u16` - an unsigned 16-bit integer
 * `s16` - a signed 16-bit integer
 * `u32` - an unsigned 32-bit integer
 * `s32` - a signed 32-bit integer
................................................................................
 * `sword` - a signed integer of platform word-length (e.g. 32 bits on x86.32; 64 on x86.64)
 * `stat` - the type of process return values expected by the platform (usually u8 on linux)

### struct kenv
`kenv` is a struct that encompasses the environment the program was launched in.
 * `kiochan std` - a stereo IO channel for reading and writing to and from stdout.
 * `kiochan err` - a mono IO channel for writing to stderr.
 * `kvar* env` - a pointer into the program's environment

### 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







|







 







|






1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 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.

## 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
kcore contains fixed-width integer types (in <k/type.h>). note that the availability of each depends on your platform; compilation will fail if e.g. you try to use a u64 or a u128 on a 32-bit platform, so where exact lengths are not required, you may wish to use the built-in C types instead.

 * `u8` - an unsigned 8-bit integer
 * `s8` - a signed 8-bit integer
 * `u16` - an unsigned 16-bit integer
 * `s16` - a signed 16-bit integer
 * `u32` - an unsigned 32-bit integer
 * `s32` - a signed 32-bit integer
................................................................................
 * `sword` - a signed integer of platform word-length (e.g. 32 bits on x86.32; 64 on x86.64)
 * `stat` - the type of process return values expected by the platform (usually u8 on linux)

### struct kenv
`kenv` is a struct that encompasses the environment the program was launched in.
 * `kiochan std` - a stereo IO channel for reading and writing to and from stdout.
 * `kiochan err` - a mono IO channel for writing to stderr.
 * `kvar* vars` - a pointer into the program's environment

### 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

Modified kcore/makefile from [f0df06fe05] to [a535931ecd].




















1




































include ../modmake

















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
## kcore/makefile
# kcore has to include, among other things, a replacement
# for stddef.h, and that can't be written in portable C,
# so we're generating it at build time.
#
# 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
# instead of makefile gibberish. hopefully tho this will be
# enough in the short term for libk to get going, enough that
# 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 def.$$*.i >> $$@
	cat $$< >> $$@
	echo '#endif' >> $$@
endef

$(eval $(call arch,x86,32))
$(eval $(call arch,x86,64))

Modified kgraft/exe.attach.c from [a7ffc6f8bf] to [572a900790].









>
>
>
>
1
2
3
4
#include <k/core.h>
stat entry(kenv e) {
	return 0;
}

Modified kio/io.h from [247977844e] to [824a86b217].

1
2







3










































4
#ifndef KIio
#define KIio


















































#endif


>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#ifndef KIio
#define KIio
/* <k/io.h>
 * ~ lexi hale <lexi@hale.su>
 * this header declares IO primitive functions and
 * structures. it is the same for all platforms.
 * platform-specific code is found in the *.platform.h
 * files.
 */

#include <k/type.h>

typedef enum kiostream_kind {
	kiostream_closed,
	  // this kiostream cannot be written to
	kiostream_file,
	  // this kiostream represents a file
	kiostream_sock,
	  // this kiostream is attached to a socket,
	  // UNIX, IP, or otherwise
	kiostream_term,
	  // this socket is being used to communicate
	  // directly with a human being
	kiostream_ansi,
	  // like kiostream_term, but can also understand
	  // ANSI control codes
	kiostream_other
	  // no fuckin idea
} kiostream_kind;

typedef struct kiostream {
	kiostream_kind kind;
	#include "kiostream.platform.h"
} kiostream;

typedef struct kiochan {
	kiostream in;
	  // text can be read from this stream
	kiostream out;
	  // text can be written to this stream
} kiochan;

unsigned long long kiosend(kiochan); // send data to a channel
unsigned long long kiorecv(kiochan); // receive data from a channel

typedef enum kiocond {
	kiocond_ok,
	  // success
    kiocond_fail,
	  // action failed
} kiocond;

#endif

Modified kio/makefile from [f0df06fe05] to [86c8f24ac7].



1











include ../modmake









>
>

>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
gen-headers = kiostream.platform.h

include ../modmake

ifeq (${POSIX},yes)
api = posix
else
api = ${OS}
endif

${OUT}/k/kiostream.platform.h: kiostream.$(api).i
	cp $< $@

Modified kmem/kmem.md from [9a7772ee1a] to [9ed4178122].

50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 
the convenience function `kmstat(void*) → kmptr` wraps a pointer to a static object in a `kmptr` struct.

### kmcell

`kmcell` is a stub struct used to disambiguate between source types.a "source" is an object that can hold an allocated object, such as the heap, a memory pool, a fixed-length array on stack, or a fixed-length global array. all values produced by a kmem allocation function point to within a `kmcell`.

 * `kmptr_kind kind` - kind of cell
 * `size_t sz` - kind of cell (data plus all fields)
 * `kmshred shred` - shredding flag

### kmref

`kmref` is a struct that constitutes the in-memory representation of a reference-counted cell.

 * `kmkind kind = kmkind_ref` - kind of cell







|
|







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 
the convenience function `kmstat(void*) → kmptr` wraps a pointer to a static object in a `kmptr` struct.

### kmcell

`kmcell` is a stub struct used to disambiguate between source types.a "source" is an object that can hold an allocated object, such as the heap, a memory pool, a fixed-length array on stack, or a fixed-length global array. all values produced by a kmem allocation function point to within a `kmcell`.

 * `kmkind kind` - kind of cell
 * `size_t size` - size of cell (data plus all fields)
 * `kmshred shred` - shredding flag

### kmref

`kmref` is a struct that constitutes the in-memory representation of a reference-counted cell.

 * `kmkind kind = kmkind_ref` - kind of cell

Modified kmem/mem.h from [dc631dc568] to [4d62b7403b].

1
2

3





























4
#ifndef KImem
#define KImem































#endif


>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#ifndef KImem
#define KImem
#include <k/type.h>

typedef enum kmkind {
	kmkind_none,
	kmkind_heap,
	kmkind_pool,
	kmkind_ref,
	kmkind_tree
} kmkind;

typedef enum kmshred {
	kmshred_yes,
	kmshred_no
} kmshred;

typedef struct kmcell {
	kmkind kind;
	sz size;
	kmshred shred;
	sz refs;
	struct kmcell* src;
	char data[];
} kmcell;

typedef struct kmptr {
	kmkind kind;
	kmshred shred;
	void* ref;
	kmcell* cell;
} kmptr;

#endif

Modified kstr/kstr.md from [8ba3da6088] to [b3e2b4c4d7].

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

**kstr** is the libk string library. it uses the **short** naming convention with the glyph `s`. **kstr** implies `#include <k/mem.h>`.

## types

### struct kstr
`struct kstr` is a structure for holding pascal strings (length-prefixed strings). it is the basic libk string type. **note:** if `ptr.ref` ≠ NULL and `sz` = 0, the string's length is unknown and should be calculated by any function that operates on a kstr, storing the result in the object if possible.
 * `size_t sz` - length of string, excluding any null terminator
 * `kmptr ptr` - pointer to string in memory

### struct ksraw
`struct ksraw` is like `kstr` except it uses raw `char` pointers instead of a `kmptr`.
 * `size_t sz` - length of string, excluding any null terminator
 * `char* ptr` - pointer to string in memory

### struct ksbuf
`struct ksbuf` is a structure used to hold buffers.
 * `size_t sz` - maximum size of buffer, including any null terminator
 * `char* buf` - region of memory to store buffer in
 * `ksalloc strat` - allocation strategy
 * `kmkind rule` - kind of allocator to use. only needs to be set if `where` is NULL. see [kmem](../kmem/kmem.md).
 * `kmcell* where` - where to allocate the object, in case of pool or tree allocation.

### struct kschain
`struct kschain` is a structure used for string accumulators that works by aggregating pointers to strings, instead of copying the strings themselves.
 * `kschain_kind kind` - kind of chain
 * `kmkind rule` - kind of allocation to use if `kind` ≠ `kschain_kind_linked`
 * `pstr* ptrs` - pointer to pointer list
 * `size_t ptrc` - number of pointers
 * `size_t sz` - total amount of space in `ptrs`

#### enum kschain_kind
 * `kschain_kind_block` - occupies a single block of memory
 * `kschain_kind_linked` - uses a linked list, allocated and deallocated as necessary

### enum ksalloc
`enum ksalloc` is an enumerator that tells libk what strategy to use when filling a `ksbuf` or `kschain` struct.
................................................................................
	char* src = <...>;
	kmbuf buf = { sizeof mem, &mem, kmkind_none };
    kstr chain[] = {
		Kstr("<a href=\""), { 0, src }, Kstr("\">"),
			ksref(text),
		Kstr("</a>")
	};
	char* html = kscomp([Kmsz(chain)](../kmem/kmem.md), chain, &buf);

kscomp will only calculate the length of individual strings if they are not already known. when it needs to calculate the length of a string, it will store that length in the original array so repeated calls can be made without needing to repeatedly calculate the lengths. this is not always desirable, so the variant `kscompc` exists, which is exactly the same as `kscomp` in every respect except that `chain` is not altered in any way.

### macros
if `KFclean` is not set when <k/str.h> is included, the following macros are defined.

 * `Kstr(string)` - the compile-time equivalent to `kstr()`. `Kstr` takes a literal string and inserts the text `{ sizeof (string), string }` into the document, suitable for initializing a kstr.







|




|




|










|
|







 







|







2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
..
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

**kstr** is the libk string library. it uses the **short** naming convention with the glyph `s`. **kstr** implies `#include <k/mem.h>`.

## types

### struct kstr
`struct kstr` is a structure for holding pascal strings (length-prefixed strings). it is the basic libk string type. **note:** if `ptr.ref` ≠ NULL and `sz` = 0, the string's length is unknown and should be calculated by any function that operates on a kstr, storing the result in the object if possible.
 * `sz size` - length of string, excluding any null terminator
 * `kmptr ptr` - pointer to string in memory

### struct ksraw
`struct ksraw` is like `kstr` except it uses raw `char` pointers instead of a `kmptr`.
 * `sz size` - length of string, excluding any null terminator
 * `char* ptr` - pointer to string in memory

### struct ksbuf
`struct ksbuf` is a structure used to hold buffers.
 * `sz size` - maximum size of buffer, including any null terminator
 * `char* buf` - region of memory to store buffer in
 * `ksalloc strat` - allocation strategy
 * `kmkind rule` - kind of allocator to use. only needs to be set if `where` is NULL. see [kmem](../kmem/kmem.md).
 * `kmcell* where` - where to allocate the object, in case of pool or tree allocation.

### struct kschain
`struct kschain` is a structure used for string accumulators that works by aggregating pointers to strings, instead of copying the strings themselves.
 * `kschain_kind kind` - kind of chain
 * `kmkind rule` - kind of allocation to use if `kind` ≠ `kschain_kind_linked`
 * `pstr* ptrs` - pointer to pointer list
 * `sz ptrc` - number of pointers
 * `sz size` - total amount of space in `ptrs`

#### enum kschain_kind
 * `kschain_kind_block` - occupies a single block of memory
 * `kschain_kind_linked` - uses a linked list, allocated and deallocated as necessary

### enum ksalloc
`enum ksalloc` is an enumerator that tells libk what strategy to use when filling a `ksbuf` or `kschain` struct.
................................................................................
	char* src = <...>;
	kmbuf buf = { sizeof mem, &mem, kmkind_none };
    kstr chain[] = {
		Kstr("<a href=\""), { 0, src }, Kstr("\">"),
			ksref(text),
		Kstr("</a>")
	};
	char* html = kscomp(Kmsz(chain), chain, &buf);

kscomp will only calculate the length of individual strings if they are not already known. when it needs to calculate the length of a string, it will store that length in the original array so repeated calls can be made without needing to repeatedly calculate the lengths. this is not always desirable, so the variant `kscompc` exists, which is exactly the same as `kscomp` in every respect except that `chain` is not altered in any way.

### macros
if `KFclean` is not set when <k/str.h> is included, the following macros are defined.

 * `Kstr(string)` - the compile-time equivalent to `kstr()`. `Kstr` takes a literal string and inserts the text `{ sizeof (string), string }` into the document, suitable for initializing a kstr.

Modified kstr/str.h from [21d6720beb] to [f7e947c0d1].

1
2
3











4
#ifndef KIstr
#define KIstr












#endif



>
>
>
>
>
>
>
>
>
>
>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#ifndef KIstr
#define KIstr

#include <k/mem.h>

typedef struct kstr {
	sz size;
	kmptr ptr;
} kstr;

typedef struct ksraw {
	sz size;
	char* ptr;
} ksraw;
#endif

Modified libk.md from [d834bc0082] to [523e766d88].

17
18
19
20
21
22
23



24
25
26

27
28
29
30
31
32
33
..
62
63
64
65
66
67
68

69
70
71
72
73
74
75
..
89
90
91
92
93
94
95


96
97
98
99
100
101
102
...
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
## goals

libk's goals are far-reaching, and suggestions are welcome. note however that libk is *not* intended to be a kitchen-sink library like libiberty. it's meant to do one thing, and to it well: to provide an easy- and pleasant-to-use foundation for modern open source projects. below is a list of some of the project's major goals.

 1. **IO.** libc's basic input/output mechanisms are dreadful, built at entirely the wrong level of abstraction. libk is intended to make many more primitives available to the user, and offer a sliding scale of abstraction so libk is suitable for a wide range of needs.
 2. **file manipulation.** libc's file manipulation primitives are a relic of a bygone age and in dire need of upgrading.
 3. **terminal manipulation.** libc has no provision for simple output formatting, a task that requires a combination of ANSI codes and in some cases pty manipulation with POSIX APIs, both of which are somewhat dark wizardry. this situation forces many innocent coders to drag in the entire unholy bulk of the aptly named library `ncurses`, much of whose code has been utterly obsolete for the last twenty years and whose API is one of the most singularly hateful ones in existence. libk therefore should offer a simple, straightforward way to do gracefully-degrading terminal sorcery.



 0. **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))
 0. **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.
 0. **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.


## 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.

................................................................................
 * FreeBSD: `fbsd`
 * NetBSD: `nbsd`
 * OpenBSD: `obsd`
 * Darwin/Mac OS X/iOS: `dar`
 * MS-DOS: `dos`
 * FreeDOS: `fdos`
 * Windows: `win`


#### file extensions

 * C function implementations: `*.c`
 * C module headers: `*.h`
 * ancillary C headers: `*.inc.h`
 * assembly code: `*.s`
................................................................................
libk uses only three languages: C (\*.c, \*.h), yasm (\*.s), and make (makefile).

other assemblers will probably be necessary for the more exotic targets, however.

## repository structure

libk uses a strict directory structure for code, and deviations from this structure will not be tolerated without extremely good reason.



all libk code is dispersed into modules: `kcore` for internals, `kio` for I/O, `kgraft` for binary packing, etc. each module has a folder in the root directory. (libk does not have submodules.) inside each module's directory should be a header with the same name as the module (see **naming conventions** above).

each function should be kept in a separate file within its module's directory. when OS or architecture-specific code is needed, the file's name should be a list of one or more of the fields [arch, OS, bits, format] separated by a `.` -- for instance, the 32-bit x86 haiku version of a function called `write` defined in assembly would be named `write.x86.hai.32.s`. however, if a function has an extraordinarily large number of versions, they may instead be stored in a folder with the same name as the function.

each module should have a header named the same thing as the module except without the `k` prefix. (e.g. the header for `kio` is `kio/io.h`) located in its folder. this is the header that the end-user will be importing, and should handle any user-defined flags to present the API the user has selected.

................................................................................

**libk.so** will build the dynamically linked form of libk, according to the build variables set

**libk.a** will build the statically linked form of libk, according to the build variables set

**tool** will build the executables used for modules such as `kgraft`.

there is no **clean** target. to clean the repository, simply delete the directory `out/`.

## authors

so far, this is a one-woman show. contributions are welcome however.

 * lexi hale <lexi@hale.su>








>
>
>
|
|
|
>







 







>







 







>
>







 







|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
..
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
...
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
## goals

libk's goals are far-reaching, and suggestions are welcome. note however that libk is *not* intended to be a kitchen-sink library like libiberty. it's meant to do one thing, and to it well: to provide an easy- and pleasant-to-use foundation for modern open source projects. below is a list of some of the project's major goals.

 1. **IO.** libc's basic input/output mechanisms are dreadful, built at entirely the wrong level of abstraction. libk is intended to make many more primitives available to the user, and offer a sliding scale of abstraction so libk is suitable for a wide range of needs.
 2. **file manipulation.** libc's file manipulation primitives are a relic of a bygone age and in dire need of upgrading.
 3. **terminal manipulation.** libc has no provision for simple output formatting, a task that requires a combination of ANSI codes and in some cases pty manipulation with POSIX APIs, both of which are somewhat dark wizardry. this situation forces many innocent coders to drag in the entire unholy bulk of the aptly named library `ncurses`, much of whose code has been utterly obsolete for the last twenty years and whose API is one of the most singularly hateful ones in existence. libk therefore should offer a simple, straightforward way to do gracefully-degrading terminal sorcery.
 4. **memory management.** the single memory management function `malloc()` provided by libc is absolutely pitiful. this is 2019. modern applications have much more exotic allocation needs, and a standard library should offer a range of allocators and management techniques, as well as abstract pointer objects so that pointers to objects of different allocation types (including static or stack allocation!) can be mixed freely and safely.
 5. **intrinsic reentrancy.** because *jesus christ,* libc.
 6. **interprocess communication.** libc offers no useful IPC abstractions over the paltry array of tools POSIX &co. give us to work with. we can do better.
 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.

## 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.

................................................................................
 * FreeBSD: `fbsd`
 * NetBSD: `nbsd`
 * OpenBSD: `obsd`
 * Darwin/Mac OS X/iOS: `dar`
 * MS-DOS: `dos`
 * FreeDOS: `fdos`
 * Windows: `win`
 * Windows MinGW: `mgw`

#### file extensions

 * C function implementations: `*.c`
 * C module headers: `*.h`
 * ancillary C headers: `*.inc.h`
 * assembly code: `*.s`
................................................................................
libk uses only three languages: C (\*.c, \*.h), yasm (\*.s), and make (makefile).

other assemblers will probably be necessary for the more exotic targets, however.

## repository structure

libk uses a strict directory structure for code, and deviations from this structure will not be tolerated without extremely good reason.

total segregation is maintained between source code, temporary files, and output objects. source is found in module directories (`k*/`). the destination for temporary files and output objects are retargetable via the `make` parameters `TMP= OUT=`, but default to `tmp/` and `out/`, which are excluded from repo with fossil's `ignore-glob` setting.

all libk code is dispersed into modules: `kcore` for internals, `kio` for I/O, `kgraft` for binary packing, etc. each module has a folder in the root directory. (libk does not have submodules.) inside each module's directory should be a header with the same name as the module (see **naming conventions** above).

each function should be kept in a separate file within its module's directory. when OS or architecture-specific code is needed, the file's name should be a list of one or more of the fields [arch, OS, bits, format] separated by a `.` -- for instance, the 32-bit x86 haiku version of a function called `write` defined in assembly would be named `write.x86.hai.32.s`. however, if a function has an extraordinarily large number of versions, they may instead be stored in a folder with the same name as the function.

each module should have a header named the same thing as the module except without the `k` prefix. (e.g. the header for `kio` is `kio/io.h`) located in its folder. this is the header that the end-user will be importing, and should handle any user-defined flags to present the API the user has selected.

................................................................................

**libk.so** will build the dynamically linked form of libk, according to the build variables set

**libk.a** will build the statically linked form of libk, according to the build variables set

**tool** will build the executables used for modules such as `kgraft`.

**clean** will delete the `tmp` and `out` trees.

## authors

so far, this is a one-woman show. contributions are welcome however.

 * lexi hale <lexi@hale.su>

Modified makefile from [0ee3372ddd] to [2300652a5e].

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15
16
17
18













19
20
21


22
23

24
25
26
27
28
29
30
31
32
33
34
35



36
37

38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
export OUT = $(PWD)/out

export ARCH = x86
export OS = lin
export BITS = 64


export TARGET = $(ARCH).$(OS).$(BITS)

moddirs = $(wildcard k*)
modules = $(moddirs:k%=%)
headers = $(moddirs:k%=$(OUT)/k/%.h)
objects = $(modules:%=$(OUT)/k%.o)
makefiles = $(moddirs:%=%/makefile)

binaries = $(wildcard */exe.*.c)
binmods = $(sort $(dir $(binaries)))

all: obj defs tool













obj: $(moddirs:%=%.obj)
defs: $(headers)
tool: $(binmods:%=%.tool)



lists = moddirs modules headers objects makefiles binaries binmods

dbg:
	@echo -e lists: $(foreach var, $(lists), "\\n - \\e[1m$(var)\\e[m = $($(var))")

%.obj: %/makefile ${TARGET}.calls $(OUT)
	cd $* && $(MAKE) obj

%.tool: %/makefile $(OUT)
	cd $* && $(MAKE) tool

%.dbg: %/makefile $(OUT)
	cd $* && $(MAKE) dbg




%.calls: arch/makefile
	cd arch && make calls.$*.s


$(OUT)/libk.so: mods $(OUT)
	$(CC) -shared -o $@ $(objects)

$(OUT)/libk.a: mods $(OUT)
	# using `ar c` and ranlib here instead of
	# `ar cs` in case `ar` isn't the GNU version
	ar c $@ $(objects)
	ranlib $@

$(OUT)/k/%.h: k%/makefile $(OUT)/k
	cd k$* && $(MAKE) $@

$(OUT) $(OUT)/k:
	mkdir -p $@





>




<
<
<
<
<



|
>
>
>
>
>
>
>
>
>
>
>
>
>

|
|
>
>

<
>












>
>
>

<
>

|
|

|
|
|
|


<
<
<


1
2
3
4
5
6
7
8
9
10





11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50

51
52
53
54
55
56
57
58
59
60
61



62
63
export OUT = $(PWD)/out

export ARCH = x86
export OS = lin
export BITS = 64
export TMP = $(PWD)/tmp

export TARGET = $(ARCH).$(OS).$(BITS)

moddirs = $(wildcard k*)





binaries = $(wildcard */exe.*.c)
binmods = $(sort $(dir $(binaries)))

posix-oses = lin fbsd dar and hai mgw

ifeq ($(findstring $(OS),$(posix-oses)),$(OS))
export POSIX = yes
else
export POSIX = no
endif

# include libgcc.a in gcc builds, just in case
ifeq ($(CC),gcc)
export COMPLIB = -lgcc
endif

all: defs obj tool
obj: $(moddirs:%=%.obj)
defs: $(moddirs:%=%.def)
tool: $(OUT)/libk.a $(binmods:%=%.tool) 
clean:
	rm -rf $(TMP) $(OUT)


lists = moddirs objects binaries binmods POSIX
dbg:
	@echo -e lists: $(foreach var, $(lists), "\\n - \\e[1m$(var)\\e[m = $($(var))")

%.obj: %/makefile ${TARGET}.calls $(OUT)
	cd $* && $(MAKE) obj

%.tool: %/makefile $(OUT)
	cd $* && $(MAKE) tool

%.dbg: %/makefile $(OUT)
	cd $* && $(MAKE) dbg

%.def: %/makefile $(OUT) $(OUT)/k
	cd $* && $(MAKE) def

%.calls: arch/makefile

	cd arch && $(MAKE) $(TMP)/calls.$*.s

$(OUT)/libk.so: obj $(OUT)
	$(CC) -shared -nostdlib $(COMPLIB) -o $@ $(OUT)/*.o

$(OUT)/libk.a: obj $(OUT)
	# using `ar rc` and ranlib here instead of
	# `ar rcs` in case `ar` isn't the GNU version
	ar rc $@ $(OUT)/*.o
	ranlib $@




$(OUT) $(OUT)/k:
	mkdir -p $@

Modified modmake from [e72fb78502] to [adae292a57].

2
3
4
5
6
7
8

9
10
11
12
13
14


15
16
17



18
19
20
21
22
23

24
25
26
27
28
29
30
31
32
33



34
35
36
37
38
39
40
..
43
44
45
46
47
48
49




50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 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)
bare = $(mod:k%=%)


tools    = $(filter     exe.%.c,   $(src))
nontools = $(filter-out exe.%.c,   $(src))
cobjects = $(filter     %.c,       $(nontools))
sobjects = $(filter %.${TARGET}.s, $(nontools))



obj: $(cobjects:%.c=${OUT}/$(mod).%.o) \
	 $(sobjects:%.s=${OUT}/$(mod).%.o)
tool: $(tools:exe.%.c=${OUT}/$(mod).%)




dbg:
	@echo tools = $(tools)
	@echo TARGET = ${TARGET}
	@echo cobjects = $(cobjects)
	@echo sobjects = $(sobjects)

	@echo mod = $(mod)

${OUT}/$(mod).%.o: %.c
	$(CC) -c $< -o $@

${OUT}/k/$(bare).h: $(bare).h
	cp $< $@

${OUT}/$(mod).%: exe.%.c
	$(CC) $< -o $@




#- assembly
# compiling  the assembly  code will  be  faster but  a lot  more
# complex, given the  nature of assembly and the  large number of
# platforms  targeted.  we need  to  add  build rules  for  every
# arch.OS[.bits] tuple;  since this  is a fairly  repetetive task
# that  requires ugly  make  rules,  we're just  going  to use  a
................................................................................
# ${OUT} = ultimate build directory
# $(mod) = module name
#      % = function name
#   $(1) = arch tuple
arch = ${OUT}/$(mod).%.$(1).o: %.$(1).s
# invoke with $(call arch,tuple). do not
# put spaces between either term though!





#-- 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 -gdwarf2 -felf32 $< -o $@

#${OUT}/$(mod).%.x86.lin.64.o: %.x86.lin.64.s
$(call arch,x86.lin.64)
	yasm -gdwarf2 -felf64 $< -o $@

#-- 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







>






>
>


|
>
>
>






>



|

|



|
>
>
>







 







>
>
>
>








|



|







2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# 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)
bare = $(mod:k%=%)
headers = $(wildcard *.h) $(gen-headers)

tools    = $(filter     exe.%.c,   $(src))
nontools = $(filter-out exe.%.c,   $(src))
cobjects = $(filter     %.c,       $(nontools))
sobjects = $(filter %.${TARGET}.s, $(nontools))

cflags = -isystem ${OUT} -nostdlib ${COMPLIB} -L${OUT} -lk

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 tools = $(tools)
	@echo TARGET = ${TARGET}
	@echo cobjects = $(cobjects)
	@echo sobjects = $(sobjects)
	@echo headers = $(headers)
	@echo mod = $(mod)

${OUT}/$(mod).%.o: %.c
	$(CC) $(cflags) -c $< -o $@

${OUT}/k/%.h: %.h
	cp $< $@

${OUT}/$(mod).%: exe.%.c
	$(CC) $(cflags) $< -o $@

${TMP}:
	mkdir -p ${TMP}

#- assembly
# compiling  the assembly  code will  be  faster but  a lot  more
# complex, given the  nature of assembly and the  large number of
# platforms  targeted.  we need  to  add  build rules  for  every
# arch.OS[.bits] tuple;  since this  is a fairly  repetetive task
# that  requires ugly  make  rules,  we're just  going  to use  a
................................................................................
# ${OUT} = ultimate build directory
# $(mod) = module name
#      % = function name
#   $(1) = arch tuple
arch = ${OUT}/$(mod).%.$(1).o: %.$(1).s
# invoke with $(call arch,tuple). do not
# put spaces between either term though!

ifeq ($(debug),yes)
yasm-flags = -gdwarf2
endif

#-- 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 $@

#${OUT}/$(mod).%.x86.lin.64.o: %.x86.lin.64.s
$(call arch,x86.lin.64)
	yasm $(yasm-flags) -felf64 -i${TMP} $< -o $@

#-- 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