Native Client(NaCl)をやってみる (4) - dlopen(dlfcn.h)を使う -

2012-08-26T00:00:00+00:00 C JavaScript Native Client

Native Clientのデモにはdlopenっていうのが入ってるのですが、これを単純にやってみる。っていってもこれをそのまま動かすんじゃなくて、作ったtest.cをlibtest.soにして、それをNative Clientから使う(恐らくはNative Client環境のコンパイラを使わないと動かせれないんだと思われる。検証してないけど)

test.h

#ifndef TEST_H
#define TEST_H

char* say();

#endif

test.c

#include "test.h"

char* say() {
    char* s;
    s = "abcdef";

    return s;
}

でこれをlibtest.soにする。方法は後述参考

hoge.c (Native Clientのモジュール)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <dlfcn.h>
#include <pthread.h>

#include "ppapi/c/pp_errors.h"
#include "ppapi/c/pp_module.h"
#include "ppapi/c/pp_var.h"
#include "ppapi/c/ppb.h"
#include "ppapi/c/ppb_instance.h"
#include "ppapi/c/ppb_messaging.h"
#include "ppapi/c/ppb_var.h"
#include "ppapi/c/ppp.h"
#include "ppapi/c/ppp_instance.h"
#include "ppapi/c/ppp_messaging.h"

#include "test.h"

static PPB_Messaging* ppb_messaging_interface = NULL;
static PPB_Var* ppb_var_interface = NULL;

void *handle;
pthread_t t_id;

static struct PP_Var StrToVar(const char* str) {
    if (ppb_var_interface != NULL) {
        return ppb_var_interface->VarFromUtf8(str, strlen(str));
    }

    return PP_MakeUndefined();
}

static void* LoadLibrary() {
    handle = dlopen("libtest.so", RTLD_LAZY);

    return NULL;
}

static PP_Bool Instance_DidCreate(PP_Instance instance, uint32_t argc, const char* argn[], const char* argv[]) {
    if (ppb_var_interface != NULL) {
        if (pthread_create(&t_id, NULL, LoadLibrary, NULL)) {
            printf("pthread_create failedn");
        }

        return PP_TRUE;
    }

    return PP_FALSE;
}

static void Instance_DidDestroy(PP_Instance instance) {
}

static void Instance_DidChangeView(PP_Instance instance, PP_Resource view_resource) {
}

static void Instance_DidChangeFocus(PP_Instance instance, PP_Bool has_focus) {
}

static PP_Bool Instance_HandleDocumentLoad(PP_Instance instance, PP_Resource url_loader) {
    return PP_FALSE;
}

static void HandleMessage(PP_Instance instance, struct PP_Var message) {
    if (ppb_messaging_interface != NULL) {
        if (ppb_var_interface != NULL) {
            if (handle == NULL) {
                return;
            }

            char* (*say)();
            *(void **)(&say) = dlsym(handle, "say");

            char* s = (*say)();

            ppb_messaging_interface->PostMessage(instance, StrToVar((const char*)s));
        }
    }
}

PP_EXPORT int32_t PPP_InitializeModule(PP_Module a_module_id, PPB_GetInterface get_browser) {
    ppb_messaging_interface = (PPB_Messaging*)(get_browser(PPB_MESSAGING_INTERFACE));
    ppb_var_interface = (PPB_Var*)(get_browser(PPB_VAR_INTERFACE));

    return PP_OK;
}

PP_EXPORT const void* PPP_GetInterface(const char* interface_name) {
    printf("Interface: %sn", interface_name);

    if (strcmp(interface_name, PPP_INSTANCE_INTERFACE) == 0) {
        static PPP_Instance instance_interface = {
            &Instance_DidCreate,
            &Instance_DidDestroy,
            &Instance_DidChangeView,
            &Instance_DidChangeFocus,
            &Instance_HandleDocumentLoad
        };

        return &instance_interface;
    }

    if (strcmp(interface_name, PPP_MESSAGING_INTERFACE) == 0) {
        static PPP_Messaging messaging_interface = { &HandleMessage };

        return &messaging_interface;
    }

    return NULL;
}

PP_EXPORT void PPP_ShutdownModule() {
}

でNative Clientでdlopenを使用する際にメインスレッドでdlopenしてもNULLしか返ってこない模様。という事でpthreadで別スレッド内でdlopenする必要があるみたい

コンパイル

rm -fr glibc
python /opt/nacl/pepper_21/tools/oshelpers.py mkdir glibc

/opt/nacl/pepper_21/toolchain/linux_x86_glibc/bin/i686-nacl-gcc
    -c test.c
    -o glibc/libtest_x86_32.o
    -m32
    -O0
    -g
    -pthread
    -Wno-long-long
    -Wall
    -Wswitch-enum
    -Werror
    -pedantic
    -fPIC
    -DTCNAME=glibc

/opt/nacl/pepper_21/toolchain/linux_x86_glibc/bin/i686-nacl-gcc
    -Wl,-as-needed
    -o glibc/libtest_x86_32.so glibc/libtest_x86_32.o
    -m32
    -shared
    -g
    -ldl

/opt/nacl/pepper_21/toolchain/linux_x86_glibc/bin/i686-nacl-gcc
    -c hoge.c
    -o glibc/hoge_x86_32.o
    -m32
    -O0
    -pthread
    -Wno-long-long
    -Wall
    -Wswitch-enum
    -Werror
    -DTCNAME=glibc

/opt/nacl/pepper_21/toolchain/linux_x86_glibc/bin/i686-nacl-gcc
    -Wl,-as-needed
    -o glibc/hoge_x86_32.nexe glibc/hoge_x86_32.o
    -m32
    -g
    -ldl
    -lppapi

python /opt/nacl/pepper_21/tools/create_nmf.py
    -D /opt/nacl/pepper_21/toolchain/linux_x86_glibc/x86_64-nacl/bin/objdump
    -o glibc/hoge.nmf
    -L /opt/nacl/pepper_21/toolchain/linux_x86_glibc/x86_64-nacl/lib32
    -L /opt/nacl/pepper_21/toolchain/linux_x86_glibc/x86_64-nacl/lib
    glibc/hoge_x86_32.nexe glibc/libtest_x86_32.so
    -t glibc
    -s glibc
    -n libtest_x86_32.so,libtest.so

glibcディレクトリにもろもろなファイルが生成される。んでNative Clientを使う際に.nmfなファイルもglibcに入るのでHTML側からはapplication/x-naclでそれを参照する

index.html

<html>
  <body>
    <div id="message"></div>
    <script type="text/javascript">
(function(undefined) {
  window.addEventListener("message", function(e) {
    console.log(e);

    var h2 = document.createElement("h2");
    h2.innerText = e.data;

    document.getElementById("message").appendChild(h2);
  }, true);
})();

function send() {
  document.getElementById("module").postMessage(null);
}
    </script>

    <button onclick="send()">send</button>

    <embed name="nacl_module" id="module" width="0" height="0" src="/glibc/hoge.nmf" type="application/x-nacl" />
  </body>
</html>

Chrome Extension開発を勉強してみる (17) - oauth.jsを使ってDropbox API -