summaryrefslogtreecommitdiffstats
path: root/rsnix/src/load.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-08-27 16:58:31 +0200
committersalmonik <salmonik@tuta.io>2026-08-27 16:58:31 +0200
commit300c0acbd7ab3fae8596ff839c39d9a17c70c68d (patch)
tree5a600b24c037dc798766e3ce3fc004956e517abb /rsnix/src/load.rs
parentexec: PE32+ loader, versioned ABI, first external binaries (v0.6.0) (diff)
downloadrs-dos-300c0acbd7ab3fae8596ff839c39d9a17c70c68d.tar.gz
rs-dos-300c0acbd7ab3fae8596ff839c39d9a17c70c68d.zip
streams: per-invocation channel, pumping mode, filter pipeline wiring
exec: LineChannel bounded ring (cap=16) with abort/eof/marker fields. Shell 'pumping mode' assembles canonical lines from its queue into the channel while the command runs; ^C marks the channel aborted. Shell-side grammar: USE/TIL pre-drain into the channel before spawn; <cmd> <path> TIL <marker> sugar drains paths until the marker line. Interactive TIL: pump_key checks each submitted line against the marker and closes the channel when it matches. Exports callbacks run in the command task but touch shell-owned state: this is safe by construction — the shell blocks in exec_external until the command completes. put_line feeds Pipeline::line directly; get_line pops from the channel (yield-poll when empty). load: drop NX from image mapping (single-privilege kernel, NX bit 63 reserved when EFER.NXE is set by OVMF); compute image span from section RVAs not file size. bins/count: gate binary — reads lines via get_line, reports count via put_line. Verified: count USE file → 6; count <path> TIL END → 2; interactive count TIL DONE → 3; -h filter across task boundary; ^C abort. v1 stream exports: put_line → Pipeline, get_line ← channel, log_line → klog append, exit → abort_current. Accounting: print_one_line → pipe.line() returns false (head kill / pager ^C) → the binary should wrap up promptly; accepted: binary does not check the return value in v1 (phase 3 simplification).
Diffstat (limited to 'rsnix/src/load.rs')
-rw-r--r--rsnix/src/load.rs68
1 files changed, 53 insertions, 15 deletions
diff --git a/rsnix/src/load.rs b/rsnix/src/load.rs
index cebca89..488c703 100644
--- a/rsnix/src/load.rs
+++ b/rsnix/src/load.rs
@@ -50,7 +50,8 @@ pub enum LoadError {
50// --------------------------------------------------------------------------- 50// ---------------------------------------------------------------------------
51 51
52fn u16_at(b: &[u8], off: usize) -> Option<u16> { 52fn u16_at(b: &[u8], off: usize) -> Option<u16> {
53 b.get(off..off + 2).map(|s| u16::from_le_bytes([s[0], s[1]])) 53 b.get(off..off + 2)
54 .map(|s| u16::from_le_bytes([s[0], s[1]]))
54} 55}
55fn u32_at(b: &[u8], off: usize) -> Option<u32> { 56fn u32_at(b: &[u8], off: usize) -> Option<u32> {
56 b.get(off..off + 4) 57 b.get(off..off + 4)
@@ -87,7 +88,23 @@ struct Section {
87 raw_size: usize, 88 raw_size: usize,
88} 89}
89 90
90/// Parse headers, apply relocations into `mem`, return the entry RVA. 91/// Total virtual span of a PE image: the highest section RVA+virt_size
92/// (never smaller than one page for the headers).
93fn image_span_bytes(img: &[u8]) -> usize {
94 let e_lfanew = u32_at(img, 0x3C).unwrap_or(0) as usize;
95 let coff = e_lfanew + 4;
96 let num_sections = u16_at(img, coff + 2).unwrap_or(0) as usize;
97 let opt_size = u16_at(img, coff + 16).unwrap_or(0) as usize;
98 let sect_tab = coff + 20 + opt_size;
99 let mut end = PAGE_SIZE; // at least one page for headers
100 for i in 0..num_sections {
101 let s = sect_tab + i * 40;
102 let vsz = u32_at(img, s + 8).unwrap_or(0) as usize;
103 let va = u32_at(img, s + 12).unwrap_or(0) as usize;
104 end = end.max(va + vsz);
105 }
106 end
107}
91fn parse_and_relocate(img: &[u8], mem: usize) -> Result<usize, LoadError> { 108fn parse_and_relocate(img: &[u8], mem: usize) -> Result<usize, LoadError> {
92 // ---- DOS header ---- 109 // ---- DOS header ----
93 if u16_at(img, 0) != Some(DOS_MAGIC) { 110 if u16_at(img, 0) != Some(DOS_MAGIC) {
@@ -151,7 +168,9 @@ fn parse_and_relocate(img: &[u8], mem: usize) -> Result<usize, LoadError> {
151 if n == 0 { 168 if n == 0 {
152 continue; 169 continue;
153 } 170 }
154 let src = img.get(s.raw_ptr..s.raw_ptr + n).ok_or(LoadError::BadSectionTable)?; 171 let src = img
172 .get(s.raw_ptr..s.raw_ptr + n)
173 .ok_or(LoadError::BadSectionTable)?;
155 unsafe { 174 unsafe {
156 let dst = (mem + s.virt_addr) as *mut u8; 175 let dst = (mem + s.virt_addr) as *mut u8;
157 core::ptr::copy_nonoverlapping(src.as_ptr(), dst, n); 176 core::ptr::copy_nonoverlapping(src.as_ptr(), dst, n);
@@ -185,7 +204,9 @@ fn parse_and_relocate(img: &[u8], mem: usize) -> Result<usize, LoadError> {
185 unsafe { 204 unsafe {
186 let p = target as *mut u64; 205 let p = target as *mut u64;
187 let v = p.read_unaligned(); 206 let v = p.read_unaligned();
188 p.write_unaligned(v.wrapping_add((mem as u64).wrapping_sub(image_base as u64))); 207 p.write_unaligned(
208 v.wrapping_add((mem as u64).wrapping_sub(image_base as u64)),
209 );
189 } 210 }
190 } 211 }
191 _ => return Err(LoadError::BadReloc), 212 _ => return Err(LoadError::BadReloc),
@@ -213,17 +234,22 @@ pub fn load_image(bytes: &[u8]) -> Result<Image, LoadError> {
213 return Err(LoadError::NotPe); 234 return Err(LoadError::NotPe);
214 } 235 }
215 236
216 let page_count = bytes.len().div_ceil(PAGE_SIZE); 237 // The memory span is the VIRTUAL layout: the highest section RVA +
238 // virt_size (headers live at rva 0). Sections are scattered across
239 // RVAs, so this can exceed the file size.
240 let image_span = image_span_bytes(bytes);
241 let page_count = image_span.div_ceil(PAGE_SIZE);
217 // Allocate physical pages at a fixed window (identity mapping makes 242 // Allocate physical pages at a fixed window (identity mapping makes
218 // the link address = runtime address after relocation). 243 // the link address = runtime address after relocation).
219 let base = crate::memory::alloc_pages_above(page_count, LOAD_BASE).ok_or(LoadError::AllocFailed)?; 244 let base =
245 crate::memory::alloc_pages_above(page_count, LOAD_BASE).ok_or(LoadError::AllocFailed)?;
220 if base >= LOAD_LIMIT { 246 if base >= LOAD_LIMIT {
221 crate::memory::free_pages(base, page_count); 247 crate::memory::free_pages(base, page_count);
222 return Err(LoadError::TooBig); 248 return Err(LoadError::TooBig);
223 } 249 }
224 // Identity-map them user+writable+nx-default (relocations need write; 250 // Identity-map user+writable: single-privilege-level kernel, no W^X —
225 // we do not enforce W^X here — single-privilege-level kernel). 251 // the image contains code that must execute and data that relocates.
226 if !paging::map_range(base, page_count, paging::PAGE_USER | paging::PAGE_WRITABLE | paging::PAGE_NX) { 252 if !paging::map_range(base, page_count, paging::PAGE_USER | paging::PAGE_WRITABLE) {
227 crate::memory::free_pages(base, page_count); 253 crate::memory::free_pages(base, page_count);
228 return Err(LoadError::MapFailed); 254 return Err(LoadError::MapFailed);
229 } 255 }
@@ -250,15 +276,27 @@ pub fn load_image(bytes: &[u8]) -> Result<Image, LoadError> {
250/// Zero the part of each section between raw_size and virt_size (BSS). 276/// Zero the part of each section between raw_size and virt_size (BSS).
251fn zero_bss_tails(img: &[u8], mem: usize) { 277fn zero_bss_tails(img: &[u8], mem: usize) {
252 // Re-walk the section table minimally (headers are small). 278 // Re-walk the section table minimally (headers are small).
253 let Some(e_lfanew) = u32_at(img, 0x3C) else { return }; 279 let Some(e_lfanew) = u32_at(img, 0x3C) else {
280 return;
281 };
254 let coff = e_lfanew as usize + 4; 282 let coff = e_lfanew as usize + 4;
255 let Some(num_sections) = u16_at(img, coff + 2) else { return }; 283 let Some(num_sections) = u16_at(img, coff + 2) else {
256 let Some(opt_size) = u16_at(img, coff + 16) else { return }; 284 return;
285 };
286 let Some(opt_size) = u16_at(img, coff + 16) else {
287 return;
288 };
257 let sect_tab = coff + 20 + opt_size as usize; 289 let sect_tab = coff + 20 + opt_size as usize;
258 for i in 0..num_sections as usize { 290 for i in 0..num_sections as usize {
259 let Some(virt_size) = u32_at(img, sect_tab + i * 40 + 8) else { return }; 291 let Some(virt_size) = u32_at(img, sect_tab + i * 40 + 8) else {
260 let Some(raw_size) = u32_at(img, sect_tab + i * 40 + 16) else { return }; 292 return;
261 let Some(virt_addr) = u32_at(img, sect_tab + i * 40 + 12) else { return }; 293 };
294 let Some(raw_size) = u32_at(img, sect_tab + i * 40 + 16) else {
295 return;
296 };
297 let Some(virt_addr) = u32_at(img, sect_tab + i * 40 + 12) else {
298 return;
299 };
262 if virt_size > raw_size { 300 if virt_size > raw_size {
263 unsafe { 301 unsafe {
264 core::ptr::write_bytes( 302 core::ptr::write_bytes(