initial commit
This commit is contained in:
commit
7accc34991
10 changed files with 498 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
msh
|
||||||
25
Makefile
Normal file
25
Makefile
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
SOURCES := src/main.c
|
||||||
|
CFLAGS := -g -O0 -nostdlib
|
||||||
|
|
||||||
|
UNAME := $(shell uname)
|
||||||
|
ARCH := $(shell uname -m)
|
||||||
|
|
||||||
|
ifeq ($(UNAME), Linux)
|
||||||
|
SOURCES += src/system/linux/linux.c src/system/linux/alloc.c
|
||||||
|
CFLAGS += -fno-stack-protector
|
||||||
|
CFLAGS += -DMSH_ENTRY_POINT="void _start()"
|
||||||
|
ifeq ($(ARCH),i386)
|
||||||
|
SOURCES += src/system/linux/i386.s
|
||||||
|
CFLAGS += -DARCH_32
|
||||||
|
endif
|
||||||
|
ifeq ($(ARCH),x86_64)
|
||||||
|
SOURCES += src/system/linux/x86_64.s
|
||||||
|
CFLAGS += -DARCH_64
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: $(SOURCES)
|
||||||
|
$(CC) $(SOURCES) $(CFLAGS) -o msh
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o msh
|
||||||
5
README.md
Normal file
5
README.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# msh
|
||||||
|
|
||||||
|
msh is a WIP shell with the goal of being portable to older operating systems, namely classic Windows and classic Mac OS. While the former already has a shell, the aim for both is to provide an emulation of bash that's more sane to use in the modern day then whatever is built in.
|
||||||
|
|
||||||
|
It's written without the C standard library. Ports instead must implement `src/system/system.h` accordingly. It's also written in C89/C99 (mostly the former as it's needed for some compilers, but if C99 is supported by a platform's backend then it'll probably be used).
|
||||||
39
src/main.c
Normal file
39
src/main.c
Normal file
|
|
@ -0,0 +1,39 @@
|
||||||
|
#include "system/system.h"
|
||||||
|
|
||||||
|
unsigned char prefix[] = "%> ";
|
||||||
|
|
||||||
|
/* Makefile-defined system entry point. */
|
||||||
|
MSH_ENTRY_POINT {
|
||||||
|
while (1) {
|
||||||
|
int out;
|
||||||
|
unsigned char *buf = msh_callocate(2048, sizeof(unsigned char));
|
||||||
|
unsigned char **args = msh_callocate(2048 * 2, sizeof(unsigned char));
|
||||||
|
int i = 0, n = 0, last = 0;
|
||||||
|
unsigned long long sz = 0;
|
||||||
|
|
||||||
|
msh_write(prefix, sizeof(prefix));
|
||||||
|
|
||||||
|
while ((out = msh_read_input(buf, &sz, 2048)), !out) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/* args */
|
||||||
|
for (i = 0; i < sz; i++) {
|
||||||
|
if (buf[i] == ' ') {
|
||||||
|
*(buf + i) = '\0';
|
||||||
|
if (i != sz - 1) {
|
||||||
|
args[n++] = buf + last;
|
||||||
|
last = i + 1;
|
||||||
|
if (n >= 2048) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
args[n++] = buf + last;
|
||||||
|
|
||||||
|
msh_execute(buf, args);
|
||||||
|
|
||||||
|
msh_deallocate(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
138
src/system/linux/alloc.c
Normal file
138
src/system/linux/alloc.c
Normal file
|
|
@ -0,0 +1,138 @@
|
||||||
|
#include "../system.h"
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
typedef struct msh_linux_block_t msh_linux_block;
|
||||||
|
|
||||||
|
#ifdef ARCH_32
|
||||||
|
#define PTR_ALIGN(x) (((((x) - 1) >> 2) << 2) + 4)
|
||||||
|
#else
|
||||||
|
#define PTR_ALIGN(x) (((((x) - 1) >> 3) << 3) + 8)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct msh_linux_block_t {
|
||||||
|
SIZE_TYPE size;
|
||||||
|
msh_linux_block *prev;
|
||||||
|
msh_linux_block *next;
|
||||||
|
int free;
|
||||||
|
char data[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MSH_BLOCK_SIZE 28
|
||||||
|
|
||||||
|
static msh_linux_block *msh_linux_base = 0;
|
||||||
|
|
||||||
|
static msh_linux_block *find_block(msh_linux_block **last, SIZE_TYPE size) {
|
||||||
|
msh_linux_block *b = msh_linux_base;
|
||||||
|
while (b && !(b->free && b->size >= size)) {
|
||||||
|
*last = b;
|
||||||
|
b = b->next;
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
static msh_linux_block *extend_heap(msh_linux_block *last, SIZE_TYPE size) {
|
||||||
|
msh_linux_block *sb = (msh_linux_block *)SYSCALL(brk, 0);
|
||||||
|
|
||||||
|
if (SYSCALL(brk, sb + (MSH_BLOCK_SIZE) + size) < 0) {
|
||||||
|
return (void *)0;
|
||||||
|
};
|
||||||
|
|
||||||
|
sb->size = size;
|
||||||
|
sb->next = (msh_linux_block *)0;
|
||||||
|
sb->prev = last;
|
||||||
|
|
||||||
|
if (last) {
|
||||||
|
last->next = sb;
|
||||||
|
}
|
||||||
|
sb->free = 0;
|
||||||
|
|
||||||
|
return (void *)sb;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void split_block(msh_linux_block *b, SIZE_TYPE size) {
|
||||||
|
msh_linux_block *new;
|
||||||
|
new = (msh_linux_block *)b->data + size;
|
||||||
|
new->size = b->size - size - MSH_BLOCK_SIZE;
|
||||||
|
new->next = b->next;
|
||||||
|
new->free = 1;
|
||||||
|
b->size = size;
|
||||||
|
b->next = new;
|
||||||
|
if (new->next) {
|
||||||
|
new->next->prev = new;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static msh_linux_block *get_metadata_block(void *p) {
|
||||||
|
return p - MSH_BLOCK_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static msh_linux_block *fuse_with_next_block(msh_linux_block *b) {
|
||||||
|
if (b->next && b->next->free) {
|
||||||
|
b->size += MSH_BLOCK_SIZE + b->next->size;
|
||||||
|
b->next = b->next->next;
|
||||||
|
if (b->next) {
|
||||||
|
b->next->prev = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *msh_allocate(SIZE_TYPE size) {
|
||||||
|
msh_linux_block *b, *last = 0;
|
||||||
|
SIZE_TYPE s = PTR_ALIGN(size);
|
||||||
|
|
||||||
|
if (msh_linux_base) {
|
||||||
|
/* first find a block */
|
||||||
|
last = msh_linux_base;
|
||||||
|
b = find_block(&last, s);
|
||||||
|
if (b) {
|
||||||
|
/* if we can split, do so */
|
||||||
|
if ((b->size - s) >= MSH_BLOCK_SIZE + 4) {
|
||||||
|
split_block(b, s);
|
||||||
|
}
|
||||||
|
b->free = 0;
|
||||||
|
} else {
|
||||||
|
b = extend_heap(last, s);
|
||||||
|
if (!b) {
|
||||||
|
return (msh_linux_block *)0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
b = extend_heap(last, s);
|
||||||
|
if (!b) {
|
||||||
|
return (msh_linux_block *)0;
|
||||||
|
}
|
||||||
|
msh_linux_base = b;
|
||||||
|
}
|
||||||
|
return b->data;
|
||||||
|
};
|
||||||
|
|
||||||
|
void msh_deallocate(void *ptr) {
|
||||||
|
msh_linux_block *block = get_metadata_block(ptr);
|
||||||
|
block->free = 1;
|
||||||
|
if (block->prev && block->prev->free) {
|
||||||
|
block = fuse_with_next_block(block->prev);
|
||||||
|
}
|
||||||
|
if (block->next) {
|
||||||
|
fuse_with_next_block(block);
|
||||||
|
} else {
|
||||||
|
/* free the end of the heap */
|
||||||
|
if (block->prev) {
|
||||||
|
block->prev->next = 0;
|
||||||
|
} else {
|
||||||
|
/* no more block */
|
||||||
|
msh_linux_base = 0;
|
||||||
|
}
|
||||||
|
SYSCALL(brk, block);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void *msh_callocate(SIZE_TYPE number, SIZE_TYPE size) {
|
||||||
|
SIZE_TYPE *new, i, s4;
|
||||||
|
new = msh_allocate(number * size);
|
||||||
|
if (new) {
|
||||||
|
for (i = 0; i < (number * size); i++) {
|
||||||
|
new[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (new);
|
||||||
|
}
|
||||||
75
src/system/linux/i386.s
Normal file
75
src/system/linux/i386.s
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
.globl syscall_0
|
||||||
|
.globl syscall_1
|
||||||
|
.globl syscall_2
|
||||||
|
.globl syscall_3
|
||||||
|
.globl syscall_4
|
||||||
|
.globl syscall_5
|
||||||
|
.globl syscall_6
|
||||||
|
syscall_0:
|
||||||
|
push %ebx
|
||||||
|
mov 0x4(%esp), %eax
|
||||||
|
int $0x80
|
||||||
|
pop %ebx
|
||||||
|
ret
|
||||||
|
syscall_1:
|
||||||
|
push %ebx
|
||||||
|
mov 0x8(%esp), %eax
|
||||||
|
mov 0xc(%esp), %ebx
|
||||||
|
int $0x80
|
||||||
|
pop %ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
syscall_2:
|
||||||
|
push %ebx
|
||||||
|
mov 0x8(%esp), %eax
|
||||||
|
mov 0xc(%esp), %ebx
|
||||||
|
mov 0x10(%esp), %ecx
|
||||||
|
int $0x80
|
||||||
|
pop %ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
syscall_3:
|
||||||
|
push %ebx
|
||||||
|
mov 0x8(%esp), %eax
|
||||||
|
mov 0xc(%esp), %ebx
|
||||||
|
mov 0x10(%esp), %ecx
|
||||||
|
mov 0x14(%esp), %edx
|
||||||
|
int $0x80
|
||||||
|
pop %ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
syscall_4:
|
||||||
|
push %ebx
|
||||||
|
mov 0x8(%esp), %eax
|
||||||
|
mov 0xc(%esp), %ebx
|
||||||
|
mov 0x10(%esp), %ecx
|
||||||
|
mov 0x14(%esp), %edx
|
||||||
|
mov 0x18(%esp), %esi
|
||||||
|
int $0x80
|
||||||
|
pop %ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
syscall_5:
|
||||||
|
push %ebx
|
||||||
|
mov 0x8(%esp), %eax
|
||||||
|
mov 0xc(%esp), %ebx
|
||||||
|
mov 0x10(%esp), %ecx
|
||||||
|
mov 0x14(%esp), %edx
|
||||||
|
mov 0x18(%esp), %esi
|
||||||
|
int $0x80
|
||||||
|
pop %ebx
|
||||||
|
ret
|
||||||
|
|
||||||
|
syscall_6:
|
||||||
|
push %ebx
|
||||||
|
mov 0x8(%esp), %eax
|
||||||
|
mov 0xc(%esp), %ebx
|
||||||
|
mov 0x10(%esp), %ecx
|
||||||
|
mov 0x14(%esp), %edx
|
||||||
|
mov 0x18(%esp), %esi
|
||||||
|
mov 0x1c(%esp), %edi
|
||||||
|
mov 0x20(%esp), %ebp
|
||||||
|
int $0x80
|
||||||
|
pop %ebx
|
||||||
|
|
||||||
|
ret
|
||||||
41
src/system/linux/linux.c
Normal file
41
src/system/linux/linux.c
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#include "../system.h"
|
||||||
|
#include "syscall.h"
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
int msh_read_input(unsigned char *ptr, SIZE_TYPE *size, SIZE_TYPE n) {
|
||||||
|
char ch;
|
||||||
|
SIZE_TYPE sz;
|
||||||
|
if (*size >= n) {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
sz = (SIZE_TYPE)SYSCALL(read, 0, &ch, 1);
|
||||||
|
if (ch == '\n') {
|
||||||
|
*(ptr + *size) = '\0';
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
*(ptr + *size) = ch;
|
||||||
|
*size += sz;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
void msh_write(unsigned char *st, SIZE_TYPE size) {
|
||||||
|
SYSCALL(write, 1, st, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
int msh_execute(unsigned char *cmd, unsigned char **args) {
|
||||||
|
SIZE_TYPE fork = (SIZE_TYPE)SYSCALL(fork, 0);
|
||||||
|
if (fork == 0) {
|
||||||
|
SYSCALL(execve, cmd, args, 0);
|
||||||
|
msh_deallocate(args);
|
||||||
|
SYSCALL(kill, fork, SIGKILL);
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
siginfo_t info;
|
||||||
|
SYSCALL(waitid, P_ALL, 0, &info, WEXITED);
|
||||||
|
return info.si_code;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
38
src/system/linux/syscall.h
Normal file
38
src/system/linux/syscall.h
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#ifndef __MSH_SYSTEM_LINUX_SYSCALL
|
||||||
|
#define __MSH_SYSTEM_LINUX_SYSCALL
|
||||||
|
|
||||||
|
#define NARGS_SEQ(_1, _2, _3, _4, _5, _6, _7, _8, _9, N, ...) N
|
||||||
|
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1)
|
||||||
|
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
|
||||||
|
void *syscall_0(void *number);
|
||||||
|
void *syscall_1(void *number, void *arg1);
|
||||||
|
void *syscall_2(void *number, void *arg1, void *arg2);
|
||||||
|
void *syscall_3(void *number, void *arg1, void *arg2, void *arg3);
|
||||||
|
void *syscall_4(void *number, void *arg1, void *arg2, void *arg3, void *arg4);
|
||||||
|
void *syscall_5(void *number, void *arg1, void *arg2, void *arg3, void *arg4,
|
||||||
|
void *arg5);
|
||||||
|
void *syscall_6(void *number, void *arg1, void *arg2, void *arg3, void *arg4,
|
||||||
|
void *arg5, void *arg6);
|
||||||
|
|
||||||
|
#define _syscall_0(a) syscall_0((void *)(a))
|
||||||
|
#define _syscall_1(a, b) syscall_1((void *)(a), (void *)(b))
|
||||||
|
#define _syscall_2(a, b, c) syscall_2((void *)(a), (void *)(b), (void *)(c))
|
||||||
|
#define _syscall_3(a, b, c, d) \
|
||||||
|
syscall_3((void *)(a), (void *)(b), (void *)(c), (void *)(d))
|
||||||
|
#define _syscall_4(a, b, c, d, e) \
|
||||||
|
syscall_4((void *)(a), (void *)(b), (void *)(c), (void *)(d), (void *)(e))
|
||||||
|
#define _syscall_5(a, b, c, d, e, f) \
|
||||||
|
syscall_5((void *)(a), (void *)(b), (void *)(c), (void *)(d), (void *)(e), \
|
||||||
|
(void *)(f))
|
||||||
|
#define _syscall_6(a, b, c, d, e, f, g) \
|
||||||
|
syscall_6((void *)(a), (void *)(b), (void *)(c), (void *)(d), (void *)(e), \
|
||||||
|
(void *)(f), (void *)(g))
|
||||||
|
#define PASTER(x, y) x##_##y
|
||||||
|
#define EVALUATOR(x, y) PASTER(x, y)
|
||||||
|
#define VARIABLE(...) NARGS(__VA_ARGS__)
|
||||||
|
#define FUNC(...) EVALUATOR(_syscall, VARIABLE(__VA_ARGS__))
|
||||||
|
|
||||||
|
#define SYSCALL(name, ...) FUNC(__VA_ARGS__)(__NR_##name, __VA_ARGS__)
|
||||||
|
#endif
|
||||||
86
src/system/linux/x86_64.s
Normal file
86
src/system/linux/x86_64.s
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
# Copyright 2024 (c) Jonathan M. Wilbur. Released under an MIT License.
|
||||||
|
|
||||||
|
.globl syscall_0
|
||||||
|
.globl syscall_1
|
||||||
|
.globl syscall_2
|
||||||
|
.globl syscall_3
|
||||||
|
.globl syscall_4
|
||||||
|
.globl syscall_5
|
||||||
|
.globl syscall_6
|
||||||
|
|
||||||
|
syscall_0:
|
||||||
|
mov %rdi, %rax
|
||||||
|
syscall
|
||||||
|
retq
|
||||||
|
|
||||||
|
syscall_1:
|
||||||
|
push %rbp
|
||||||
|
mov %rsp, %rbp
|
||||||
|
|
||||||
|
push %rdi
|
||||||
|
|
||||||
|
mov %rdi, %rax
|
||||||
|
mov %rsi, %rdi
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov %rdi, %rsi
|
||||||
|
pop %rdi
|
||||||
|
|
||||||
|
leaveq
|
||||||
|
retq
|
||||||
|
|
||||||
|
syscall_2:
|
||||||
|
mov %rdi, %rax
|
||||||
|
mov %rsi, %rdi
|
||||||
|
mov %rdx, %rsi
|
||||||
|
syscall
|
||||||
|
retq
|
||||||
|
|
||||||
|
syscall_3:
|
||||||
|
push %rbp
|
||||||
|
mov %rsp, %rbp
|
||||||
|
push %rdi
|
||||||
|
|
||||||
|
mov %rdi, %rax
|
||||||
|
mov %rsi, %rdi
|
||||||
|
mov %rdx, %rsi
|
||||||
|
mov %rcx, %rdx
|
||||||
|
syscall
|
||||||
|
|
||||||
|
mov %rdx, %rcx
|
||||||
|
mov %rsi, %rdx
|
||||||
|
mov %rdi, %rsi
|
||||||
|
pop %rdi
|
||||||
|
|
||||||
|
leaveq
|
||||||
|
retq
|
||||||
|
|
||||||
|
syscall_4:
|
||||||
|
mov %rdi, %rax
|
||||||
|
mov %rsi, %rdi
|
||||||
|
mov %rdx, %rsi
|
||||||
|
mov %rcx, %rdx
|
||||||
|
mov %r8, %r10
|
||||||
|
syscall
|
||||||
|
retq
|
||||||
|
|
||||||
|
syscall_5:
|
||||||
|
mov %rdi, %rax
|
||||||
|
mov %rsi, %rdi
|
||||||
|
mov %rdx, %rsi
|
||||||
|
mov %rcx, %rdx
|
||||||
|
mov %r8, %r10
|
||||||
|
mov %r9, %r8
|
||||||
|
syscall
|
||||||
|
retq
|
||||||
|
|
||||||
|
syscall_6:
|
||||||
|
mov %rdi, %rax
|
||||||
|
mov %rsi, %rdi
|
||||||
|
mov %rdx, %rsi
|
||||||
|
mov %rcx, %rdx
|
||||||
|
mov %r8, %r10
|
||||||
|
mov %r9, %r8
|
||||||
|
mov 0x8(%rsp), %r9
|
||||||
|
syscall
|
||||||
|
retq
|
||||||
50
src/system/system.h
Normal file
50
src/system/system.h
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#ifndef __MSH_SYSTEM_H
|
||||||
|
#define __MSH_SYSTEM_H
|
||||||
|
|
||||||
|
#ifndef SIZE_TYPE
|
||||||
|
#ifdef ARCH_32
|
||||||
|
#define SIZE_TYPE unsigned long
|
||||||
|
#else
|
||||||
|
#define SIZE_TYPE unsigned long long
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Allocate memory */
|
||||||
|
void *msh_allocate(SIZE_TYPE size);
|
||||||
|
|
||||||
|
/* Allocates memory for an array of num objects of size */
|
||||||
|
void *msh_callocate(SIZE_TYPE number, SIZE_TYPE size);
|
||||||
|
|
||||||
|
/* Deallocate memory previously allocated by msh_allocate */
|
||||||
|
void msh_deallocate(void *ptr);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read user input into ptr, stopping if it has reached n characters.
|
||||||
|
* returns 1 if finished reading, 2 if it ran out of space.
|
||||||
|
*/
|
||||||
|
int msh_read_input(unsigned char *ptr, SIZE_TYPE *size, SIZE_TYPE n);
|
||||||
|
|
||||||
|
/* Write characters to terminal. */
|
||||||
|
void msh_write(unsigned char *st, SIZE_TYPE size);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute given command with args.
|
||||||
|
*
|
||||||
|
* cmd is self explanatory, args is a null terminated array of strings based on
|
||||||
|
* the arguments the user provided.
|
||||||
|
*
|
||||||
|
* The function is also responsible for searching the user's PATH to execute the
|
||||||
|
* first command.
|
||||||
|
*
|
||||||
|
* Returns exit status (or 0 on platforms where that isn't a thing).
|
||||||
|
*/
|
||||||
|
int msh_execute(unsigned char *cmd, unsigned char **args);
|
||||||
|
|
||||||
|
#ifndef MSH_ENTRY_POINT
|
||||||
|
/*
|
||||||
|
* default to int main() to make language servers happy.
|
||||||
|
*/
|
||||||
|
#define MSH_ENTRY_POINT int main()
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
Loading…
Add table
Add a link
Reference in a new issue