Commit Diff


commit - 166c6b70ce597414f40e5a3e2ba33f1572c0b8a4
commit + d83e2c2bfa0da3e560fb2531756425b8fa7b00b1
blob - 2739ea77906d91f6c1a52c00b357948a4c7390ef
blob + d5a600d820bf688595bba5fe8d1422b4282f2199
--- .gitignore
+++ .gitignore
@@ -1,7 +1,16 @@
+tools/bin
+tools/build
+tools/include
+tools/lib
+tools/libexec
+tools/riscv64-unknown-linux-musl
+tools/share
+tools/src
 *.swp
 *.core
 *.o
 *.elf
+*.pdf
 rootfs
 syscalls.h
 rvemu
blob - b88ff85a5f3c5a6be87cde2b332b0bfcdf00d464
blob + dda54ef21b86d17b849a365a3e017cd10a0b0e91
--- Makefile
+++ Makefile
@@ -5,9 +5,10 @@
 SUDO	= doas
 PREFIX	= /usr/local
 
-CROSS	= riscv64-unknown-linux-musl
-CFLAGS	= -std=c2x -fPIC -O3
-LDFLAGS	= -s -pie -static -lpthread
+TARGET	= riscv64-unknown-linux-musl
+CROSS	= ./tools/bin/${TARGET}
+CFLAGS	= -std=c2x -fPIC -O0 -g
+LDFLAGS	= -pie -static -lpthread
 OBJ	= rvemu.o ecall.o cpu.o exec.o
 PROGS	= test.elf hello.elf
 
@@ -44,5 +45,8 @@ syscalls.h: syscalls.inc
 .S.o: syscalls.inc
 	${CROSS}-as -o $@ $<
 
+.c.elf: linker.ld
+	${CROSS}-gcc -o $@ $<
+
 .o.elf: linker.ld
 	${CROSS}-ld -s -T linker.ld -o $@ $<
blob - a8e4ca5cef165745638682cdc3cd92323f4b09ea
blob + 01a083c40878b0008d9c60680e2858af82eab131
--- cpu.c
+++ cpu.c
@@ -27,6 +27,7 @@ static i64 extend (i32 x)
 	return x;
 }
 
+#define log(fmt, ...) eprintf ("%08llx: " fmt "\n", pc - 4, __VA_ARGS__)
 void cpu_exec (u32 instr)
 {
 	const char *name;
@@ -35,6 +36,7 @@ void cpu_exec (u32 instr)
 	const u8 rs1 = (instr >> 15) & 0x1f;
 	const u8 rs2 = (instr >> 20) & 0x1f;
 	const u8 funct7 = instr >> 25;
+	const u8 funct6 = instr >> 26;
 	const u64 imm_i = extend ((i32)instr >> 20);
 	const u64 imm_s = extend (((i32)instr >> 25 << 5) | ((instr >> 7) & 0x1f));
 	const u64 imm_b = extend (((i32)instr >> 31 << 12)
@@ -52,22 +54,27 @@ void cpu_exec (u32 instr)
 	u64 a, b, c;
 	switch (instr & 0x7f) {
 	case 0b0110111: // lui rd, uimm
-		eprintf ("lui x%u, %u\n", (uint)rd, imm_u);
+		log ("lui x%u, %llu", (uint)rd, imm_u);
 		cpu_set (rd, imm_u);
 		break;
 	case 0b0010111: // auipc rd, uimm
-		eprintf ("auipc x%u, %u\n", (uint)rd, imm_u);
+		log ("auipc x%u, %llu", (uint)rd, imm_u);
 		cpu_set (rd, pc + imm_u - 4);
 		break;
 	case 0b1101111: // jal rd, jimm
-		eprintf ("jal x%u, %d\n", (uint)rd, (int)imm_j);
+		log ("jal x%u, %lld", (uint)rd, imm_j);
 		cpu_set (rd, pc);
 		pc += imm_j - 4;
 		break;
 	case 0b1100111: // jalr rd, rs1, iimm
 		switch (funct3) {
 		case 0b000:
-			eprintf ("jalr x%u, x%u, %d\n", (uint)rd, (uint)rs1, (int)imm_i);
+			// XXX: Fix broken musl
+			if (cpu_get (rs1) == 0) {
+				warnx ("%08llx: tried to jump to address 0!", pc - 4);
+				return;
+			}
+			log ("jalr x%u, x%u, %lld", (uint)rd, (uint)rs1, imm_i);
 			cpu_set (rd, pc);
 			pc = cpu_get (rs1);
 			break;
@@ -105,7 +112,7 @@ void cpu_exec (u32 instr)
 		default:
 			goto ud;
 		}
-		eprintf ("%s x%u, x%u, %d\n", name, (uint)rs1, (uint)rs2, (int)imm_b);
+		log ("%s x%u, x%u, %d", name, (uint)rs1, (uint)rs2, (int)imm_b);
 		if (c)
 			pc += imm_b - 4;
 		break;
@@ -143,7 +150,7 @@ void cpu_exec (u32 instr)
 		default:
 			goto ud;
 		}
-		eprintf ("%s x%u, %d(x%u)\n", name, (uint)rd, (int)imm_i, (uint)rs1);
+		log ("%s x%u, %d(x%u)", name, (uint)rd, (int)imm_i, (uint)rs1);
 		cpu_set (rd, b);
 		break;
 	case 0b0100011: // sx rs2, simm(rs1)
@@ -169,7 +176,7 @@ void cpu_exec (u32 instr)
 		default:
 			goto ud;
 		}
-		eprintf ("%s x%u, %d(x%u)\n", name, (uint)rs2, (int)imm_s, (uint)rs1);
+		log ("%s x%u, %d(x%u)", name, (uint)rs2, (int)imm_s, (uint)rs1);
 		break;
 	case 0b0010011: // alui rd, rs1, iimm
 		a = cpu_get (rs1);
@@ -180,7 +187,7 @@ void cpu_exec (u32 instr)
 			c = a + b;
 			break;
 		case 0b001: // slli
-			if (funct7 != 0)
+			if (funct6 != 0)
 				goto ud;
 			name = "slli";
 			c = a << shamt;
@@ -198,12 +205,12 @@ void cpu_exec (u32 instr)
 			c = a ^ b;
 			break;
 		case 0b101: // srli/srai
-			switch (funct7) {
-			case 0b0000000:
+			switch (funct6) {
+			case 0b000000:
 				name = "srli";
 				c = a >> shamt;
 				break;
-			case 0b0100000:
+			case 0b010000:
 				name = "srai";
 				c = (i64)a >> shamt;
 				break;
@@ -222,7 +229,7 @@ void cpu_exec (u32 instr)
 		default:
 			goto ud;
 		}
-		eprintf ("%s x%u, x%u, %lld\n", name, (uint)rd, (uint)rs1, (i64)imm_i);
+		log ("%s x%u, x%u, %lld", name, (uint)rd, (uint)rs1, (i64)imm_i);
 		cpu_set (rd, c);
 		break;
 	case 0b0011011: // aluiw rd, rs1, iimm
@@ -260,59 +267,76 @@ void cpu_exec (u32 instr)
 		}
 		c &= 0x00000000ffffffff;
 		c = extend (c);
-		eprintf ("%s x%u, x%u, %lld\n", name, (uint)rd, (uint)rs1, (i64)imm_i);
+		log ("%s x%u, x%u, %lld", name, (uint)rd, (uint)rs1, (i64)imm_i);
 		cpu_set (rd, c);
 		break;
 	case 0b0110011: // alu rd, rs1, rs2
 		a = cpu_get (rs1);
 		b = cpu_get (rs2);
-		c = (instr >> 30) & 1;
-		switch (funct3) {
-		case 0b000: // add/sub
-			if (c) {
-				name = "sub";
-				a -= b;
-			} else {
-				name = "add";
-				a += b;
-			}
+		switch (funct10) {
+		case 0b0000000'000:
+			name = "add";
+			c = a + b;
 			break;
-		case 0b001: // sll
+		case 0b0000001'000:
+			name = "mul";
+			c = a * b;
+			break;
+		case 0b0100000'000:
+			name = "sub";
+			c = a - b;
+			break;
+		case 0b0000000'001:
 			name = "sll";
-			a <<= b;
+			c = a << b;
 			break;
-		case 0b010: // slt
+		case 0b0000000'010:
 			name = "slt";
-			a = (i64)a < (i64)b;
+			c = (i64)a < (i64)b;
 			break;
-		case 0b011: // sltu
+		case 0b0000000'011:
 			name = "sltu";
-			a = a < b;
-			break;
-		case 0b100: // xor
+			c = a < b;
+			break;
+		case 0b0000000'100:
 			name = "xor";
-			a ^= b;
+			c = a ^ b;
 			break;
-		case 0b101: // srl/sra
-			if (c) {
-				name = "sra";
-				a = (i64)a >> (b & 0x3f);
-			} else {
-				name = "srl";
-				a >>= (b & 0x3f);
-			}
+		case 0b0000001'100:
+			name = "div";
+			c = b != 0 ? (i64)a / (i64)b : -1;
 			break;
-		case 0b110: // or
+		case 0b0000000'101:
+			name = "srl";
+			c = a >> (b & 0x3f);
+			break;
+		case 0b0000001'101:
+			name = "divu";
+			c = b != 0 ? a / b : -1;
+			break;
+		case 0b0100000'101:
+			name = "sra";
+			c = (i64)a >> (b & 0x3f);
+			break;
+		case 0b0000000'110:
 			name = "or";
-			a |= b;
+			c = a | b;
 			break;
-		case 0b111: // and
+		case 0b0000001'110:
+			name = "rem";
+			c = b != 0 ? (i64)a % (i64)b : -1;
+			break;
+		case 0b0000000'111:
 			name = "and";
-			a &= b;
+			c = a & b;
 			break;
+		case 0b0000001'111:
+			name = "remu";
+			c = b != 0 ? a % b : -1;
+			break;
 		}
-		eprintf ("%s x%u, x%u, x%u\n", name, (uint)rd, (uint)rs1, (uint)rs2);
-		cpu_set (rd, a);
+		log ("%s x%u, x%u, x%u", name, (uint)rd, (uint)rs1, (uint)rs2);
+		cpu_set (rd, c);
 		break;
 	case 0b0111011: // aluw
 		a = cpu_get (rs1);
@@ -341,15 +365,15 @@ void cpu_exec (u32 instr)
 		default:
 			goto ud;
 		}
-		eprintf ("%s x%u, x%u, x%u\n", name, (uint)rd, (uint)rs1, (uint)rs2);
+		log ("%s x%u, x%u, x%u", name, (uint)rd, (uint)rs1, (uint)rs2);
 		cpu_set (rd, extend ((i32)a));
 		break;
 	case 0b0001111: // fence/fence.tso/pause
-		eprintf ("fence\n");
+		eprintf ("%08llx: efence\n", pc - 4);
 		break;
 	case 0b1110011: // ecall/ebreak
-		eprintf (
-			"ecall a0=%u, a1=%u, a2=%u, a3=%u, a4=%u, a5=%u, a7=%u\n",
+		log (
+			"ecall a0=%llu, a1=%llu, a2=%llu, a3=%llu, a4=%llu, a5=%llu, a7=%llu",
 			cpu_get (10),
 			cpu_get (11),
 			cpu_get (12),
blob - f2631d600322b6a3bc1d74865ef5cd3f54fe743a
blob + d9d65bf68d10b5d9c1590f6519a0c84c3eceaed5
--- ecall.c
+++ ecall.c
@@ -391,7 +391,8 @@ void ecall (void)
 		ret = enosys ("waitid");
 		break;
 	case SYS_set_tid_address:
-		ret = enosys ("set_tid_address");
+		warnx ("set_tid_address(): not implemented");
+		ret = getpid ();
 		break;
 	case SYS_unshare:
 		ret = enosys ("unshare");
@@ -726,6 +727,7 @@ void ecall (void)
 		ret = map (my_execve (str (a0), ptr (char *, a1), ptr (char *, a2)));
 		break;
 	case SYS_mmap:
+		//eprintf ("mmap (%p, %zu, %d, %d, %d, %lld);\n", ptr (void, a0), (size_t)a1, (int)a2, (int)a3, (int)a4, (off_t)a5);
 		ptr = mmap (ptr (void, a0), (size_t)a1, (int)a2, (int)a3, (int)a4, (off_t)a5);
 		if (ptr == NULL) {
 			ret = -map_errno (errno);
blob - f5eebd803603f22e5415e2c2dbd56c756d929182
blob + 016725c65ba8bd0ec68749e033a54b366ba424b9
--- rvemu.c
+++ rvemu.c
@@ -9,8 +9,52 @@
 #include <errno.h>
 #include "rvemu.h"
 
-u64 brkval;
+u64 brkval = 0;
 
+static void load_segment (int fd, Elf64_Phdr phdr)
+{
+	size_t end, len, ps;
+	int prot = 0;
+	void *addr, *ptr;
+
+
+	if (phdr.p_filesz > phdr.p_memsz)
+		errx (1, "invalid program header: p_filesz > p_memsz");
+
+	ps = getpagesize ();
+	addr = (void *)(phdr.p_vaddr & ~(ps - 1));
+	len = phdr.p_memsz + phdr.p_vaddr - (size_t)addr;
+
+	ptr = mmap (
+		addr,
+		len,
+		PROT_READ | PROT_WRITE,
+		MAP_PRIVATE | MAP_ANON,
+		-1,
+		0
+	);
+	fprintf (stderr, "mmap (%p, %#zx) = %p;\n", addr, len, ptr);
+
+	if (ptr == NULL)
+		err (1, "mmap()");
+
+	if (phdr.p_filesz > 0) {
+		lseek (fd, phdr.p_offset, SEEK_SET);
+		read (fd, ptr, phdr.p_filesz);
+	}
+
+	if (phdr.p_flags & (PF_R | PF_X))
+		prot |= PROT_READ;
+	if (phdr.p_flags & PF_W)
+		prot |= PROT_WRITE;
+	if (mprotect (addr, len, prot) != 0)
+		err (1, "mprotect()");
+
+	end = phdr.p_vaddr + phdr.p_memsz;
+	if (end > brkval)
+		brkval = end;
+}
+
 static void load_image (const char *filename)
 {
 	Elf64_Ehdr ehdr;
@@ -30,16 +74,7 @@ static void load_image (const char *filename)
 
 	for (unsigned i = 0; i < ehdr.e_phnum; ++i) {
 		Elf64_Phdr phdr;
-		u32 flags = 0, end;
-		void *ptr;
 
-		if (phdr.p_flags & PF_R)
-			flags |= PROT_READ;
-		if (phdr.p_flags & PF_W)
-			flags |= PROT_WRITE;
-		if (phdr.p_flags & PF_X)
-			flags |= PROT_EXEC;
-
 		lseek (fd, ehdr.e_phoff + i * ehdr.e_phentsize, SEEK_SET);
 		if (read (fd, &phdr, ehdr.e_phentsize) != ehdr.e_phentsize)
 			err (1, "failed to read program header %u", i);
@@ -48,37 +83,7 @@ static void load_image (const char *filename)
 		case PT_NULL:
 			break;
 		case PT_LOAD:
-			eprintf ("loading %016llx into %016llx flags %x memsz %u\n",
-				phdr.p_offset, phdr.p_vaddr, phdr.p_flags, phdr.p_memsz);
-			if (phdr.p_filesz == phdr.p_memsz) {
-				ptr = mmap (
-					(void *)phdr.p_vaddr,
-					phdr.p_memsz,
-					flags,
-					MAP_PRIVATE | MAP_COPY,
-					fd,
-					phdr.p_offset
-				);
-			} else if (phdr.p_filesz == 0) {
-				ptr = mmap (
-					(void *)phdr.p_vaddr,
-					phdr.p_memsz,
-					flags,
-					MAP_ANON | MAP_PRIVATE,
-					-1,
-					0
-				);
-			} else {
-				errx (1, "%u: p_filesz: %llu, p_memsz: %llu",
-					i, phdr.p_filesz, phdr.p_memsz);
-			}
-
-			if ((u64)ptr != phdr.p_vaddr)
-				err (1, "%u: failed to map program header", i);
-
-			end = (((u64)ptr + phdr.p_memsz) + 4095) & ~0x3ff;
-			if (end > brkval)
-				brkval = end;
+			load_segment (fd, phdr);
 			break;
 		case PT_INTERP:
 		case PT_DYNAMIC:
@@ -124,11 +129,25 @@ static void setup_stack (
 	if (ptr != stack_bottom)
 		err (1, "failed to map stack");
 
+	// auxv
 	cpu_push (0);
+	cpu_push (0);
+	cpu_push (getegid ());
+	cpu_push (14);
+	cpu_push (getgid ());
+	cpu_push (13);
+	cpu_push (geteuid ());
+	cpu_push (12);
+	cpu_push (getuid ());
+	cpu_push (11);
+	cpu_push (getpagesize ());
+	cpu_push (6);
+
+	cpu_push (0);
 	for (int i = envc - 1; i >= 0; --i)
 		cpu_push ((u64)envp[i]);
 	cpu_push (0);
-	for (int i = argc; i >= 0; --i)
+	for (int i = argc - 1; i >= 0; --i)
 		cpu_push ((u64)argv[i]);
 	cpu_push (argc);
 }
@@ -142,8 +161,6 @@ int main (int argc, char *argv[], char *envp[])
 	char *base;
 	void *ptr;
 
-	eprintf ("&main = %p\n", &main);
-
 	base = basename (argv[0]);
 
 	if (strcmp (base, "rvemu") == 0) {
blob - 85a84552dd1982a85a006dc9c3b5193fa5b25c11 (mode 644)
blob + /dev/null
--- test.S
+++ /dev/null
@@ -1,103 +0,0 @@
-.include "syscalls.inc"
-
-.section .data
-parent:	.string "Parent\n"
-child:	.string "Child\n"
-arg0:
-path:	.string "./hello.elf"
-argv:
-	.dword arg0
-	.dword 0
-
-envp:
-	.dword 0
-
-.section .text
-.global _start
-.type _start, %function
-_start:
-	addi sp, sp, -4
-
-	li a0, 17
-	li a1, 0
-	li a7, SYS_clone
-	ecall
-
-	beq a0, zero, .Lchild
-
-	li a0, 1
-	la a1, parent
-	li a2, 7
-	li a7, SYS_write
-	ecall
-
-	la a0, path
-	la a1, argv
-	la a2, envp
-	li a7, SYS_execve
-	ecall
-
-	jal printhex
-
-	li a0, 0
-	li a7, SYS_exit
-	ecall
-
-.Lchild:
-	li a0, 1
-	la a1, child
-	li a2, 6
-	li a7, SYS_write
-	ecall
-
-	li a0, 0
-	li a7, SYS_exit
-	ecall
-
-# printhex(u64 a0)
-printhex:
-	addi sp, sp, -12
-	sw s0, 8(sp)
-	sw s1, 4(sp)
-	li s0, 52
-	mv s1, a0
-
-.Loop:
-	srl t0, s1, s0
-	andi t0, t0, 0xf
-
-	li t1, 10
-	blt t0, t1, .Ldec
-	addi t0, t0, 55
-	j .Lprint
-
-.Ldec:
-	addi t0, t0, 48
-
-.Lprint:
-	sb t0, 0(sp)
-	li a0, 1
-	mv a1, sp
-	li a2, 2
-	li a7, 64
-	ecall
-
-	beq s0, zero, .Lret
-	addi s0, s0, -4
-	j .Loop
-
-.Lret:
-	li t0, 10
-	sw t0, 0(sp)
-	li a0, 1
-	mv a1, sp
-	li a2, 1
-	ecall
-
-	lw s1, 4(sp)
-	lw s0, 8(sp)
-	addi sp, sp, 12
-	ret
-
-
-
blob - /dev/null
blob + e61a4cd6fefd3e020aa5e35cc33c1a32f776730d (mode 644)
--- /dev/null
+++ test.c
@@ -0,0 +1,8 @@
+#include <unistd.h>
+#include <string.h>
+
+int main (void) {
+	const char str[] = "Hello, World\n";
+	write (1, str, sizeof (str) - 1);
+	return 0;
+}
blob - /dev/null
blob + 85a84552dd1982a85a006dc9c3b5193fa5b25c11 (mode 644)
--- /dev/null
+++ test2.S
@@ -0,0 +1,103 @@
+.include "syscalls.inc"
+
+.section .data
+parent:	.string "Parent\n"
+child:	.string "Child\n"
+arg0:
+path:	.string "./hello.elf"
+argv:
+	.dword arg0
+	.dword 0
+
+envp:
+	.dword 0
+
+.section .text
+.global _start
+.type _start, %function
+_start:
+	addi sp, sp, -4
+
+	li a0, 17
+	li a1, 0
+	li a7, SYS_clone
+	ecall
+
+	beq a0, zero, .Lchild
+
+	li a0, 1
+	la a1, parent
+	li a2, 7
+	li a7, SYS_write
+	ecall
+
+	la a0, path
+	la a1, argv
+	la a2, envp
+	li a7, SYS_execve
+	ecall
+
+	jal printhex
+
+	li a0, 0
+	li a7, SYS_exit
+	ecall
+
+.Lchild:
+	li a0, 1
+	la a1, child
+	li a2, 6
+	li a7, SYS_write
+	ecall
+
+	li a0, 0
+	li a7, SYS_exit
+	ecall
+
+# printhex(u64 a0)
+printhex:
+	addi sp, sp, -12
+	sw s0, 8(sp)
+	sw s1, 4(sp)
+	li s0, 52
+	mv s1, a0
+
+.Loop:
+	srl t0, s1, s0
+	andi t0, t0, 0xf
+
+	li t1, 10
+	blt t0, t1, .Ldec
+	addi t0, t0, 55
+	j .Lprint
+
+.Ldec:
+	addi t0, t0, 48
+
+.Lprint:
+	sb t0, 0(sp)
+	li a0, 1
+	mv a1, sp
+	li a2, 2
+	li a7, 64
+	ecall
+
+	beq s0, zero, .Lret
+	addi s0, s0, -4
+	j .Loop
+
+.Lret:
+	li t0, 10
+	sw t0, 0(sp)
+	li a0, 1
+	mv a1, sp
+	li a2, 1
+	ecall
+
+	lw s1, 4(sp)
+	lw s0, 8(sp)
+	addi sp, sp, 12
+	ret
+
+
+
blob - /dev/null
blob + 7c00c653c85d2f7a68c6ddec2ec894995f887fc2 (mode 644)
--- /dev/null
+++ tools/Makefile
@@ -0,0 +1,216 @@
+.POSIX:
+
+TOP != pwd
+STAMPS = ${TOP}/build/.stamps
+PREFIX = ${TOP}
+TARGET = riscv64-unknown-linux-musl
+ARCH = rv64ima
+
+BINUTILS_VER = 2.42
+GCC_VER = 13.2.0
+MUSL_VER = 1.2.4
+LINUX_VER = 6.6.17
+LINUX_MAJOR != echo ${LINUX_VER} | cut -d. -f1
+GMP_VER = 6.3.0
+MPFR_VER = 4.2.1
+MPC_VER = 1.3.1
+
+GMAKE = gmake -j8
+#SUDO = doas
+
+all: build
+
+build: ${STAMPS}/libgcc-install
+
+clean:
+	rm -rf bin build include lib libexec ${TARGET} share
+
+test:
+	${PREFIX}/bin/${TARGET}-gcc -o /dev/null test.c
+
+download: src/binutils.tgz src/gcc.tgz src/musl.tgz src/linux.tgz src/gmp.tgz src/mpfr.tgz src/mpc.tgz
+extract: ${STAMPS}/binutils-extract ${STAMPS}/gcc-extract ${STAMPS}/musl-extract ${STAMPS}/linux-extract ${STAMPS}/gmp ${STAMPS}/mpfr ${STAMPS}/mpc
+
+headers: ${STAMPS}/linux-headers
+install-headers: ${STAMPS}/linux-hdrinst
+binutils: ${STAMPS}/binutils-build
+install-binutils: ${STAMPS}/binutils-install
+
+# DOWNLOAD
+
+src/binutils.tgz:
+	mkdir -p src
+	ftp -o $@ https://ftp.gnu.org/gnu/binutils/binutils-${BINUTILS_VER}.tar.gz
+
+src/gcc.tgz:
+	mkdir -p src
+	ftp -o $@ https://ftp.gnu.org/gnu/gcc/gcc-${GCC_VER}/gcc-${GCC_VER}.tar.gz
+
+src/musl.tgz:
+	mkdir -p src
+	ftp -o $@ http://musl.libc.org/releases/musl-${MUSL_VER}.tar.gz
+
+src/linux.tgz:
+	mkdir -p src
+	ftp -o $@ https://mirrors.edge.kernel.org/pub/linux/kernel/v${LINUX_MAJOR}.x/linux-${LINUX_VER}.tar.gz
+
+src/gmp.tgz:
+	mkdir -p src
+	ftp -o $@ https://gmplib.org/download/gmp/gmp-${GMP_VER}.tar.gz
+
+src/mpfr.tgz:
+	mkdir -p src
+	ftp -o $@ https://www.mpfr.org/mpfr-current/mpfr-${MPFR_VER}.tar.gz
+
+src/mpc.tgz:
+	mkdir -p src
+	ftp -o $@ https://ftp.gnu.org/gnu/mpc/mpc-${MPC_VER}.tar.gz
+
+# EXTRACT
+
+${STAMPS}/binutils-extract: src/binutils.tgz
+	mkdir -p ${STAMPS}
+	rm -rf build/binutils
+	tar -C build -xzf src/binutils.tgz
+	mv build/binutils-* build/binutils
+	touch $@
+
+${STAMPS}/gcc-extract: src/gcc.tgz
+	mkdir -p ${STAMPS}
+	rm -rf build/gcc
+	tar -C build -xzf src/gcc.tgz
+	mv build/gcc-* build/gcc
+	cd build/gcc && ln -sf ../gmp ../mpfr ../mpc .
+	touch $@
+
+${STAMPS}/musl-extract: src/musl.tgz
+	mkdir -p ${STAMPS}
+	rm -rf build/musl
+	tar -C build -xzf src/musl.tgz
+	mv build/musl-* build/musl
+	touch $@
+
+${STAMPS}/linux-extract: src/linux.tgz
+	mkdir -p ${STAMPS}
+	rm -rf build/linux
+	tar -C build -xzf src/linux.tgz
+	mv build/linux-* build/linux
+	sed -i 's/sed/gsed/g' build/linux/scripts/headers_install.sh
+	touch $@
+
+${STAMPS}/gmp: src/gmp.tgz
+	mkdir -p ${STAMPS}
+	tar -C build -xzf src/gmp.tgz
+	mv build/gmp-* build/gmp
+	touch $@
+
+${STAMPS}/mpfr: src/mpfr.tgz
+	mkdir -p ${STAMPS}
+	tar -C build -xzf src/mpfr.tgz
+	mv build/mpfr-* build/mpfr
+	touch $@
+
+${STAMPS}/mpc: src/mpc.tgz
+	mkdir -p ${STAMPS}
+	tar -C build -xzf src/mpc.tgz
+	mv build/mpc-* build/mpc
+	touch $@
+
+
+# KERNEL HEADERS
+
+${STAMPS}/linux-headers: ${STAMPS}/linux-extract
+	cd ${TOP}/build/linux && ${GMAKE} ARCH=riscv HOSTCC=${CC} headers
+	rm -f ${TOP}/build/linux/usr/include/Makefile
+	rm -f ${TOP}/build/linux/usr/include/headers_check.pl
+	touch $@
+
+${STAMPS}/linux-hdrinst: ${STAMPS}/linux-headers
+	mkdir -p ${PREFIX}/${TARGET}
+	cd ${TOP}/build/linux && ${SUDO} cp -rf ${TOP}/build/linux/usr/include ${PREFIX}/${TARGET}/
+	touch $@
+
+# BINUTILS
+
+${STAMPS}/binutils-configure: ${STAMPS}/binutils-extract
+	mkdir -p ${TOP}/build/binutils/build
+	cd ${TOP}/build/binutils/build && ../configure	\
+		--prefix=${PREFIX}			\
+		--target=${TARGET}			\
+		--with-system-zlib			\
+		--with-arch=${ARCH}			\
+		--disable-nls				\
+		--disable-werror			\
+		--disable-multilib
+	touch $@
+
+${STAMPS}/binutils-build: ${STAMPS}/binutils-configure
+	cd ${TOP}/build/binutils/build && ${GMAKE}
+	touch $@
+
+${STAMPS}/binutils-install: ${STAMPS}/binutils-build
+	cd ${TOP}/build/binutils/build && ${SUDO} ${GMAKE} install
+	touch $@
+
+# GCC
+
+${STAMPS}/gcc-configure: ${STAMPS}/gcc-extract ${STAMPS}/binutils-install ${STAMPS}/gmp ${STAMPS}/mpfr ${STAMPS}/mpc
+	mkdir -p ${TOP}/build/gcc/build
+	cd ${TOP}/build/gcc/build && ../configure	\
+		--prefix=${PREFIX}			\
+		--target=${TARGET}			\
+		--with-system-zlib			\
+		--with-arch=${ARCH}			\
+		--with-newlib				\
+		--without-headers			\
+		--enable-languages=c			\
+		--disable-nls				\
+		--disable-multilib			\
+		--disable-libssp			\
+		--disable-libsanitizer			\
+		--disable-libstdcxx			\
+		--disable-libgomp			\
+		--disable-libquadmath			\
+		--disable-libvtv			\
+		--disable-shared			\
+		--enable-static				\
+		--disable-threads
+	touch $@
+
+${STAMPS}/gcc-build: ${STAMPS}/gcc-configure
+	cd ${TOP}/build/gcc/build && ${GMAKE} all-gcc
+	touch $@
+
+${STAMPS}/gcc-install: ${STAMPS}/gcc-build
+	cd ${TOP}/build/gcc/build && ${SUDO} ${GMAKE} install-gcc
+	touch $@
+
+# MUSL
+
+${STAMPS}/musl-configure: ${STAMPS}/musl-extract ${STAMPS}/gcc-install
+	mkdir -p ${TOP}/build/musl/build
+	cd ${TOP}/build/musl/build && ../configure	\
+		--prefix=${PREFIX}/${TARGET}		\
+		--target=${TARGET}			\
+		--disable-shared			\
+		--disable-wrapper			\
+		--enable-static
+	touch $@
+
+${STAMPS}/musl-build: ${STAMPS}/musl-configure ${STAMPS}/linux-hdrinst
+	cd ${TOP}/build/musl/build && ${GMAKE}
+	touch $@
+
+${STAMPS}/musl-install: ${STAMPS}/musl-build
+	cd ${TOP}/build/musl/build && ${SUDO} ${GMAKE} install
+	touch $@
+
+# LIBGCC
+
+${STAMPS}/libgcc-build: ${STAMPS}/gcc-install ${STAMPS}/musl-install
+	cd ${TOP}/build/gcc/build && ${GMAKE} all-target-libgcc
+	touch $@
+
+${STAMPS}/libgcc-install: ${STAMPS}/libgcc-build
+	cd ${TOP}/build/gcc/build && ${SUDO} ${GMAKE} install-target-libgcc
+	touch $@