m4b/faerie

[Feature Request] Ability to specify load addresses (generate ProgramHeaders, Segments)

kitlith opened this issue · 9 comments

For my usecase, I'm wrapping a memory dump in an ELF and bundling it with available symbols so that I can easilly open it up in something like ghidra. Unfortunately, what is currently output is a partially linked file, which I can pass through a linker with a linker script that does almost nothing, but is an extra step that I would prefer to remove.

This would require (at minimum!) the ability to set the location of sections and ability to set the address of the entrypoint.

m4b commented

Yea, so I started work on a pure rust in-memory minimal linker a few times, but I lost interest/got distracted because it gets extremely complicated very quickly, along with lots of undocumented issues, and other tedious things. (Although this was before faerie and goblin writer structs, so it might be easier now.)

I still think it would be very cool; but adding even "minor" linker requires one to effectively create a full linker, in my experience.

This might be somewhat easier now that goblin has all the writer values + faerie does a lot of the heavy lifting with symbol collection and other boring things.

I think it would probably be feasible to add a fully linked step, but i suspect it will be a pretty invasive PR? and a non-trivial amount of work. Or maybe not? I'm not sure.

But it would be quite cool to do this:

    let mut obj = ArtifactBuilder::new(target)
        .name(args.filename.clone())
        .link(SharedLibrary) // or .link(PieExecutable) or .link(StaticExecutable)
        .finish();

well, at least in my case, I don't need to do any actual linking. i just need a way to specify the memory location of section(s) and specify an entry point. but, it is definitely better to design for the future on this point, and I'm not familiar enough with the field to come up with a good design.

m4b commented

i see; so if I understand correctly, you effectively want to specify the entry point field in the object file? i think that's somewhat unusual, since object files don't really have an entry point, since they aren't relocated and linked.

For section locations, they should always start at 0x40 and then proceed upwards depending on size (and probably alignment, i think)

essentially, i have a pre-linked blob that loads at a static address. what i want is an executable, not an object file per-se. The difference between the two files I uploaded in my PR appears to just be in the header(s) and a bit of padding.

m4b commented

got it; so almost a sort of passthrough linker, in that it takes the artifact info, and without relocating, just simply emits an executable with some given entry point.

this seems like a somewhat niche usecase, but it would also be first start for writing out a linked binary, so subsequent work in that area, if it ever happens, would probably be able to re-use it?

i wonder if it's reasonable for you to just emit the bytes instead of into the file, and then alter the header to be an exe, along with some other things. if it really is just passed through, it might be as simple as writing the Elf header to have your entry point + make the binary type executable...?

m4b commented

E.g., something like:

let mut obj = artifact.to_bytes::<Elf>()?;
let mut header = obj.pread::<Header>(0);
header.e_type = header::ET_EXEC;
header.e_entry = 0xdeadbeef;
obj.pwrite(header, 0)?;

that might be reasonable for me to do. I'll also need to assign the section an address, but it shouldn't be too difficult to do on top of that.

Ah, a program header is also missing. I bet that's (part of?) the """extra padding""" i mentioned. It specifies the load address of the section, etc. I could... append this to the end of the file when doing it manually? i kinda forgot that elf headers can just be placed wherever you like. hmmm...

I have a working incantation: https://gist.github.com/kitlith/a26a1021c268b34bef60d57cfc6cc19f#file-wasm_lib-rs-L50-L83

Nevertheless, I think it would be nice if we could instruct faerie to generate program headers of some sort.

EDIT: yeah i'm pretty sure that missing ProgramHeaders are my main issue, rather than relocatable vs executable and other stuff. ProgramHeaders are optional in relocatables, which means they are still allowed to occur. I'm having some ideas here, maybe an optional artifact.locate() to parallel artifact.declare() and artifact.define()?

EDIT again: for mach-o, this would require generating segments and specifying their vmaddr :/