Studying assembly output

After a few excellent weeks playing with zig, I feel like going one step further and looking at assembly output, comparing design choices.

Do you have suggestions for tools and workflows ? I’ve used godbolt a bit, but it is really slow and rather tedious to use on my small laptop screen …

1 Like

Well zig build-exe, zig build-lib, etc have -femit-asm which is pretty useful.

1 Like

Yes, I tried that yesterday but I got lost in the huge amount of code generated. Are you using tools to make the analysis easier ? For example matching a function with its aim counterpart ?

1 Like

As an option:

zig build-exe file.zig -fstrip
objdump -d file > file.s
1 Like

Below is a comparison of output files’ sizes (in bytes)
depending on compilation options (just out of curiosity):

                                          h       h.s
-femit-asm -O ReleaseSafe          : 2_030_376 5_784_599
-femit-asm                         : 1_926_728 5_696_145
-femit-asm -O ReleaseFast          : 1_787_672 4_473_742
-femit-asm -fstrip                 :    53_912   320_732
-femit-asm -O ReleaseSafe  -fstrip :    17_048    60_478
-femit-asm -O ReleaseFast  -fstrip :    12_952    27_023
-femit-asm -O ReleaseSmall         :     8_856    32_355
-femit-asm -O ReleaseSmall -fstrip :     8_856    32_355

h.zig:

const std = @import("std");
pub fn main() void {
    std.debug.print("Hello!\n", .{});
}

h - executable
h.s - asm

3 Likes

Note that strip is enabled by default for -OReleaseSmall, and you can use -fno-strip in combination with -OReleaseSmall.

4 Likes

I usually compile with ReleaseFast, with .pdb enabled, and put a breakpoint in the function that I want to analyze. Depending on what it is you’re trying to examine, ReleaseSmall might be better, simply because the code is smaller, but then you have to take into consideration what Andrew mentioned above.
I usually do this with VSCode, but binary debugging is an afterthought there, it’s not very ergonomic. When I need to get serious with disassembly, I use x64dbg. It’s intended for debugging with no source code, so the way it displays binary information is excellent.

3 Likes

Adding to the previous comment, you can use rizin - which is a fork of radare2 - instead of x64dbg as a more cross-platform alternative (e.g. Linux). Set a @breakpoint(), compile with ReleaseFast and hop into your binary with rizin -d <exe>, dc to run your program and then print the disassembly with pd. You can also enter visual mode and look around the assembly. What puts this apart from disassembling with lldb or objdump directly is that it prints some cool branch target lines so that you can see where your loops are. Also the visual mode is cool to look around, like the tui mode in gdb (lldb has smth similar… not sure). They also have a GUI (Cutter). I noticed that zig aggressively inlines your functions so looking at the assembly it is really hard to tell where you are, lldb and rizin show some source code comments on the right but its still no 1-to-1 mapping.

Tbh I was just having fun looking at assembly, but just wanted to put it out here nevertheless , even though its been 2 years :>

4 Likes