Static library causes segfault on exit

I am attempting to export a function via a static library using zig build. Here’s a link to the full project. and here’s the relevant code from my build.zig:

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    const anyline_mod = b.createModule(.{
        .root_source_file = b.path("src/root.zig"),
        .target = target,
        .optimize = optimize,
        .link_libc = true,
    });

    const anyline_lib = b.addLibrary(.{
        .name = "anyline",
        .root_module = anyline_mod,
        .linkage = .static,
    });
    b.installArtifact(anyline_lib);
}

Here’s the relevant code from root.zig

pub fn readline_zig(outlive_allocator: std.mem.Allocator, prompt: []const u8) ![]const u8 {
    //...
}

export fn readline(prompt: [*c]const u8) [*c]const u8 {
    const prompt_slice = std.mem.span(prompt);
    const line_slice = readline_zig(std.heap.raw_c_allocator, prompt_slice) catch return null;
    return @ptrCast(@alignCast(line_slice.ptr));
}

Essentially, I’m trying to expose readline_zig so that it can be linked against in a C executable (or other languages that support the C ABI.)

To test that the library was working, I used the following demo code in demo-anyline.c:

#include <stdio.h>
#include <stdlib.h>
char *readline(const char *);

int main(void)
{
    char *p;

    while ((p = readline("CLI> ")) != NULL) {
        puts(p);
        free(p);
    }

    return 0;
}

And compiled it with:

gcc demo-anyline.c -o demo-anyline libanyline.a

When I run the executable, it starts and runs just fine, but when I press CTRL-D to exit, I get a segfault: fish: Job 1, './demo-anyline' terminated by signal SIGSEGV (Address boundary error)

As a sanity check, I compiled a similar executable, but this time I used editline instead. For context, I compiled the project and copied the header and archive file into my demo project:

#include <stdio.h>
#include <stdlib.h>
//Notice the local header file instead of the forward delcaration
#include "editline.h"

int main(void)
{
    char *p;

    while ((p = readline("CLI> ")) != NULL) {
        puts(p);
        free(p);
    }

    return 0;
}

This second executable runs just fine.

Any idea on what mistake(s) I made that are causing the segfault?

readline_zig is probably not null terminating the returned slice,
fyi zig can represent terminated slice [:0]/many item ptrs [*:0] in the type system, you can specify any value to be terminating. Changing the return type to be terminated will require you to enforce that.

note that for slices the terminator is at the index of its length, terminators before that depends on if the code checks for that or not, for loops will not.

you can create terminated slices from non terminated slices/many item pointers by doing s[n..len :0] which will assert that the terminator is at the end of the slice, you have to insert it your self beforehand.

Allocator has functions for arbitrary terminated and zero terminated allocations.

I would reccomend either making a seperate function that returns a terminated slice or taking a comptime bool to change the behaviour and return type of the function.

1 Like

Didn’t see the link to the code

since you’re duping the slice anyway, you can just use dupeZ and change the return type to be zero terminated

just checked to be sure, you can change your exported function return type and prompt type to be terminated multi item pointers to further enforce this in the type system.

Its always a good idea to restrict your pointers to what they actually are supposed to be.

1 Like

Perfect! This was my exact problem. Your advice was excellent, as always!

1 Like