/*
 *  Copyright (C) NAKAMURA Minoru <nminoru@nminoru.jp>
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <sys/mman.h>
#include <signal.h>

#define UNW_LOCAL_ONLY
#include <libunwind.h>


#ifdef __ia64__
struct fdesc {
    long code;
    long gp;
};
# define get_fdesc(fdesc,func)  (fdesc = *(struct fdesc *) &(func))
# define get_funcp(fdesc)       ((template_t) &(fdesc))
# define get_gp(fdesc)          ((fdesc).gp)
#endif


typedef void (*functype)(int);

void print_message(int a) {
    printf("invoke print_message : arg = %d\n", a);
}

void cause_null_exception(int a) {
    *(int*)0 = 0;
}

void generated(functype f, int a) {
    f(a);
}

void (*funcp)(functype f, int a);


void test(void) {
    int i;
    long pagesize = sysconf(_SC_PAGESIZE);

    char* p = (char*)mmap(0, pagesize, PROT_READ|PROT_WRITE|PROT_EXEC,
                          MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    assert(p != MAP_FAILED);

#if defined(__ia64__)
    const int generated_size = 0xa0;

    // IA-64 は関数ポインタが直接命令アドレスを指すのではなく、
    // struct fdesc のような関数ディスクリプタをさしているため
    // 少し工夫が必要。
    memcpy(p, ((long*)generated)[0], 0xa0);

    // コードをフラッシュしないとダメ
    for (i=0 ; i<generated_size ; i += 128) {
        asm volatile ("fc.i %0" : : "r"(p+i) : "memory");
        asm volatile ("sync.i"  : : : "memory");
    }

    struct fdesc fdesc;
    unw_dyn_region_info_t *region;
    unw_dyn_info_t di;

    get_fdesc(fdesc, generated);

    /* register the new function: */
    region = alloca (_U_dyn_region_info_size (2));
    region->next = NULL;
    region->insn_count = generated_size / 16 * 3;
    region->op_count   = 2;
    _U_dyn_op_alias (&region->op[0], 0, -1, fdesc.code);
    _U_dyn_op_stop (&region->op[1]);

    memset (&di, 0, sizeof (di));
    di.start_ip      = (long) p;
    di.end_ip        = (long) p + 16 * region->insn_count/3;
    di.gp            = get_gp(fdesc);
    di.format        = UNW_INFO_FORMAT_DYNAMIC;
    di.u.pi.name_ptr = (unw_word_t) "copy_of_generated";
    di.u.pi.regions  = region;

    _U_dyn_register (&di);

    fdesc.code = p;
    funcp = (functype)&fdesc;
#else
    memcpy(p, generated, pagesize);
    funcp = p;
#endif

    /* 動的コードをはさんで print_message を呼び出す */
    funcp(print_message,        123);

    /* 動的コードをはさんで cause_null_exception を呼び出す */
    funcp(cause_null_exception, 0  );
} 

int main(int argc, char** argv) {
    test();
    return 0;
}