Skip to content

strange argv handling / mangled argv ? #6

@es-fabricemarie

Description

@es-fabricemarie

I'm running Fedora 40 in a UTM VM on MacOS, on an M1 chip.

The relevant software versions I have are:

binfmt-dispatcher-0.1.2-1.fc40.aarch64
fex-emu-2412-3.fc40.aarch64

To test my setup, I made a small ls equivalent in standard C, and compiled it under Fedora 40 x86_64. I then copy it into my as /var/tmp/fexls. The source code for that test is attached as fexls.c. It expects a path argument on the command line and lists the files at that path like ls would. Again this is just to test my fex setup.

When I run /var/tmp/fexls /var/tmp from my shell, I get this:

[binfmt_dispatcher] Using FEX
argc = 4
argv[0] = '/var/tmp/fexls'
argv[1] = '/var/tmp/fexls'
argv[2] = '/var/tmp/fexls'
argv[3] = '/var/tmp/'
Usage: /var/tmp/fexls <target-directory>

Why is my argv mangled?

When I run directly (without binfmt-dispatcher) FEXInterpreter /var/tmp/fexls /var/tmp/ I get the correct output:

argc = 2
argv[0] = '/var/tmp/fexls'
argv[1] = '/var/tmp/'
drwxr-xr-x   1 abrt abrt        0 Apr 16 03:17 abrt
-rw-r--r--   1 root root     2298 Apr 16 03:19 fstab
-rw-r--r--   1 root root      280 Apr 22 20:14 .location
.... [more files] ...

My binfmt-dispatcher configuration file /etc/binfmt-dispatcher.toml is standard (I just disabled muvm only):

[defaults]
interpreter = "fex"
log_level = "info"

[muvm]
path = "/usr/bin/muvm"

[interpreters.box64]
path = "/usr/bin/box64"

[interpreters.fex]
name = "FEX"
path = "/usr/bin/FEXInterpreter"
required_paths = ["/usr/share/fex-emu/RootFS/default.erofs"]
use_muvm = false

[interpreters.qemu]
name = "QEMU User space emulator"
path = "/usr/bin/qemu-x86_64"

[interpreters.qemu-static]
name = "QEMU User space emulator (static)"
path = "/usr/bin/qemu-x86_64-static"

Am I doing something wrong? Or is there a bug here?

The C code for fexls.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <unistd.h>

void print_file_info(const char *path, const char *filename) {
    char fullpath[4096];
    snprintf(fullpath, sizeof(fullpath), "%s/%s", path, filename);

    struct stat st;
    if (lstat(fullpath, &st) == -1) {
        perror("lstat");
        return;
    }

    // File type and permissions
    char perms[11] = "----------";
    if (S_ISDIR(st.st_mode)) perms[0] = 'd';
    else if (S_ISLNK(st.st_mode)) perms[0] = 'l';
    else if (S_ISCHR(st.st_mode)) perms[0] = 'c';
    else if (S_ISBLK(st.st_mode)) perms[0] = 'b';
    else if (S_ISFIFO(st.st_mode)) perms[0] = 'p';
    else if (S_ISSOCK(st.st_mode)) perms[0] = 's';

    if (st.st_mode & S_IRUSR) perms[1] = 'r';
    if (st.st_mode & S_IWUSR) perms[2] = 'w';
    if (st.st_mode & S_IXUSR) perms[3] = 'x';
    if (st.st_mode & S_IRGRP) perms[4] = 'r';
    if (st.st_mode & S_IWGRP) perms[5] = 'w';
    if (st.st_mode & S_IXGRP) perms[6] = 'x';
    if (st.st_mode & S_IROTH) perms[7] = 'r';
    if (st.st_mode & S_IWOTH) perms[8] = 'w';
    if (st.st_mode & S_IXOTH) perms[9] = 'x';

    // User and group names
    struct passwd *pw = getpwuid(st.st_uid);
    struct group *gr = getgrgid(st.st_gid);

    // Modification time
    char timebuf[64];
    struct tm *mtm = localtime(&st.st_mtime);
    strftime(timebuf, sizeof(timebuf), "%b %e %H:%M", mtm);

    // Output format similar to ls -l
    printf("%s %3lu %s %s %8ld %s %s\n",
           perms,
           st.st_nlink,
           pw ? pw->pw_name : "???",
           gr ? gr->gr_name : "???",
           st.st_size,
           timebuf,
           filename);
}

int main(int argc, char *argv[]) {
    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; ++i) {
        printf("argv[%d] = '%s'\n", i, argv[i]);
    }
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <target-directory>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *dirpath = argv[1];
    DIR *dir = opendir(dirpath);
    if (!dir) {
        perror("opendir");
        return EXIT_FAILURE;
    }

    struct dirent *entry;
    while ((entry = readdir(dir))) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        print_file_info(dirpath, entry->d_name);
    }

    closedir(dir);
    return EXIT_SUCCESS;
}

and I compile it with: gcc -Wall -Wextra -O2 -o fexls fexls.c.

I can confirm the ELF is indeed x86_64:

$ file /var/tmp/fexls 

/var/tmp/fexls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4efe18c6f1e3fa1fffbc8347a43cb3c48d4be70d, for GNU/Linux 3.2.0, not stripped

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions