1use crate::util::get_null_index;
25use crate::{AuxVar, AuxVarRaw, AuxVarType};
26use aligned_vec::{ABox, AVec};
27use alloc::string::String;
28use alloc::vec::Vec;
29use core::ffi::CStr;
30
31#[derive(Clone, Debug, Default, PartialEq, Eq)]
36pub struct StackLayoutBuilder<'a> {
37 argv: Vec<String>,
38 envv: Vec<String>,
39 auxv: Vec<AuxVar<'a>>,
40}
41
42impl<'a> StackLayoutBuilder<'a> {
43 #[must_use]
45 pub const fn new() -> Self {
46 Self {
47 argv: vec![],
48 envv: vec![],
49 auxv: vec![],
50 }
51 }
52
53 pub fn add_argv(mut self, arg: impl Into<String>) -> Self {
58 let mut arg = arg.into();
59 if let Some(pos) = arg.find('\0') {
60 assert_eq!(
61 pos,
62 arg.len() - 1,
63 "strings must not contain interim NUL bytes"
64 );
65 }
66
67 if !arg.ends_with('\0') {
68 arg.push('\0');
69 }
70
71 self.argv.push(arg);
72 self
73 }
74
75 pub fn add_envv(mut self, env: impl Into<String>) -> Self {
83 let mut env = env.into();
84 if let Some(pos) = env.find('\0') {
85 assert_eq!(
86 pos,
87 env.len() - 1,
88 "strings must not contain interim NUL bytes"
89 );
90 }
91
92 if !env.ends_with('\0') {
93 env.push('\0');
94 }
95
96 {
98 let (key, _value) = env
99 .split_once('=')
100 .expect("should have ENV var syntax (`key=value`)");
101 assert!(!key.is_empty());
102 }
103 self.envv.push(env);
104 self
105 }
106
107 #[must_use]
109 pub fn add_auxv(mut self, aux: AuxVar<'a>) -> Self {
110 if aux != AuxVar::Null {
112 self.auxv.push(aux);
113 }
114 self
115 }
116
117 fn calc_len_argv_entries(&self) -> usize {
121 (self.argv.len() + 1) * size_of::<usize>()
122 }
123
124 fn calc_len_envv_entries(&self) -> usize {
128 (self.envv.len() + 1) * size_of::<usize>()
129 }
130
131 fn calc_len_auxv_entries(&self) -> usize {
135 (self.auxv.len() + 1) * size_of::<AuxVarRaw>()
136 }
137
138 fn _calc_len_data_cstr(strs: &[String]) -> usize {
139 strs.iter()
140 .map(|arg| arg.as_bytes())
141 .map(|bytes| CStr::from_bytes_until_nul(bytes).expect("should have NUL byte"))
142 .map(|cstr| cstr.count_bytes() + 1 )
143 .sum::<usize>()
144 }
145
146 fn calc_len_argv_data(&self) -> usize {
150 Self::_calc_len_data_cstr(&self.argv)
151 }
152
153 fn calc_len_envv_data(&self) -> usize {
157 Self::_calc_len_data_cstr(&self.envv)
158 }
159
160 fn calc_len_auxv_data(&self) -> usize {
164 self.auxv
165 .iter()
166 .map(|aux| {
167 match aux {
168 AuxVar::Platform(v) => {
169 v.count_bytes() + 1 }
171 AuxVar::BasePlatform(v) => {
172 v.count_bytes() + 1 }
174 AuxVar::Random(v) => {
175 assert_eq!(v.len(), 16);
176 16 }
178 AuxVar::ExecFn(v) => {
179 v.count_bytes() + 1 }
181 _ => 0,
182 }
183 })
184 .sum::<usize>()
185 }
186
187 fn calc_total_len(&self) -> usize {
191 size_of::<usize>() +
192 self.calc_len_argv_entries()
193 + self.calc_len_envv_entries()
194 + self.calc_len_auxv_entries()
195 + self.calc_len_argv_data()
196 + self.calc_len_envv_data()
197 + self.calc_len_auxv_data()
198 }
199
200 #[must_use]
207 pub fn build(mut self, target_addr: Option<usize>) -> ABox<[u8]> {
208 if Some(&AuxVar::Null) != self.auxv.last() {
209 self.auxv.push(AuxVar::Null);
210 }
211
212 let mut buffer = {
215 let len = self.calc_total_len();
216 let mut vec = AVec::<u8>::new(align_of::<usize>());
217 for _ in 0..len {
218 vec.push(0);
219 }
220 vec.into_boxed_slice()
221 };
222
223 let target_addr = target_addr.unwrap_or(buffer.as_ptr() as usize);
226
227 let mut serializer = StackLayoutSerializer::new(
228 &mut buffer,
229 target_addr,
230 self.calc_len_argv_entries(),
231 self.calc_len_envv_entries(),
232 self.calc_len_auxv_entries(),
233 self.calc_len_argv_data(),
234 self.calc_len_envv_data(),
235 self.calc_len_auxv_data(),
236 );
237 serializer.write_argc(self.argv.len());
238
239 for arg in self.argv {
240 let c_str = CStr::from_bytes_until_nul(arg.as_bytes()).unwrap();
241 serializer.write_arg(c_str);
242 }
243 for var in self.envv {
246 let c_str = CStr::from_bytes_until_nul(var.as_bytes()).unwrap();
247 serializer.write_env(c_str);
248 }
249 for var in self.auxv {
252 serializer.write_aux(&var);
253 }
254
255 buffer
256 }
257}
258
259struct StackLayoutSerializer<'a> {
267 buffer: &'a mut [u8],
268 offset_argv: usize,
270 offset_envv: usize,
272 offset_auxv: usize,
274 offset_argv_data: usize,
276 offset_envv_data: usize,
278 offset_auxv_data: usize,
280 target_addr: usize,
281}
282
283impl<'a> StackLayoutSerializer<'a> {
284 #[allow(clippy::too_many_arguments)]
292 fn new(
293 buffer: &'a mut [u8],
294 target_addr: usize,
295 len_argv_entries: usize,
296 len_envv_entries: usize,
297 len_auxv_entries: usize,
298 len_argv_data: usize,
299 len_envv_data: usize,
300 len_auxv_data: usize,
301 ) -> Self {
302 assert_eq!(buffer.as_ptr().align_offset(align_of::<usize>()), 0);
303
304 let total_size = size_of::<usize>() + len_argv_entries + len_envv_entries + len_auxv_entries
305 + len_argv_data + len_envv_data + len_auxv_data;
306 assert!(buffer.len() >= total_size);
307
308 let offset_argv = size_of::<usize>() ;
310 let offset_envv = offset_argv + len_argv_entries;
311 let offset_auxv = offset_envv + len_envv_entries;
312 let offset_auxv_data = offset_auxv + len_auxv_entries;
314 let offset_argv_data = offset_auxv_data + len_auxv_data;
315 let offset_envv_data = offset_argv_data + len_argv_data;
316
317 Self {
318 buffer,
319 offset_argv: size_of::<usize>(), offset_envv,
321 offset_auxv,
322 offset_argv_data,
323 offset_envv_data,
324 offset_auxv_data,
325 target_addr,
326 }
327 }
328
329 fn sanity_checks(&self) {
331 assert!(self.offset_argv <= self.offset_envv);
332 assert!(self.offset_envv <= self.offset_auxv);
333 assert!(self.offset_auxv <= self.offset_auxv_data);
334 assert!(self.offset_auxv_data <= self.offset_argv_data);
335 assert!(self.offset_argv_data <= self.offset_envv_data);
336 assert!(self.offset_envv_data <= self.buffer.len());
337 }
338
339 const fn _write_data_area(buffer: &mut [u8], data: &[u8], data_area_offset: &mut usize) {
341 let src_ptr = data.as_ptr();
342 let dst_ptr = buffer.as_mut_ptr().cast::<u8>();
343 let dst_ptr = unsafe { dst_ptr.add(*data_area_offset) };
344 unsafe {
345 core::ptr::copy_nonoverlapping(src_ptr, dst_ptr, data.len());
346 }
347 *data_area_offset += data.len();
348 }
349
350 const fn _write_cstr(
353 buffer: &mut [u8],
354 str: &CStr,
355 entry_offset: &mut usize,
356 data_area_offset: &mut usize,
357 target_addr: usize,
358 ) {
359 let data_addr = target_addr + *data_area_offset;
361
362 {
364 let src_ptr = buffer.as_mut_ptr().cast::<u8>();
365 let src_ptr = unsafe { src_ptr.add(*entry_offset) };
366 unsafe { core::ptr::write(src_ptr.cast::<usize>(), data_addr) }
367 *entry_offset += size_of::<usize>();
368 }
369
370 Self::_write_data_area(buffer, str.to_bytes(), data_area_offset);
372 Self::_write_data_area(buffer, &[0], data_area_offset);
374 }
375
376 fn write_argc(&mut self, argc: usize) {
378 unsafe { core::ptr::write(self.buffer.as_mut_ptr().cast::<usize>(), argc) }
379
380 self.sanity_checks();
381 }
382
383 fn write_arg(&mut self, arg: &CStr) {
385 Self::_write_cstr(
386 self.buffer,
387 arg,
388 &mut self.offset_argv,
389 &mut self.offset_argv_data,
390 self.target_addr,
391 );
392 self.sanity_checks();
393 }
394
395 fn write_env(&mut self, var: &CStr) {
397 Self::_write_cstr(
398 self.buffer,
399 var,
400 &mut self.offset_envv,
401 &mut self.offset_envv_data,
402 self.target_addr,
403 );
404
405 self.sanity_checks();
406 }
407
408 fn write_aux_immediate(&mut self, key: AuxVarType, val: usize) {
410 let ptr = self.buffer.as_mut_ptr().cast::<u8>();
411 let ptr = unsafe { ptr.add(self.offset_auxv) };
412 let value = AuxVarRaw::new(key, val);
413 unsafe { core::ptr::write(ptr.cast::<AuxVarRaw>(), value) }
414 self.offset_auxv += size_of::<AuxVarRaw>();
415 }
416
417 fn write_aux_refdata(&mut self, key: AuxVarType, data: &[u8], add_nul_byte: bool) {
420 let data_addr = self.target_addr + self.offset_auxv_data;
422 self.write_aux_immediate(key, data_addr);
423
424 Self::_write_data_area(self.buffer, data, &mut self.offset_auxv_data);
426
427 if add_nul_byte {
429 if let Some(pos) = get_null_index(data) {
432 assert_eq!(
433 pos,
434 data.len() - 1,
435 "strings must not contain interim NUL bytes"
436 );
437 }
438
439 if data.last().copied().unwrap() != 0 {
440 Self::_write_data_area(self.buffer, &[0], &mut self.offset_auxv_data);
442 }
443 }
444 }
445
446 fn write_aux(&mut self, aux: &AuxVar<'a>) {
449 match aux {
450 AuxVar::Platform(v) => self.write_aux_refdata(aux.key(), v.as_bytes(), true),
451 AuxVar::BasePlatform(v) => self.write_aux_refdata(aux.key(), v.as_bytes(), true),
452 AuxVar::Random(v) => self.write_aux_refdata(aux.key(), v, false),
453 AuxVar::ExecFn(v) => self.write_aux_refdata(aux.key(), v.as_bytes(), true),
454 _ => self.write_aux_immediate(aux.key(), aux.value_raw()),
455 }
456
457 self.sanity_checks();
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use crate::StackLayoutRef;
465
466 #[test]
467 fn test_builder() {
468 let builder = StackLayoutBuilder::new()
469 .add_argv("first arg")
470 .add_argv("second arg")
471 .add_envv("var1=foo")
472 .add_envv("var2=bar")
473 .add_auxv(AuxVar::EGid(1_1337))
474 .add_auxv(AuxVar::Gid(2_1337))
475 .add_auxv(AuxVar::Random([
476 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
477 ]))
478 .add_auxv(AuxVar::Uid(3_1337))
479 .add_auxv(AuxVar::ExecFn(c"ExecFn as &CStr".into()))
480 .add_auxv(AuxVar::Platform("Platform as &str".into()))
481 .add_auxv(AuxVar::BasePlatform("Base Platform as &str".into()));
482 let layout = builder.build(None);
483
484 let layout = StackLayoutRef::new(layout.as_ref(), None);
486
487 assert_eq!(layout.argc(), 2);
488
489 {
491 assert_eq!(layout.argv_raw_iter().count(), 2);
492
493 unsafe { layout.argv_iter() }
496 .enumerate()
497 .for_each(|(i, str)| eprintln!(" arg {i:>2}: {str:?}"));
498 }
499
500 {
502 assert_eq!(layout.envc(), 2);
503
504 unsafe { layout.envv_iter() }
507 .enumerate()
508 .for_each(|(i, ptr)| eprintln!(" env {i:>2}: {ptr:?}"));
509 }
510
511 {
513 assert_eq!(layout.auxvc(), 7);
514
515 unsafe { layout.auxv_iter() }
518 .enumerate()
519 .for_each(|(i, aux)| eprintln!(" auxv {i:>2}: {aux:?}"));
520 }
521
522 let fn_get_at_string = |key: AuxVarType| {
524 unsafe { layout.auxv_iter() }
525 .find(|e| e.key() == key)
526 .map(|v| {
527 v.value_payload_str()
528 .cloned()
529 .map(|s| s.into_string())
530 .unwrap()
531 })
532 .unwrap()
533 };
534 let at_exec_fn = fn_get_at_string(AuxVarType::ExecFn);
535 assert_eq!(at_exec_fn, "ExecFn as &CStr");
536
537 let at_base_platform = fn_get_at_string(AuxVarType::BasePlatform);
538 assert_eq!(at_base_platform, "Base Platform as &str");
539 }
540}