code wiki / (root) / nx_archive.nx

nx_archive.nx source

↩ module page · 228 lines · 7814 B

1// nx_archive.nx -- BSD-format ar(1) static library builder. 2// 3// Replaces `ar rcs libfoo.a *.o`. Useful for shipping reusable 4// NishiLang code as a single linkable artifact. 5// 6// AR format (System V variant; also BSD-compatible): 7// 8// "!<arch>\n" 8 bytes -- magic 9// one or more entries: 10// [name (16 bytes, space-padded, ends with '/')] 11// [date (12 bytes ASCII decimal seconds-since-epoch)] 12// [uid (6 bytes ASCII decimal)] 13// [gid (6 bytes ASCII decimal)] 14// [mode (8 bytes ASCII octal)] 15// [size (10 bytes ASCII decimal)] 16// [magic (2 bytes "`\n")] 17// [data (size bytes)] 18// [pad (1 byte if size is odd -- "\n")] 19// 20// Total per entry: 60-byte header + data + optional pad. Names 21// longer than 15 chars use the "//" extended-name table (BSD/SysV 22// convention), which we DEFER to v0.0.2 -- v0.0.1 names must be 23// ≤ 15 chars + a single trailing '/'. 24// 25// Pairs with nx_elf_writer (produces objects), nx_elf_read + 26// nx_objdump (inspect), nxld (linker that consumes archives). 27 28// nx_safety_envelope: 29// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 30// sil_target: SIL1 31// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 32// verdict: NOT_YET_EVALUATED 33 34import "syscalls.nx" 35const NX_MAGIC_4096: i64 = 4096 36 37// NX_AR_MAGIC bytes: "!<arch>\n" -- written byte-by-byte in nx_ar_new 38// since NishiLang const slots accept only integer literals. 39const NX_AR_HEADER_BYTES: i64 = 60 40const NX_AR_NAME_BYTES: i64 = 16 41const NX_AR_DATE_BYTES: i64 = 12 42const NX_AR_UID_BYTES: i64 = 6 43const NX_AR_GID_BYTES: i64 = 6 44const NX_AR_MODE_BYTES: i64 = 8 45const NX_AR_SIZE_BYTES: i64 = 10 46 47struct NxAr { 48 buf: *u8, 49 cap: i64, 50 used: i64, 51 n: i64, // number of entries 52} 53 54const NX_AR_BYTES: i64 = 32 55 56// ---- decimal / ASCII pack helpers -------------------------------- 57 58// Write `v` as ASCII decimal, left-justified, space-padded to width. 59// Returns 0 OK / -1 if value won't fit. 60func nx_ar_pad_dec(dst: *u8, off: i64, width: i64, v: i64) -> i64 { 61 let tmp: *u8 = sys_mmap(32) 62 var n: i64 = v 63 var k: i64 = 0 64 if n == 0 { 65 tmp[0] = 0x30 66 k = 1 67 } 68 while n > 0 { 69 tmp[k] = 0x30 + (n - (n / 10) * 10) 70 n = n / 10 71 k = k + 1 72 } 73 if k > width { return -1 } 74 // Write digits left-to-right (reversed from tmp). 75 var di: i64 = 0 76 while di < k { 77 dst[off + di] = tmp[k - 1 - di] 78 di = di + 1 79 } 80 // Space-pad the remainder. 81 while di < width { 82 dst[off + di] = 0x20 83 di = di + 1 84 } 85 return 0 86} 87 88// Pack `name` into the 16-byte name field, append '/', space-pad. 89func nx_ar_pad_name(dst: *u8, off: i64, name: *u8) -> i64 { 90 var i: i64 = 0 91 while i < 15 { 92 if name[i] == 0 { i = 15 } 93 if i < 15 { 94 dst[off + i] = name[i] 95 i = i + 1 96 } 97 } 98 // Compute actual length to know where to put the '/'. 99 var nlen: i64 = 0 100 while nlen < 15 { 101 if name[nlen] == 0 { nlen = 15 } 102 if nlen < 15 { nlen = nlen + 1 } 103 } 104 if nlen > 15 { return -1 } 105 dst[off + nlen] = 0x2F // '/' 106 var pi: i64 = nlen + 1 107 while pi < NX_AR_NAME_BYTES { 108 dst[off + pi] = 0x20 109 pi = pi + 1 110 } 111 return 0 112} 113 114// ---- construction ------------------------------------------------- 115 116func nx_ar_new(cap: i64) -> *NxAr { 117 let raw: *u8 = sys_mmap(NX_AR_BYTES) 118 let a: *NxAr = raw as *NxAr 119 a.buf = sys_mmap(cap) 120 a.cap = cap 121 a.used = 0 122 a.n = 0 123 // Write magic "!<arch>\n" at offset 0. 124 a.buf[0] = 0x21 // '!' 125 a.buf[1] = 0x3C // '<' 126 a.buf[2] = 0x61 // 'a' 127 a.buf[3] = 0x72 // 'r' 128 a.buf[4] = 0x63 // 'c' 129 a.buf[5] = 0x68 // 'h' 130 a.buf[6] = 0x3E // '>' 131 a.buf[7] = 0x0A // '\n' 132 a.used = 8 133 return a 134} 135 136// Append a member. `name` ≤ 15 chars + NUL. Returns 0 / -1. 137func nx_ar_add(a: *NxAr, name: *u8, data: *u8, size: i64) -> i64 { 138 let entry_total: i64 = NX_AR_HEADER_BYTES + size + (size & 1) 139 if a.used + entry_total > a.cap { return -1 } 140 141 let h_off: i64 = a.used 142 if nx_ar_pad_name(a.buf, h_off + 0, name) < 0 { return -1 } 143 144 // date / uid / gid / mode / size — fill with sensible defaults 145 // (date=0 keeps F6 manifests deterministic across machines). 146 nx_ar_pad_dec(a.buf, h_off + 16, NX_AR_DATE_BYTES, 0) 147 nx_ar_pad_dec(a.buf, h_off + 28, NX_AR_UID_BYTES, 0) 148 nx_ar_pad_dec(a.buf, h_off + 34, NX_AR_GID_BYTES, 0) 149 nx_ar_pad_dec(a.buf, h_off + 40, NX_AR_MODE_BYTES, 644) 150 nx_ar_pad_dec(a.buf, h_off + 48, NX_AR_SIZE_BYTES, size) 151 152 // Trailing 2-byte magic "`\n" 153 a.buf[h_off + 58] = 0x60 // '`' 154 a.buf[h_off + 59] = 0x0A // '\n' 155 156 // Data. 157 var i: i64 = 0 158 while i < size { 159 a.buf[h_off + NX_AR_HEADER_BYTES + i] = data[i] 160 i = i + 1 161 } 162 163 // Odd-size pad newline. 164 if (size & 1) == 1 { 165 a.buf[h_off + NX_AR_HEADER_BYTES + size] = 0x0A 166 } 167 168 a.used = a.used + entry_total 169 a.n = a.n + 1 170 return 0 171} 172 173func nx_ar_size(a: *NxAr) -> i64 { return a.used } 174func nx_ar_count(a: *NxAr) -> i64 { return a.n } 175func nx_ar_bytes(a: *NxAr) -> *u8 { return a.buf } 176 177// ---- self-test ---------------------------------------------------- 178 179func main() -> i64 { 180 let a: *NxAr = nx_ar_new(NX_MAGIC_4096) 181 182 // Magic at offset 0. 183 if a.buf[0] != 0x21 { return __syscall(93, 10, 0, 0, 0, 0, 0) } // '!' 184 if a.buf[7] != 0x0A { return __syscall(93, 11, 0, 0, 0, 0, 0) } 185 if nx_ar_size(a) != 8 { return __syscall(93, 12, 0, 0, 0, 0, 0) } 186 if nx_ar_count(a) != 0 { return __syscall(93, 13, 0, 0, 0, 0, 0) } 187 188 // Add a 4-byte member named "foo.o". 189 let foo_data: *u8 = sys_mmap(16) 190 foo_data[0] = 0x41; foo_data[1] = 0x42; foo_data[2] = 0x43; foo_data[3] = 0x44 191 let foo_name: *u8 = sys_mmap(16) 192 foo_name[0] = 0x66; foo_name[1] = 0x6F; foo_name[2] = 0x6F 193 foo_name[3] = 0x2E; foo_name[4] = 0x6F; foo_name[5] = 0 194 if nx_ar_add(a, foo_name, foo_data, 4) != 0 { return __syscall(93, 20, 0, 0, 0, 0, 0) } 195 196 // Header at offset 8 starts with "foo.o/" then spaces. 197 if a.buf[8] != 0x66 { return __syscall(93, 30, 0, 0, 0, 0, 0) } 198 if a.buf[9] != 0x6F { return __syscall(93, 31, 0, 0, 0, 0, 0) } 199 if a.buf[10] != 0x6F { return __syscall(93, 32, 0, 0, 0, 0, 0) } 200 if a.buf[11] != 0x2E { return __syscall(93, 33, 0, 0, 0, 0, 0) } 201 if a.buf[12] != 0x6F { return __syscall(93, 34, 0, 0, 0, 0, 0) } 202 if a.buf[13] != 0x2F { return __syscall(93, 35, 0, 0, 0, 0, 0) } // '/' 203 if a.buf[14] != 0x20 { return __syscall(93, 36, 0, 0, 0, 0, 0) } // ' ' 204 205 // Trailing magic at offset 8+58. 206 if a.buf[8 + 58] != 0x60 { return __syscall(93, 40, 0, 0, 0, 0, 0) } 207 if a.buf[8 + 59] != 0x0A { return __syscall(93, 41, 0, 0, 0, 0, 0) } 208 209 // Data follows the header. 210 if a.buf[8 + 60] != 0x41 { return __syscall(93, 50, 0, 0, 0, 0, 0) } // 'A' 211 if a.buf[8 + 63] != 0x44 { return __syscall(93, 51, 0, 0, 0, 0, 0) } // 'D' 212 213 // Even-size data: no pad. Total used = 8 + 60 + 4 = 72. 214 if nx_ar_size(a) != 72 { return __syscall(93, 52, 0, 0, 0, 0, 0) } 215 216 // Add an odd-size member: 5 bytes named "bar.o" -> +1 byte pad. 217 let bar_data: *u8 = sys_mmap(16) 218 bar_data[0] = 0x57; bar_data[1] = 0x58; bar_data[2] = 0x59; bar_data[3] = 0x5A; bar_data[4] = 0x5B 219 let bar_name: *u8 = sys_mmap(16) 220 bar_name[0] = 0x62; bar_name[1] = 0x61; bar_name[2] = 0x72 221 bar_name[3] = 0x2E; bar_name[4] = 0x6F; bar_name[5] = 0 222 nx_ar_add(a, bar_name, bar_data, 5) 223 // 72 + 60 + 5 + 1 = 138. 224 if nx_ar_size(a) != 138 { return __syscall(93, 60, 0, 0, 0, 0, 0) } 225 if nx_ar_count(a) != 2 { return __syscall(93, 61, 0, 0, 0, 0, 0) } 226 227 return 0 228}