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