platform_linux.c (5191B)
1 /* See LICENSE for license details. */ 2 #define os_path_sep s8("/") 3 4 #define NULL ((void *)0) 5 6 #include "jdict.c" 7 8 #define PROT_READ 0x01 9 #define PROT_WRITE 0x02 10 #define MAP_PRIVATE 0x02 11 #define MAP_ANONYMOUS 0x20 12 13 #define O_RDONLY 0x00 14 #define O_DIRECTORY 0x10000 15 16 #define DT_REGULAR_FILE 8 17 18 static i64 syscall1(i64, i64); 19 static i64 syscall2(i64, i64, i64); 20 static i64 syscall3(i64, i64, i64, i64); 21 static i64 syscall6(i64, i64, i64, i64, i64, i64, i64); 22 23 typedef struct { 24 u8 buf[2048]; 25 iptr fd; 26 i32 buf_pos; 27 i32 buf_end; 28 } LinuxDirectoryStream; 29 30 /* NOTE: necessary garbage required by GCC/CLANG even when -nostdlib is used */ 31 __attribute((section(".text.memset"))) 32 void *memset(void *d, int c, usize n) 33 { 34 return mem_clear(d, c, n); 35 } 36 37 static void 38 os_exit(i32 code) 39 { 40 syscall1(SYS_exit, code); 41 __builtin_unreachable(); 42 } 43 44 static b32 45 os_write(iptr fd, s8 raw) 46 { 47 while (raw.len) { 48 size r = syscall3(SYS_write, fd, (i64)raw.s, raw.len); 49 if (r < 0) return 0; 50 raw = s8_cut_head(raw, r); 51 } 52 return 1; 53 } 54 55 static b32 56 os_read_stdin(u8 *buf, size count) 57 { 58 size rlen = syscall3(SYS_read, 0, (iptr)buf, count); 59 return rlen == count; 60 } 61 62 static s8 63 os_read_whole_file(char *file, Arena *a, u32 arena_flags) 64 { 65 __attribute((aligned(16))) u8 stat_buf[STAT_BUF_SIZE]; 66 u64 status = syscall2(SYS_stat, (iptr)file, (iptr)stat_buf); 67 if (status > -4096UL) { 68 stream_append_s8(&error_stream, s8("failed to stat: ")); 69 stream_append_s8(&error_stream, cstr_to_s8(file)); 70 die(&error_stream); 71 } 72 73 u64 fd = syscall3(SYS_open, (iptr)file, O_RDONLY, 0); 74 if (fd > -4096UL) { 75 stream_append_s8(&error_stream, s8("failed to open: ")); 76 stream_append_s8(&error_stream, cstr_to_s8(file)); 77 die(&error_stream); 78 } 79 80 i64 *file_size = (i64 *)(stat_buf + STAT_SIZE_OFF); 81 s8 result = {.len = *file_size, .s = alloc(a, u8, *file_size, arena_flags|ARENA_NO_CLEAR)}; 82 size rlen = syscall3(SYS_read, fd, (iptr)result.s, result.len); 83 syscall1(SYS_close, fd); 84 85 if (rlen != result.len) { 86 stream_append_s8(&error_stream, s8("failed to read whole file: ")); 87 stream_append_s8(&error_stream, cstr_to_s8(file)); 88 die(&error_stream); 89 } 90 91 return result; 92 } 93 94 static Arena 95 os_new_arena(size cap) 96 { 97 Arena a = {0}; 98 99 size pagesize = PAGESIZE; 100 if (cap % pagesize != 0) 101 cap += pagesize - cap % pagesize; 102 103 u64 p = syscall6(SYS_mmap, 0, cap, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 104 if (p > -4096UL) { 105 return (Arena){0}; 106 } else { 107 a.beg = (u8 *)p; 108 } 109 a.end = a.beg + cap; 110 #ifdef _DEBUG_ARENA 111 a.min_capacity_remaining = cap; 112 #endif 113 return a; 114 } 115 116 static PathStream 117 os_begin_path_stream(Stream *dir_name, Arena *a, u32 arena_flags) 118 { 119 stream_append_byte(dir_name, 0); 120 u64 fd = syscall3(SYS_open, (iptr)dir_name->data, O_DIRECTORY|O_RDONLY, 0); 121 dir_name->widx--; 122 123 if (fd > -4096UL) { 124 stream_append_s8(&error_stream, s8("os_begin_path_stream: failed to open: ")); 125 stream_append_s8(&error_stream, (s8){.len = dir_name->widx, .s = dir_name->data}); 126 die(&error_stream); 127 } 128 129 stream_append_byte(dir_name, '/'); 130 131 LinuxDirectoryStream *lds = alloc(a, LinuxDirectoryStream, 1, arena_flags); 132 lds->fd = fd; 133 return (PathStream){.dir_name = dir_name, .dirfd = lds}; 134 } 135 136 static void 137 os_end_path_stream(PathStream *ps) 138 { 139 LinuxDirectoryStream *lds = ps->dirfd; 140 syscall1(SYS_close, (iptr)lds->fd); 141 ps->dirfd = 0; 142 } 143 144 static s8 145 os_get_valid_file(PathStream *ps, s8 match_prefix, Arena *a, u32 arena_flags) 146 { 147 s8 result = {0}; 148 LinuxDirectoryStream *lds = ps->dirfd; 149 if (lds) { 150 for (;;) { 151 if (lds->buf_pos >= lds->buf_end) { 152 u64 ret = syscall3(SYS_getdents64, lds->fd, (iptr)lds->buf, 153 sizeof(lds->buf)); 154 if (ret > -4096UL) { 155 stream_append_s8(&error_stream, s8("os_get_valid_file: SYS_getdents")); 156 die(&error_stream); 157 } 158 if (ret == 0) 159 break; 160 lds->buf_end = ret; 161 lds->buf_pos = 0; 162 } 163 u16 record_len = *(u16 *)(lds->buf + lds->buf_pos + DIRENT_RECLEN_OFF); 164 u8 type = lds->buf[lds->buf_pos + DIRENT_TYPE_OFF]; 165 /* NOTE: technically this contains extra NULs but it doesn't matter 166 * for this purpose. We need NUL terminated to call SYS_read */ 167 s8 name = {.len = record_len - 2 - DIRENT_NAME_OFF, 168 .s = lds->buf + lds->buf_pos + DIRENT_NAME_OFF}; 169 lds->buf_pos += record_len; 170 if (type == DT_REGULAR_FILE) { 171 b32 valid = 1; 172 for (size i = 0; i < match_prefix.len; i++) { 173 if (match_prefix.s[i] != name.s[i]) { 174 valid = 0; 175 break; 176 } 177 } 178 if (valid) { 179 Stream dir_name = *ps->dir_name; 180 stream_append_s8(&dir_name, name); 181 result = os_read_whole_file((char *)dir_name.data, a, 182 arena_flags); 183 break; 184 } 185 } 186 } 187 } 188 return result; 189 } 190 191 void 192 linux_main(i32 argc, char *argv[], char *envp[]) 193 { 194 (void)envp; 195 196 Arena memory = os_new_arena(1024 * MEGABYTE); 197 198 error_stream.fd = 2; 199 error_stream.cap = 4096; 200 error_stream.data = alloc(&memory, u8, error_stream.cap, ARENA_NO_CLEAR); 201 202 stdout_stream.fd = 1; 203 stdout_stream.cap = 8 * MEGABYTE; 204 stdout_stream.data = alloc(&memory, u8, error_stream.cap, ARENA_NO_CLEAR); 205 206 i32 result = jdict(&memory, argc, argv); 207 208 os_exit(result); 209 }