Using @intFromPtr to get address of linker variable does not work

I am trying to write startup code for a microcontroller (IMXRT1062) in which I have to define an array with u32s that describe my image (IVT, ImageVectorTable, see table 9-37 in the manual), such as where in flash it is stored and how long it is, what the address of the first instruction is etc.

Some of these values are created by the linker through a custom linker script. So in zig I declare them as extern variables:

extern const _flash_img_start: u32;
extern const _flash_img_length: u32;

The linker transfers these values setting the addresses of the variables to the corresponding values. This means that when you take the address of these variables, you get the correct values. In C, this would look like (uint32_t) &_flash_img_start .
In zig, I would expect it to be almost as easy, like this:

export const boot_data linksection(".boot_data") = [_]u32{
    // start Absolute address of the image
    @intFromPtr(&_flash_img_start),

    // length Size of the program image
    @intFromPtr(&_flash_img_length),

    // plugin Plugin flag (see Plugin image)
    0,
};

But when I do that I get:

startup.zig:17:5: error: unable to evaluate comptime expression
    @intFromPtr(&_flash_img_start),
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
startup.zig:17:17: note: operation is runtime due to this operand
    @intFromPtr(&_flash_img_start),
                ^~~~~~~~~~~~~~~~~

I also tried something like this:

fn addr(linker_value: anytype) u32 {
    const ptr: *const u8 = @ptrCast(linker_value);
    return @bitCast(ptr);
}
...
export const boot_data linksection(".boot_data") = [_]u32{
    // start Absolute address of the image
    addr(&_flash_img_start),
...

but then I get the error:

startup.zig:10:21: error: cannot @bitCast from '*const u8'
    return @bitCast(ptr);
                    ^~~
startup.zig:10:21: note: use @intFromPtr to cast to 'u32'
startup.zig:17:9: note: called from here
    addr(&_flash_img_start),
    ~~~~^~~~~~~~~~~~~~~~~~~

Is this intended behavior? Am I missing something?

I feel like this is something that should be simple… :sweat_smile:

Try something this (I’m using *u32 as the parameter to test on godbolt, you can change that):

fn addr(linker_value: *u32) u32 {
    const ptr: *const u8 = @ptrCast(linker_value);
    return @intCast(@intFromPtr(ptr));
}

@ptrFromInt returns a usize that cannot be directly casted to a u32 for reasons I’m sure you can imagine. Zig is strict about silently casting between integer types. I cannot speak to what usize evaluates to on your specific platform, but your compiler is warning you that you’re getting back a bigger integer than you’re trying to return.

Also, welcome to the forum :slight_smile:

Thanks! :slightly_smiling_face:

I tried your suggestion but that still gives the same error:

startup.zig:10:21: error: unable to evaluate comptime expression
    return @intCast(@intFromPtr(ptr));
                    ^~~~~~~~~~~~~~~~
startup.zig:10:33: note: operation is runtime due to this operand
    return @intCast(@intFromPtr(ptr));
                                ^~~
startup.zig:17:9: note: called from here
    addr(&_flash_img_start),
    ~~~~^~~~~~~~~~~~~~~~~~~

Could the problem be that I try to define an array at compile time with link time values? And if so, how would I work around that?

Yes, I was referring to this error here:

startup.zig:10:21: error: cannot @bitCast from '*const u8'
    return @bitCast(ptr);
                    ^~~
startup.zig:10:21: note: use @intFromPtr to cast to 'u32'
startup.zig:17:9: note: called from here
    addr(&_flash_img_start),

@ptrFromInt is a runtime expression. What you’re creating there is an array literal (think of it like a string literal). What I’m taking away from your issue is that the addresses of the integers you’re trying to take cannot be resolved at compile time.

You can make a runtime array by using var instead of const and assigning the values in. In other words, declare your declare your array as var and maybe make a helper function that you can call and assign those values directly into place. That’s one possible solution, I’d have to draft more examples to think of other approaches.

Yeah now that I think about it, the addr function also has to be resolved at compile time, because the ‘boot_data’ array must be included in the program binary and therefore not be filled at runtime. The MCU ROM reads from it to know how to load my program from flash.

For reference, this is how something like this is implemented in C:

I could off course generate this whole array in the linker script, but I’d rather have it in zig.

This might actually be the same issue as Cannot evaluate @ptrToInt at comptime · Issue #15080 · ziglang/zig · GitHub

1 Like

It looks like the issue was added to the milestones - not sure when that means it will be completed. Sorry for the inconvenience, looks like it’s a work in progress.

As a workaround, try to declare the pointer directly:

export const boot_data linksection(".boot_data") = [_][*c] const u32{
    // start Absolute address of the image
    &_flash_img_start,

    // length Size of the program image
    &_flash_img_length,

    // plugin Plugin flag (see Plugin image)
    null,
};
3 Likes

Thanks, that works! :slight_smile:

1 Like