summaryrefslogtreecommitdiff
path: root/common/templates/clang/src/test.zig
blob: 49ff4d088c191c1a75ee289bd2ad9425220ced58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
const std = @import("std");

extern fn hello_world(action: [*c]const u8) void;

// We use global static memory for our trap. 
// Because this test runs in a single thread and we know the string is tiny, 
var captured_output: [64]u8 = undefined;
var captured_len: usize = 0;

// Exported to the dynamic symbol table so dlsym() can find it.
// Signature must accept two pointers to perfectly map to the 
// System V ABI hardware registers used by: dynamic_eval("%s", result);
// CPU registers (RDI and RSI) that the C code will use when calling this via variadic arguments.
export fn test_capture_sink(fmt: [*c]const u8, msg: [*c]const u8) void {
    // We intentionally ignore the format string ("%s") sitting in the first register
    _ = fmt; 

    // std.mem.span walks the raw C pointer until it finds the \0 null terminator.
    // It doesn't allocate memory; it just calculates the length so we have a safe Zig slice.
    const slice = std.mem.span(msg);
    
    // Copy the raw bytes directly from the C memory space into our static Zig buffer.
    @memcpy(captured_output[0..slice.len], slice);
    captured_len = slice.len;
}

test "catfish dlsym eval" {
    // We hand our C code the name of our exported Zig function. 
    // The C code will parse its own __func__, ask the OS to find "test_capture_sink",
    // and execute it, throwing the parsed string right back into our global variables.
    hello_world("test_capture_sink");

    // Reconstruct a strict Zig string from the exact number of bytes we captured
    const result = captured_output[0..captured_len];
    
    try std.testing.expectEqualStrings("Hello World!\n", result);
}