Hello all,
I started programming a bootloader in ZIG and I already got relatively far.
As I have some knowledge about how they work but never implemented one by myself, I used this Github Repository as example.
But there, in src/bootloader/src/loader.c line 176, it seems like the raw program headers buffer (type *anyopaque
in my ZIG code) are converted to an array of program headers, but the type definition Elf32_Phdr (see src/bootloader/src/include/elf.h) seems to describe only the type of one program header.
Now my question: How could I do this casting from *anyopaque
to an array of *Elf32_Phdr
?
Looking forwards,
Samuel Fiedler
Hey Samuel - welcome back 
Let’s start by getting the source code in front of us.
/**
* @brief The 32-bit ELF program header.
*/
typedef struct s_elf32_phdr {
UINT32 p_type;
UINT32 p_offset;
UINT32 p_vaddr;
UINT32 p_paddr;
UINT32 p_filesz;
UINT32 p_memsz;
UINT32 p_flags;
UINT32 p_align;
} Elf32_Phdr;
You may already be aware of this, but the typedef in this example does not actually declare an instance of a struct. If we didn’t have the typedef, we’d have to instead write struct s_elf32_phdr
everywhere. Instead, we can just write Elf32_Phdr
because it’s been defined as a type.
To be clear… this could be a misunderstanding:
Actually, it only describes a type of header - there are no instances being created here.
Before we go forward, I’d like to make sure we’re on the same page 
In my program, I just had Elf32_Phdr
as struct name.
Yes, it only describes the type of a program header… wrote wrong things… fixed
1 Like
Okay, good to know. So I’m now a bit confused as to what the exact issue is. The way the question reads seems like it was trying to go from what was declared vs what actually exists.
Can you spend a moment reformulating the question?
Here’s where I’m a bit lost - in C, it’s quite common to cast the head of a buffer to a single pointer and then manipulate that pointer from there. For instance, if I have a buffer of int
, I would cast the first address of that buffer to a single int pointer and then everytime I increment that pointer, I get the int at the next address.
Is this what you’re asking about? If not, I need a bit more help understanding your question.
It seems like the problem can be closed after some zig translate-c:
Original C code:
typedef struct s_elf32_phdr {
int p_type;
int p_offset;
int p_vaddr;
int p_paddr;
int p_filesz;
int p_memsz;
int p_flags;
int p_align;
} Elf32_Phdr;
int dosmthwithheader(int val) {
return val * 0;
}
int test(void* kernel_program_headers_buffer) {
/** Program headers pointer. */
Elf32_Phdr* program_headers = (Elf32_Phdr*)kernel_program_headers_buffer;
int p = 0;
for(p = 0; p < 10; p++) {
if(program_headers[p].p_type == 0) {
dosmthwithheader(program_headers[p].p_offset);
dosmthwithheader(program_headers[p].p_filesz);
dosmthwithheader(program_headers[p].p_memsz);
dosmthwithheader(program_headers[p].p_paddr);
}
}
}
Relevant zig code:
pub const struct_s_elf32_phdr = extern struct {
p_type: c_int = @import("std").mem.zeroes(c_int),
p_offset: c_int = @import("std").mem.zeroes(c_int),
p_vaddr: c_int = @import("std").mem.zeroes(c_int),
p_paddr: c_int = @import("std").mem.zeroes(c_int),
p_filesz: c_int = @import("std").mem.zeroes(c_int),
p_memsz: c_int = @import("std").mem.zeroes(c_int),
p_flags: c_int = @import("std").mem.zeroes(c_int),
p_align: c_int = @import("std").mem.zeroes(c_int),
};
pub const Elf32_Phdr = struct_s_elf32_phdr;
pub export fn dosmthwithval(arg_val: c_int) c_int {
var val = arg_val;
return val * @as(c_int, 0);
}
pub export fn test (arg_kernel_program_headers_buffer: ?*anyopaque) c_int {
var kernel_program_headers_buffer = arg_kernel_program_headers_buffer;
// this was my problem
var program_headers: [*c]Elf32_Phdr = @as([*c]Elf32_Phdr, @ptrCast(@alignCast(kernel_program_headers_buffer)));
var p: c_int = 0;
{
p = 0;
while (p < @as(c_int, 10)) : (p += 1) {
if ((blk: {
const tmp = p;
if (tmp >= 0) break :blk program_headers + @as(usize, @intCast(tmp)) else break :blk program_headers - ~@as(usize, @bitCast(@as(isize, @intCast(tmp)) +% -1));
}).*.p_type == @as(c_int, 0)) {
_ = dosmthwithval((blk: {
const tmp = p;
if (tmp >= 0) break :blk program_headers + @as(usize, @intCast(tmp)) else break :blk program_headers - ~@as(usize, @bitCast(@as(isize, @intCast(tmp)) +% -1));
}).*.p_offset);
_ = dosmthwithval((blk: {
const tmp = p;
if (tmp >= 0) break :blk program_headers + @as(usize, @intCast(tmp)) else break :blk program_headers - ~@as(usize, @bitCast(@as(isize, @intCast(tmp)) +% -1));
}).*.p_filesz);
_ = dosmthwithval((blk: {
const tmp = p;
if (tmp >= 0) break :blk program_headers + @as(usize, @intCast(tmp)) else break :blk program_headers - ~@as(usize, @bitCast(@as(isize, @intCast(tmp)) +% -1));
}).*.p_memsz);
_ = dosmthwithval((blk: {
const tmp = p;
if (tmp >= 0) break :blk program_headers + @as(usize, @intCast(tmp)) else break :blk program_headers - ~@as(usize, @bitCast(@as(isize, @intCast(tmp)) +% -1));
}).*.p_paddr);
}
}
}
return 0;
}
I mainly wanted to ensure that I understood the type of program_headers correctly because the C code was iterating over a variable that was not explicitely declared as an array AFAIK.
But now the problem is solved, thanks to the great feature of zig to get zig code from c code!
1 Like
Awesome - good luck with the bootloader! Keep us posted.
1 Like