4

Using Zig and Translate-C to understand weird C code

 2 years ago
source link: https://zig.news/sobeston/using-zig-and-translate-c-to-understand-weird-c-code-4f8
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
Cover image for Using Zig and Translate-C to understand weird C code

Using Zig and Translate-C to understand weird C code

Oct 9

・2 min read

void f(int *a) {<br>   void *p = &a;<br>   ***(int *(*)[])p = 1;<br> }

@rep_stosq_void on Twitter posted this strange sample of C code, and I wanted to show my process of understanding this contrived C code.
https://twitter.com/rep_stosq_void/status/1446504706319294475

After running zig translate-c x.c > x.zig, I got the following output:

pub export fn f(arg_a: [*c]c_int) void {
    var a = arg_a;
    var p: ?*c_void = @ptrCast(?*c_void, &a);
    @ptrCast(
        [*c][*c]c_int,
        @alignCast(@import("std").meta.alignment([*c]c_int), &@ptrCast(
            [*c][*c][*c]c_int,
            @alignCast(@import("std").meta.alignment([*c][*c]c_int), p),
        ).*),
    ).*.* = 1;
}
Enter fullscreen modeExit fullscreen mode

From here I removed the unnecessary calls to std.meta.alignment by adding alignment data to p. I also converted [*c]T pointers to [*]T and *T, depending on my assumptions of how the code works. I also removed the optional from p, and stripped away arg_a.

Now we've got something a bit more faithful to the original.

fn f(a: *c_int) void {
    const p = @ptrCast(*align(@alignOf(usize)) const c_void, &a);
    @ptrCast(
        *const [*]c_int,
        &@ptrCast(*const *[*]c_int, p).*,
    ).*.* = 1;
}
Enter fullscreen modeExit fullscreen mode

We can now transform &@ptrCast(*const *[*]c_int, p).* into @ptrCast(*const *[*]c_int, p), as &x.* is equivalent to x.

fn g(a: *c_int) void {
    const p = @ptrCast(*align(@alignOf(usize)) const c_void, &a);
    @ptrCast(
        *const [*]c_int,
        @ptrCast(*const *[*]c_int, p),
    ).*.* = 1;
}
Enter fullscreen modeExit fullscreen mode

As we can see p is a pointer to the argument a, we can change its ptrCast to the more appropriate type *const *i32. The const is needed as arguments are immutable.

fn h(a: *c_int) void {
    const p = @ptrCast(*const *i32, &a);
    @ptrCast(
        *const [*]c_int,
        @ptrCast(*const *[*]c_int, p),
    ).*.* = 1;
}
Enter fullscreen modeExit fullscreen mode

After substituting in p.

fn i(a: *c_int) void {
    @ptrCast(
        *const [*]c_int,
        @ptrCast(
            *const *[*]c_int,
            @ptrCast(*const *i32, &a),
        ),
    ).*.* = 1;
}
Enter fullscreen modeExit fullscreen mode

There's some many-pointers ([*]T) here, which we should replace with single pointers (*T). This is because I can tell that there aren't really any arrays at play here.

fn j(a: *c_int) void {
    @ptrCast(
        *const *c_int,
        @ptrCast(
            *const **c_int,
            @ptrCast(*const *i32, &a),
        ),
    ).*.* = 1;
}
Enter fullscreen modeExit fullscreen mode

Here we can remove the unnecessary ptrCast around &a, as &a is already of type *const *i32.

fn k(a: *c_int) void {
    @ptrCast(
        *const *c_int,
        @ptrCast(
            *const **c_int,
            &a,
        ),
    ).*.* = 1;
}
Enter fullscreen modeExit fullscreen mode

Here we can see a ptrCast from *const *i32 to *const **c_int, back to *const *c_int. Let's remove that.

fn l(a: *c_int) void {
    (&a).*.* = 1;
}
Enter fullscreen modeExit fullscreen mode

Finally, we can transform (&x).* into x.

fn m(a: *c_int) void {
    a.* = 1;
}
Enter fullscreen modeExit fullscreen mode

Perhaps the answer is disappointing.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK