pharo-vm on NetBSD/amd64

Pharo 9のVMNetBSD/amd64でも動かせるようになったのでメモ。

f:id:steleto:20210815201040p:plain

source

リポジトリhttps://github.com/pharo-project/opensmalltalk-vm にあるが、こちらはVMMakerで自動生成されたコードは入っていない。自動生成させるにはpharo-vmが必要なので、「pharo-vmバイナリを作るのにpharo-vmのソースが必要で、pharo-vmのソースを作るのにpharo-vmバイナリが必要で……」という循環参照が発生する。

幸い自動生成されたコードも含めたものが http://files.pharo.org/vm/pharo-spur64-headless/Linux-x86_64/source/ にあるので、こっちから持ってくれば問題ない。ディレクトリ名に「headless」とあるけどcmakeのオプション指定で非headlessにできるので問題なし。

patch

とりあえず動くようにはなったけど、これで正解なのかは知らない。

  • "cmake/<OS>.cmake" を要求するので、とりあえず "cmake/OpenBSD.cmake" を "cmake/NetBSD.cmake" にコピー

  • backtrace(3) を使用しているので libexecinfo が必要になる

--- CMakeLists.txt.orig
+++ CMakeLists.txt
@@ -367,6 +367,7 @@
 check_library_exists(dl dlopen "" HAVE_LIBDL)
 check_library_exists(dyld dlopen "" HAVE_DYLD)
 check_struct_has_member("struct tm" tm_gmtoff time.h HAVE_TM_GMTOFF)
+check_library_exists(execinfo backtrace "" HAVE_EXECINFO)
 
 #Required by the UUID Plugin
 
@@ -451,6 +452,10 @@
 add_executable(${VM_EXECUTABLE_NAME} ${VM_FRONTEND_APPLICATION_TYPE} ${VM_FRONTEND_SOURCES})
 addLibraryWithRPATH(${VM_LIBRARY_NAME} ${VM_SOURCES})
 
+if(${HAVE_EXECINFO})
+   target_link_libraries(${VM_LIBRARY_NAME} execinfo)
+endif()
+
 #
 # LibFFI
 #
  • uuidgen(2) を使用する(同時に libuuid の uuid_generate との衝突を回避)
    • もともと uuidgen(2) を使用するコードがあったのでなるべくそちらを使うように頑張ったけど、やっぱり面倒だったので libuuid の uuid_generate に統一しても良いんじゃないかと思った次第。
--- CMakeLists.txt.orig
+++ CMakeLists.txt
@@ -374,6 +374,6 @@
 check_include_files(uuid/uuid.h HAVE_UUID_UUID_H)
 check_include_files(uuid.h HAVE_UUID_H)
-check_library_exists(uuid uuidgen "" HAVE_UUIDGEN)
+check_function_exists(uuidgen HAVE_UUIDGEN)
 check_library_exists(uuid uuid_generate "" HAVE_UUID_GENERATE)
 
 
--- plugins.cmake.orig
+++ plugins.cmake
@@ -77,7 +77,7 @@
     addLibraryWithRPATH(UUIDPlugin ${UUIDPlugin_SOURCES})
     if(WIN)
         target_link_libraries(UUIDPlugin PRIVATE "-lole32")
-    elseif(UNIX AND NOT OSX)
+    elseif(UNIX AND NOT OSX AND HAVE_UUID_GENENATE)
        #find_path(LIB_UUID_INCLUDE_DIR uuid.h PATH_SUFFIXES uuid)
         find_library(LIB_UUID_LIBRARY uuid)
         message(STATUS "Using uuid library:" ${LIB_UUID_LIBRARY})
  • uuidgen(2) は native byte-order なので、 uuid_generate に合わせて big endian に強制する
    • やっぱり uuid_generate に統一したほうが楽そう
--- plugins/UUIDPlugin/common/UUIDPlugin.c.orig
+++ plugins/UUIDPlugin/common/UUIDPlugin.c
@@ -41,6 +41,9 @@
 
    #if defined(HAVE_UUIDGEN)
      uuidgen(&uuid, 1);
+     uuid.time_low = htobe32(uuid.time_low);
+     uuid.time_mid = htobe16(uuid.time_mid);
+     uuid.time_hi_and_version = htobe16(uuid.time_hi_and_version);
    #else
      uuid_generate(uuid);
    #endif
  • clone(2) と名前が衝突したので、とりあえず sqclone へ変更
--- generated/64/vm/src/gcc3x-cointerp.c.orig
+++ generated/64/vm/src/gcc3x-cointerp.c
@@ -1068,7 +1068,7 @@
 extern sqInt classUnsafeAlien(void);
 static void clearLeakMapAndMapAccessibleFreeSpace(void);
 static sqInt NoDbgRegParms cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning);
-extern sqInt clone(sqInt objOop);
+extern sqInt sqclone(sqInt objOop);
 extern sqInt compactClassIndexOf(sqInt objOop);
 static sqInt NoDbgRegParms copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i);
 extern void countMarkedAndUnmarkdObjects(sqInt printFlags);
@@ -46834,7 +46834,7 @@
 
    /* SpurMemoryManager>>#clone: */
 sqInt
-clone(sqInt objOop)
+sqclone(sqInt objOop)
 {   DECL_MAYBE_SQ_GLOBAL_STRUCT
     sqInt classIndex;
     sqInt classIndex1;
@@ -54408,14 +54408,14 @@
 
    clone1 = (((longAt(obj1)) & (classIndexMask())) == ClassMethodContextCompactIndex
        ? cloneContext(obj1)
-      : clone(obj1));
+       : sqclone(obj1));
    if (!(clone1)) {
        error("Not enough space to copy the objects in two-way become. This should have been detected before");
        return;
    }
    clone2 = (((longAt(obj2)) & (classIndexMask())) == ClassMethodContextCompactIndex
        ? cloneContext(obj2)
-      : clone(obj2));
+       : sqclone(obj2));
    if (!(clone2)) {
        error("Not enough space to copy the objects in two-way become. This should have been detected before");
        return;
@@ -75835,7 +75835,7 @@
        else {
            if ((GIV(argumentCount) == 0)
             || (!(((longAt(rcvr)) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))) == 0))) {
-              newCopy = clone(rcvr);
+               newCopy = sqclone(rcvr);
            }
            else {
                newCopy = 0;
  • クラッシュレポートの NetBSD/amd64 対応(クラッシュさせたことがないので合っているか不明)
--- src/debugUnix.c.orig
+++ src/debugUnix.c
@@ -16,6 +16,12 @@
 
 #endif
 
+#if __NetBSD__
+
+#include <ucontext.h>
+
+#endif
+
 
 #ifdef HAVE_EXECINFO_H
 # include <execinfo.h>
@@ -250,6 +256,20 @@
            regs->mc_edi, regs->mc_edi, regs->mc_ebp, regs->mc_esp,
            regs->mc_eip);
    return regs->mc_eip;
+#elif __NetBSD__ && __x86_64__
+   __greg_t *regs = &uap->uc_mcontext.__gregs;
+   fprintf(output,
+           "\trax 0x%08llx rbx 0x%08llx rcx 0x%08llx rdx 0x%08llx\n"
+           "\trdi 0x%08llx rsi 0x%08llx rbp 0x%08llx rsp 0x%08llx\n"
+           "\tr8  0x%08llx r9  0x%08llx r10 0x%08llx r11 0x%08llx\n"
+           "\tr12 0x%08llx r13 0x%08llx r14 0x%08llx r15 0x%08llx\n"
+           "\trip 0x%08llx\n",
+           regs[_REG_RAX], regs[_REG_RBX], regs[_REG_RCX], regs[_REG_RDX],
+           regs[_REG_RDI], regs[_REG_RSI], regs[_REG_RBP], regs[_REG_RSP],
+           regs[_REG_R8 ], regs[_REG_R9 ], regs[_REG_R10], regs[_REG_R11],
+           regs[_REG_R12], regs[_REG_R13], regs[_REG_R14], regs[_REG_R15],
+           regs[_REG_RIP]);
+   return (void *)regs[_REG_RIP];
 #elif __linux__ && __x86_64__
    greg_t *regs = uap->uc_mcontext.gregs;
    fprintf(output,
@@ -409,6 +429,9 @@
 # elif __FreeBSD__ && __i386__
            void *fp = (void *)(uap ? uap->uc_mcontext.mc_ebp: 0);
            void *sp = (void *)(uap ? uap->uc_mcontext.mc_esp: 0);
+#elif __NetBSD__
+           void *fp = (void *)(uap ? _UC_MACHINE_FP(uap) : 0);
+           void *sp = (void *)(uap ? _UC_MACHINE_SP(uap) : 0);
 # elif __OpenBSD__
            void *fp = (void *)(uap ? uap->sc_rbp: 0);
            void *sp = (void *)(uap ? uap->sc_rsp: 0);
  • 起動スクリプトで LD_LIBRAY_PATHに @pkglibdir@ (後で /usr/pkg/lib に置換)を追加
--- packaging/linux/bin/launch.sh.in.orig
+++ packaging/linux/bin/launch.sh.in
@@ -18,8 +18,8 @@
 *) PLUGINS="`pwd`/$BIN"
 esac
 
-if [ $(uname -s) = "OpenBSD" ]; then
-  LD_LIBRARY_PATH="$PLUGINS:${LD_LIBRARY_PATH}" exec $GDB "$BIN/@VM_EXECUTABLE_NAME@" "$@"
+if [ $(uname -s) = "OpenBSD" -o $(uname -s) = "NetBSD" ]; then
+  LD_LIBRARY_PATH="$PLUGINS:@pkglibdir@:${LD_LIBRARY_PATH}" exec $GDB "$BIN/@VM_EXECUTABLE_NAME@" "$@"
 fi
 
 # On some linuxes there multiple versions of the C library.  If the image uses

cmake option

  • -DFLAVOUR=CoInterpreter: デフォルト値と同じだけどJenkinsfileが指定してたので。
  • -DALWAYS_INTERACTIVE=1: 非headlessなVMが必要な場合に指定する。これもJenkinsfileで指定されていたので。
  • -DGENERATE_SOURCES=OFF: ソースの自動生成を行わなくする。
  • -DPHARO_BIN_LOCATION=${PREFIX}/lib/pharo 起動スクリプトがここで指定されたバイナリを実行する。

after build

build/vm/* がバイナリとライブラリ、 build/packaging/linux/bin/pharo が起動スクリプトなので適当に回収する。

なお build/vm/pharoJITで動く関係か paxctl -m で PaX MPROTECT を解除しておく必要がる。