#include <dlfcn.h>
#include <mach-o/dyld.h>
#include <mach-o/loader.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>

static double now_ms(void) {
    struct timespec ts;
    clock_gettime(CLOCK_MONOTONIC, &ts);
    return ts.tv_sec * 1000.0 + ts.tv_nsec / 1e6;
}

int main(int argc, char **argv) {
    if (argc < 3) { fprintf(stderr, "usage: %s dlopen|nsobject|mmapdlopen path\n", argv[0]); return 2; }
    const char *mode = argv[1];
    const char *path = argv[2];
    if (strcmp(mode, "dlopen") == 0) {
        double t0 = now_ms();
        void *h = dlopen(path, RTLD_NOW | RTLD_LOCAL);
        double t1 = now_ms();
        printf("dlopen %.1f ms handle=%p err=%s\n", t1 - t0, h, h ? "" : dlerror());
        return h ? 0 : 1;
    }
    if (strcmp(mode, "nsobject") == 0) {
        FILE *f = fopen(path, "rb");
        if (!f) { perror("fopen"); return 1; }
        fseek(f, 0, SEEK_END); long n = ftell(f); fseek(f, 0, SEEK_SET);
        void *buf = malloc(n);
        if (fread(buf, 1, n, f) != (size_t)n) { perror("fread"); return 1; }
        fclose(f);
        double t0 = now_ms();
        NSObjectFileImage image = NULL;
        NSObjectFileImageReturnCode rc = NSCreateObjectFileImageFromMemory(buf, n, &image);
        double t1 = now_ms();
        printf("NSCreateObjectFileImageFromMemory rc=%d (%.1f ms) image=%p\n", rc, t1 - t0, image);
        if (rc != NSObjectFileImageSuccess) return 1;
        double t2 = now_ms();
        NSModule mod = NSLinkModule(image, path, NSLINKMODULE_OPTION_BINDNOW | NSLINKMODULE_OPTION_RETURN_ON_ERROR);
        double t3 = now_ms();
        printf("NSLinkModule %.1f ms mod=%p err=%s\n", t3 - t2, mod, mod ? "" : "see NSLinkEditError");
        return mod ? 0 : 1;
    }
    if (strcmp(mode, "mmapdlopen") == 0) {
        int fd = open(path, O_RDONLY);
        if (fd < 0) { perror("open"); return 1; }
        char procfd[64];
        snprintf(procfd, sizeof procfd, "/dev/fd/%d", fd);
        double t0 = now_ms();
        void *h = dlopen(procfd, RTLD_NOW | RTLD_LOCAL);
        double t1 = now_ms();
        printf("dlopen(/dev/fd) %.1f ms handle=%p err=%s\n", t1 - t0, h, h ? "" : dlerror());
        return h ? 0 : 1;
    }
    fprintf(stderr, "unknown mode\n");
    return 2;
}
