Skip to main content

in_memory/
in_memory.rs

1//! Links objects a program produced in memory, and gets the executable back
2//! as bytes: no temporary files.
3//!
4//! ```sh
5//! cargo run --example in_memory            # prints what it linked
6//! cargo run --example in_memory -- answer  # also writes ./answer
7//! ```
8//!
9//! Two ways of passing inputs are shown:
10//!
11//! 1. anonymous buffers ([`InputKind::bytes`]), built into [`LinkOptions`]
12//!    directly;
13//! 2. files by path ([`MemoryFiles`]), for running an ordinary command
14//!    line, `-l` search included, against files that only exist in memory.
15
16#[path = "support/objects.rs"]
17mod objects;
18
19use std::sync::Arc;
20
21use qld::args::{InputAttrs, InputKind, LinkOptions, OutputBuffer, OutputKind};
22use qld::diag::Collect;
23use qld::input::MemoryFiles;
24
25fn main() -> qld::Result<()> {
26    // Object code from a compiler or JIT in the same process.
27    let main_o = objects::main_object();
28    let answer_o = objects::answer_object(42);
29
30    // 1. Buffers as inputs, the output into a buffer.
31    let mut options = LinkOptions::new();
32    options.kind = OutputKind::StaticExecutable;
33    options.push_input(
34        InputKind::bytes("main.o", main_o.clone()),
35        InputAttrs::default(),
36    );
37    options.push_input(
38        InputKind::bytes("answer.o", answer_o.clone()),
39        InputAttrs::default(),
40    );
41    let buffer = OutputBuffer::new();
42    options.output_buffer = Some(buffer.clone());
43    let diagnostics = Collect::new();
44    qld::link(&options, &diagnostics)?;
45    let first = buffer.take().expect("a successful link fills the buffer");
46    report("buffers", &first);
47
48    // 2. A command line whose files live in memory: `main.o`, and
49    //    `libanswer.a` in a search directory that does not exist on disk.
50    let library = objects::archive(&[("answer.o", &answer_o, &["answer"])]);
51    let files = MemoryFiles::new()
52        .with("main.o", main_o)
53        .with("/virtual/lib/libanswer.a", library);
54    let argv = ["ld", "-static", "-L/virtual/lib", "main.o", "-lanswer"];
55    let qld::ParseOutcome::Link(options) = qld::parse_gnu(&argv)? else {
56        unreachable!("a link command line");
57    };
58    let mut options = *options;
59    options.input_provider = Some(Arc::new(files));
60    let buffer = OutputBuffer::new();
61    options.output_buffer = Some(buffer.clone());
62    qld::link(&options, &diagnostics)?;
63    let second = buffer.take().expect("a successful link fills the buffer");
64    report("memory files", &second);
65    assert_eq!(first, second, "same program either way");
66
67    for diagnostic in diagnostics.take_sorted() {
68        eprintln!("{}: {}", diagnostic.severity, diagnostic.message);
69    }
70    if let Some(path) = std::env::args_os().nth(1) {
71        std::fs::write(&path, &first).map_err(|error| qld::Error::io(&path, error))?;
72        #[cfg(unix)]
73        {
74            use std::os::unix::fs::PermissionsExt;
75            std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o755))
76                .map_err(|error| qld::Error::io(&path, error))?;
77        }
78        println!("wrote {} (it exits with status 42)", path.to_string_lossy());
79    }
80    Ok(())
81}
82
83fn report(how: &str, image: &[u8]) {
84    let entry = objects::entry_point(image).unwrap_or(0);
85    println!(
86        "{how}: {} byte x86-64 executable, entry point {entry:#x}",
87        image.len()
88    );
89}