Hi everyone!
I am pretty new to Zig, and I am using version 0.15.0-dev.1034+bd97b6618.
I have been fiddling around UEFI and Zig kinda makes things a lot more approachable because of cross-compilation. But I am getting an error while running my code that tries to use File.getInfo(). Here is an example of what I am doing and don’t mind my messy code:
const file_size = root_dir.getInfoSize(uefi.protocol.File.Info.file) catch {
return uefi.Status.aborted;
};
var info_buf: [83]u8 = undefined;
const result = uefi.system_table.boot_services.?.allocatePool(
.loader_data,
file_size,
@alignCast(@ptrCast(&info_buf)),
);
if (result != uefi.Status.success) {
return uefi.Status.aborted;
}
_ = root_dir.getInfo(
uefi.protocol.File.Info.file,
&info_buf,
) catch {
return uefi.Status.aborted;
};
I try to compile and I get a pointer alignment error inside the /usr/lib/zig/lib/std/os/uefi/protocol/file.zig file, related to the standard library.
$ zig build
install
└─ install main
└─ compile exe main Debug x86_64-uefi-msvc 1 errors
/usr/lib/zig/lib/std/os/uefi/protocol/file.zig:228:47: error: @ptrCast increases pointer alignment
.success => return @as(*InfoType, @ptrCast(buffer.ptr)),
^~~~~~~~~~~~~~~~~~~~
/usr/lib/zig/lib/std/os/uefi/protocol/file.zig:228:62: note: '[*]u8' has alignment '1'
.success => return @as(*InfoType, @ptrCast(buffer.ptr)),
~~~~~~^~~~
/usr/lib/zig/lib/std/os/uefi/protocol/file.zig:228:47: note: '*os.uefi.protocol.file.File.Info.File' has alignment '8'
/usr/lib/zig/lib/std/os/uefi/protocol/file.zig:228:47: note: use @alignCast to assert pointer alignment
referenced by:
main: src/main.zig:127:29
EfiMain: /usr/lib/zig/lib/std/start.zig:219:42
3 reference(s) hidden; use '-freference-trace=5' to see all references
Now, it could totally an issue of mine, but wouldn’t the error occur on my own code? Is this a bug of mine or is it something wrong in the standard library? Since the build is from the main branch, I agree that there could be some errors. And I don’t mind opening an issue on Github, but I would prefer to actually understand if this is really an error on the Zig’s standard library first.
Thanks
getInfo is using your buf as backing memory for info type it returns, which requires it to be a valid alignment for the type, 8 will work with all of them.
You can align arrays like so: info_buf: [83]u8 align(8).
It also happens to be the alignment that allocatePool requires.
You should ensure data is aligned correctly before @alignCast, while the docs say it modifies the alignment, I’m fairly certain that’s only at the type level i.e. it reinterprets the alignment of the type, instead of changing the alignment of the actual pointer as that would mean changing the address. The fact that a safety check that the pointer is aligned as promised seems to confirm that (also some testing I did just now and prior experience).
It’s pure luck that the buf was in a location that’s aligned to 8, otherwise you would have crashed on illegal behaviour.
1 Like
Hi!
Thank you so much for your response and tips!
I actually did a test like that, but still got the same issue. I even tried to simplify:
var info_buf: [83]u8 align(8) = undefined;
_ = root_dir.getInfo(
uefi.protocol.File.Info.file,
&info_buf,
) catch {
return uefi.Status.aborted;
};
And I still get the same problem. Am I missing something?
I read what you said about allocatePool, which makes sense! The buffer parameter is declared like this:
allocatePool: *const fn (pool_type: MemoryType, size: usize, buffer: *[*]align(8) u8) callconv(cc) Status,
The align(8) is where it should be, so I understand what allocatePool actually needs. But getInfo doesn’t have any kind of information about the alignment in it’s declaration, at all:
pub fn getInfo(
self: *const File,
comptime info: std.meta.Tag(Info),
buffer: []u8,
) GetInfoError!*@FieldType(Info, @tagName(info))
Alongside the fact that, even though I did define info_buf has having an alignment of 8 bytes, I still get the error, I am finding this behaviour rather odd.
So my question is still standing: is it me, or is it the std library? If the problem is on my side, what can I do?
Thank you!
There are two issues here, one that isn’t your fault and one that is.
Issue 1:
The alignment problem is absolutely the stdlib’s fault, and it’s worth filing an issue over. It looks like it was introduced in this commit, where they go from pointer casting InfoType => u8 (which is fine) to pointer casting u8 => InfoType (which increases alignment).
This is why testing is useful… I’m surprised that it took three months to catch this, given that you literally can’t use the function in any way after this change.
Issue 2:
This is wrong:
const result = uefi.system_table.boot_services.?.allocatePool(
.loader_data,
file_size,
@alignCast(@ptrCast(&info_buf)),
);
You are casting (pointer to [83]u8) to (pointer to a many-item pointer to u8). allocatePool stores the pointer to the allocation in the value pointed to by buffer. So, you are essentially telling allocatePool to write the result pointer to the first few bytes of your [83]u8, which is obviously not your intention.
This is a perfect example of why any usage of @ptrCast should be analyzed carefully. When @ptrCast is not absolutely needed, you probably shouldn’t use it.
Here’s a version of the code that does what I think you were trying to do:
const file_size = root_dir.getInfoSize(uefi.protocol.File.Info.file) catch {
return uefi.Status.aborted;
};
var info_buf_ptr: [*]align(8) u8 = undefined;
const result = uefi.system_table.boot_services.?.allocatePool(
.loader_data,
file_size,
&info_buf_ptr,
);
if (result != uefi.Status.success) {
return uefi.Status.aborted;
}
const info_buf: []align(8) u8 = info_buf_ptr[0..83];
// this will still result in a compile error right now,
// but it should be fine once the alignment issue is fixed
_ = root_dir.getInfo(
uefi.protocol.File.Info.file,
info_buf,
) catch {
return uefi.Status.aborted;
};
3 Likes
Thank you so much @milogreg !
Thank you for the explanation and examples. Zig and its types are still something that I have to read about in the documentation over and over again. But you explanation made it very clear!
Also thanks for confirming my suspicions! I will proceed and open an issue on Github!