UEFI debug builds generating #UD

Justin C Miller
Message ID
DKIM signature
Download raw message
Hi all,

I've been poking around with 0.6, and I'm finally trying out rewriting
my hobby OS' UEFI bootloader in Zig. I'm hitting a snag, though, and I'm
wondering if you can help. Here's a minimal example program that
exhibits the issue I'm having:

const std = @import("std");
const uefi = std.os.uefi;
const unicode = std.unicode;

pub fn main() uefi.Status {
    var buf: [64]u16 = undefined;
    var status = uefi.system_table.con_out.?.outputString(&[_:0]u16{ 'H', 'e', 'l', 'l', 'o', '!', '\r', '\n' });
    while (true) {}

This causes an #UD exception (invalid opcode) when compiled in debug
mode and run in QEMU with TianoCore. In release modes, it works fine.
Commenting out the definition of buf also causes it to work fine. Taking
a look at the generated assembly (sorry, no symbol names because I don't
know how to easily work with PDBs on linux):

Debug: https://paste.rs/7fT
Release: https://paste.rs/GnK

Here's a bit snipped from the debug version where the issue is:

 // main()
 470:	55                   	push   %rbp
 471:	48 81 ec c0 00 00 00 	sub    $0xc0,%rsp
 478:	48 8d ac 24 80 00 00 	lea    0x80(%rsp),%rbp
 47f:	00 
 480:	48 8d 45 b8          	lea    -0x48(%rbp),%rax
 484:	48 89 c1             	mov    %rax,%rcx
 487:	ba aa 00 00 00       	mov    $0xaa,%edx
 48c:	41 b8 80 00 00 00    	mov    $0x80,%r8d
 492:	e8 39 01 00 00       	callq  0x5d0
 // rest of main()...

 5d0:	48 89 c8             	mov    %rcx,%rax
 5d3:	4d 85 c0             	test   %r8,%r8
 5d6:	74 64                	je     0x63c
 5d8:	49 81 f8 80 00 00 00 	cmp    $0x80,%r8
 5df:	73 05                	jae    0x5e6
 5e1:	45 31 c9             	xor    %r9d,%r9d
 5e4:	eb 4a                	jmp    0x630
 5e6:	c5 f9 6e c2          	vmovd  %edx,%xmm0
 5ea:	4d 89 c1             	mov    %r8,%r9
 5ed:	31 c9                	xor    %ecx,%ecx
 5ef:	49 83 e1 80          	and    $0xffffffffffffff80,%r9
 5f3:	c4 e2 7d 78 c0       	vpbroadcastb %xmm0,%ymm0

The #UD happens at 5f3. I'm assuming the code that main jumps to at 5d0
is doing some safety checks, and the compiler is emitting an AVX
instruction, which are probably not enabled yet. (I can't seem to
figure out how to get at XCR0 to check for sure, though the other
control registers are set appropriately to use AVX.

I assume I'm just not setting up the UEFI build properly, but I can't
seem to figure out what I'm doing wrong. Should I be telling the
compiler to disable SIMD instructions somehow? So far I'm just doing
this in build.zig:

    const target = b.standardTargetOptions(.{
        .default_target = .{
            .os_tag = std.Target.Os.Tag.uefi,
Thanks for any insight you can provide!

Reply to thread Export thread (mbox)