Zig: build-exe: ability to output raw binary or hex format rather than ELF

Created on 5 Jul 2019  路  11Comments  路  Source: ziglang/zig

Sometimes it is desirable to get the raw machine code out of an ELF and use that. This example depends on objcopy to do that, but it should be doable with only zig:

https://github.com/andrewrk/clashos/blob/3f786d4294c71f909b1ecaf3c8d0e247b90c5c86/build.zig#L32-L37

I think this would probably be more options to --emit. Current options are:

  • llvm-ir
  • bin (ELF or PE)
  • asm

Unfortunately, bin is a bit ambiguous here. I think it would be OK to add:

  • ihex (equivalent to objcopy -O ihex)
  • raw (eqivalent to objcopy -O binary)

We could leave bin the same, or potentially try to come up with a less ambiguous name.

As for how to implement it - LLVM in theory should have a way to specify this, since it's actually less work to generate raw machine code than to wrap it in an ELF. But worst case scenario, zig could do the processing after the fact.

If we end up having to implement intel hex format, it could be written in zig itself, the same way that zig fmt and the new translate-c work.

contributor friendly enhancement stage1

Most helpful comment

I will look into that issue soon

All 11 comments

I really like the idea of zig to directly output raw oder intel hex formats!

What would also be favourable: To output PE under linux and ELF under windows to cross-compile for different platforms.

This could finally allow to have a build system that doesn't suck at "one project, multiple platforms" without a hassle.

What would also be favourable: To output PE under linux and ELF under windows to cross-compile for different platforms.

That's already possible, see here.
I just tried running this build.zig on my linux machine and it produced a .exe for windows:

const Builder = @import("std").build.Builder;
const builtin = @import("builtin");

pub fn build(b: *Builder) void {
    const mode = b.standardReleaseOptions();
    const exe = b.addExecutable("my_exe", "src/main.zig");
    exe.setBuildMode(mode);
    exe.setTarget(
        builtin.Arch.x86_64,
        builtin.Os.windows,
        builtin.Abi.msvc,
    );
    exe.install();
}

This would also be desirable to be able to write shellcode with zig.

I will look into that issue soon

this would probably be more options to --emit

How should this co-exist with https://github.com/ziglang/zig/issues/2279#issuecomment-571802541 ?

--emit is deprecated;

I am using installRaw and then running a zig program to convert to intel hex (for the microbit.)

The build function does "addSystemCommand("zig", "build", "makehex.zig") which works, but I'd like to add the makehex function directly as a build step. Is there a builder function that can add a zero parameter/return !void function?

https://github.com/markfirmware/zig-bare-metal-microbit/blob/master/makehex.zig
https://github.com/markfirmware/zig-bare-metal-microbit/blob/master/build.zig

See also: COFF

I think it would be very useful if this could be implemented in a way where you could write your own custom code to generate output files. In our environment we need the "verilog" output of objcopy. Maybe that's too niche for Zig.. but only raw binary and intel hex is a bit artificially limited as well.

So somehow you would provide a function that takes a pointer to parsed ELF data, and an output file, and you'd be responsible for writing what you want to that file.

Another useful output for us is symbol tables (--sym output of objdump).. with this kind of general purpose tool, we could perhaps write symbol table in a format that's more useful for us, rather than trying to parse the output of objdump

You can already do that, the raw output mode is entirely implemented in "user space" and not baked into the compiler.

@LemonBoy You're talking about this right? https://github.com/ziglang/zig/blob/master/lib/std/build/emit_raw.zig

I saw that, so what I was thinking is making it more easy to define a custom output step, where the ELF related stuff is already handled for you.

And the other side of making it easier would be documentation.. but then I guess that's a big TODO for the whole build system anyway

Right now I guess you can't even reuse the "BinaryElfOutput" related stuff, since it's not "pub" right? And you wouldn't know to look for it in emit_raw.zig anyway. But that would have been useful

If you have an interface that gives you an ELF file reader rather than a path, you could potentially have an optimization where the ELF file is only read to memory once for all output steps

LLVM in theory should have a way to specify this

In fact, it does. You can pass --oformat=binary to LLD and it will output raw binary data (equivalent to objcopy -O binary).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fengb picture fengb  路  3Comments

daurnimator picture daurnimator  路  3Comments

DavidYKay picture DavidYKay  路  3Comments

andersfr picture andersfr  路  3Comments

jayschwa picture jayschwa  路  3Comments