Skip to content

errno seemingly lost during fstatat when -target with -gnu is specified #17034

@squeek502

Description

@squeek502

Zig Version

0.12.0-dev.244+f4c9e19bc

Steps to Reproduce and Observed Behavior

When linking glibc, the fstatat error seems to get lost, and some other error is returned from getErrno/c._errno().* instead (seen ESUCCESS and EEXIST, but expected ENOENT).

Test code:

const std = @import("std");

pub fn main() !void {
    {
        // this gives a bogus errno
        var stat = std.mem.zeroes(std.c.Stat);
        const rc = std.c.fstatat64(std.os.AT.FDCWD, "test_file_that_doesnt_exist", &stat, 0);
        const err = std.c.getErrno(rc);
        std.debug.print("glibc: {} {}\n", .{ rc, err });
    }

    {
        // this gives the correct errno
        var stat = std.mem.zeroes(std.os.linux.Stat);
        const rc = std.os.linux.fstatat(std.os.AT.FDCWD, "test_file_that_doesnt_exist", &stat, 0);
        const err = std.os.linux.getErrno(rc);
        std.debug.print("syscall: {} {}\n", .{ rc, err });
    }
}

built with:

zig build-exe fstatat.zig -lc -target x86_64-linux-gnu

output:

glibc: -1 os.linux.errno.generic.E.SUCCESS
syscall: 18446744073709551614 os.linux.errno.generic.E.NOENT

commented strace with only the relevant parts:

// std.c.fstatat64
newfstatat(AT_FDCWD, "test_file_that_doesnt_exist", 0x7ffc3675cfd8, 0) = -1 ENOENT (No such file or directory)
gettid()                                = 117441
write(2, "SUCCESS", 7SUCCESS)                  = 7

// std.os.linux.fstatat
newfstatat(AT_FDCWD, "test_file_that_doesnt_exist", 0x7ffc3675d0f8, 0) = -1 ENOENT (No such file or directory)
write(2, "NOENT", 5NOENT)                    = 5

(so both syscalls are the same, but somehow the glibc version doesn't

The same problem exists in the equivalent C code compiled by Zig:

#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
    struct stat statbuf;

    int rc = fstatat(AT_FDCWD, "test_file_that_doesnt_exist", &statbuf, 0);

    printf("%d %d\n", rc, errno);
}
$ zig build-exe fstatat.c -lc -target x86_64-linux-gnu -femit-bin=fstatat-c

$ ./fstatat-c
-1 0

$ ldd ./fstatat-c
    linux-vdso.so.1 (0x00007ffed69c3000)
    libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f77c7504000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f77c7312000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f77c754e000)

but the same C code works as expected when compiled with clang directly:

$ clang fstatat.c -o fstatat-clang

$ ./fstatat-clang
-1 2

$ ldd ./fstatat-clang
    linux-vdso.so.1 (0x00007ffd5dbc1000)
    libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc74afa3000)
    /lib64/ld-linux-x86-64.so.2 (0x00007fc74b1c1000)

(2 is ENOENT)

The strace between the Zig-compiled C version and the Clang-compiled C version are the same:

// zig compiled
newfstatat(AT_FDCWD, "test_file_that_doesnt_exist", 0x7ffd8b185998, 0) = -1 ENOENT (No such file or directory)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}) = 0
brk(NULL)                               = 0x855000
brk(0x876000)                           = 0x876000
write(1, "-1 0\n", 5-1 0
)                   = 5

// clang compiled
newfstatat(AT_FDCWD, "test_file_that_doesnt_exist", 0x7fff3985d0b0, 0) = -1 ENOENT (No such file or directory)
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0x1), ...}) = 0
brk(NULL)                               = 0x558433e61000
brk(0x558433e82000)                     = 0x558433e82000
write(1, "-1 2\n", 5-1 2
)                   = 5

Expected Behavior

fstatat errors to be accurately returned when linking glibc

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugObserved behavior contradicts documented or intended behavioros-linuxLinux

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions