I’m brand new to zig. I just started using it this past weekend and decided to jump straight into embedded with it. Overall if you’re experienced with setting up an embedded build with with make or cmake then setting up an embedded zig project is pretty straight forward. I’d never used clang before this so I had a bit of a learning curve there. The linker files that ST provide are for gcc so there are some small modifications needed.
My setup is a build targeting an stm32 using ST’s hal libraries for the drivers and zig 0.12.0 for the application. Currently it just blinks an led so the code is nothing to write home about but I encountered some oddities in setting up the build so I thought I’d share and see if anyone else has had similar experiences.
The first, is that zig gives me a warning from ld.lld that my elf and an object file, ld-temp.o, are of different triples. I don’t know what ld-temp.o contains, but I assume based on the name that it has something to do with the linker. Zig marks the warning as a error, but still produces a working elf file.
ld.lld: warning: Linking two modules of different target triples: '/home/git/GPS-Tracking-Device/zig/zig-cache/o/400208bfbe7179686ede919aa975077a/gps.elf.o' is 'thumb-unknown-unknown-eabi' whereas 'ld-temp.o' is 'thumbv7m-unknown-unknown-eabi'
I’m guessing that this is zig and ld.lld/clang just using a slightly different name for the same triple, but I don’t know enough about either compiler to be sure. I tried to give clang flags that matched my resolveTargetQuery parameters.
//stm32f103
const target = b.resolveTargetQuery(.{
.cpu_arch = .thumb,
.cpu_model = .{ .explicit = &std.Target.arm.cpu.cortex_m3 },
.abi = .eabi,
.os_tag = .freestanding,
});
const c_flags = [_][]const u8{
"-g3", //max debug symbols
//"-O1", //minor optimizations
"-Wall",
"-Wextra",
"-mthumb",
"-mlittle-endian",
"-specs=nosys.specs", //don't link to libc
"-mcpu=cortex-m3",
"-mfloat-abi=soft", //Software floating point
"-ffreestanding",
//"-Wl,--verbose,-Map=bin/gps.map", //not giving me a map file for some reason
"-ffunction-sections",
"-fdata-sections",
"-nostdlib",
"-nostdinc",
};
The second oddity is the contents of my binary depending on my compiler optimization level.
Debug - This gives me a binary that is so large that it over flows my my flash memory of 64k. I think is makes sense since Debug specifically states that it creates a larger binary. I’m wondering how much value I would get out of using Debug during development if I wrote a custom panic handler that writes to uart or SWD.
install
└─ install gps.elf
└─ zig build-exe gps.elf Debug thumb-freestanding-eabi 4 errors
error: ld.lld: section '.text' will not fit in region 'FLASH': overflowed by 336648 bytes
error: ld.lld: section '.rodata' will not fit in region 'FLASH': overflowed by 353920 bytes
error: ld.lld: section '.ARM' will not fit in region 'FLASH': overflowed by 353936 bytes
error: ld.lld: section '.data' will not fit in region 'FLASH': overflowed by 353972 bytes
error: the following command failed with 4 compilation errors:
Another interesting thing about this is that if I give clang the -O1 flag it optimizes enough of the binary for it to just barely fit in flash memory.
ReleaseSafe - Since Debug gave me a huge binary I expected ReleaseSafe to give me a smaller but still large binary. I thought that a lot of the size for a Debug build was due to the safety checks. However, the resulting binary using ReleaseSafe and no clang optimizations was just under 6k. This does seem on the large side for a blinky + hal but at least its not 400k. Giving clan -O1 this time only shrinks it by 0.4k. I’m still a little foggy on how zig does safety checks and what that means exactly. What happens if zig finds a safety issue during runtime? Panic? Crash? Is ReleaseSafe supposed to be a smaller/faster alternative to Debug during development?
ReleaseFast - The binary was the exact same size as ReleaseSafe. This is probably because c currently makes up 95% of my program. I only have a handful of zig lines written.
ReleaseSmall - From the information about embedded zig I gathered it seemed like ReleaseSmall is the preferred optimization for embedded systems. That makes a lot of sense given the limited flash sizes. However, when I build with ReleaseSmall the entire content of my symbol table is missing. ReleaseSmall might be just a tad to small.
That’s pretty much it. I’m not sure how much of this are zig bugs/issues and how much of this is just me not knowing what I’m doing. I’m looking forward to this project and seeing how zig compares to c for embedded work.