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