最近在给EJDB2rust的绑定。本文记录怎么在 Windows 下编译及正常运行EJDB2。 直接到文章末尾看结论。

安装环境

参考官方文档安装msys2mingw64。 安装开发工具链:

pacman -S --needed base-devel git vim \
      mingw-w64-x86_64-toolchain \

编译

cmake .. -DCMAKE_C_COMPILER=gcc -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./output -DBUILD_SHARED_LIBS=OFF -DENABLE_HTTP=OFF
make
make install

直接编译不通过。

  • mmap的问题 iowow项目中本身有mman相关代码platform\win32\mman,并且配置了一个CMakeLists.txt来导入mman/mman.c源代码,但是这个CMakeLists.txt没有生效,导致不能编译。 在platform\win32\win32.c中添加
#include "mman/mman.c"
  • 找不到localtime_rlog\iwlog.c文件中的localtime_r(&ts_sec, &timeinfo)修改为localtime_s(&timeinfo,&ts_sec)
#ifndef _WIN32
  localtime_r(&ts_sec, &timeinfo);
#else
  localtime_s(&timeinfo, &ts_sec);
#endif

注意:localtime_slocaltime_r的参数的顺序是相反的。

  • strndup的问题 链接时加上iberty依赖,不需要改代码。如果不想加上iberty依赖,自己定义一个strndup:
char *strndup(const char *src, size_t len)
{
  char *dst = NULL;
  if (src)
  {
    size_t src_len = strlen(src);
    int count = min(src_len, max(len, 0));
    dst = malloc(count + 1);
    if (dst)
    {
      strncpy(dst, src, count);
      dst[count] = '\0';
    }
  }
  return dst;
}

做完以上修改,应该可以编译通过了。

修复运行报错

运行编译好的可执行程序,运行报错:

ERROR iwkv.c:3590 70007|22|0|Threading error with errno status set. (IW_ERROR_THREADING_ERRNO)|Invalid argument

example代码一样报错。

虽然可以知道是哪个文件报错,但是 rc 返回值可能来自很多地方,看代码不能直接定位到具体出错的地方。70007在代码中代表pthread相关错误,所以应该是pthread某个函数调用出错了。

搜索之后发现这个错误Invalid argument是函数验证参数时抛出的错误。 于是想用_set_invalid_parameter_handlerhook出错的位置。代码如下:

use libc::c_void;

type FPHandler = Option<
    unsafe extern "C" fn(
        expr: *const u16,
        func: *const u16,
        file: *const u16,
        line: u32,
        pReserved: c_void,
    ),
>;

extern "system" {
    fn _set_invalid_parameter_handler(fp: FPHandler);
}
unsafe fn u16_ptr_to_string(ptr: *const u16) -> OsString {
    use std::os::windows::ffi::OsStringExt;
    let len = (0..).take_while(|&i| *ptr.offset(i) != 0).count();
    let slice = std::slice::from_raw_parts(ptr, len);

    OsString::from_wide(slice)
}
unsafe extern "C" fn my_handler(
    expr: *const u16,
    func: *const u16,
    file: *const u16,
    line: u32,
    pReserved: c_void,
) {
    use std::ffi::{OsStr, OsString};

    let expr = u16_ptr_to_string(expr);
    let func = u16_ptr_to_string(func);
    let file = u16_ptr_to_string(file);
    println!("expr: {}", expr.to_str().unwrap_or_default());
    println!("func: {}", func.to_str().unwrap_or_default());
    println!("file: {}", file.to_str().unwrap_or_default());
    println!("line: {}", line);
    panic!("invalid parameter")
}

//hook
unsafe {
    _set_invalid_parameter_handler(Some(my_handler));
}

但是没有起作用。

试了GDB 调试,不太会用,放弃了。

接下来使用IDA单步调试,跟踪到具体函数为rci = pthread_condattr_setclock(&cattr, CLOCK_MONOTONIC)。 查看pthread_condattr_setclock 的实现,发现第二个参数clock_id只接受0,而在iowow代码中有定义#define CLOCK_MONOTONIC 1。最简单的做法,在这里条件判断一下传入0。但是根本原因是CMakeLists.txt判断HAVE_CLOCK_MONOTONIC出问题了,判断的相关代码:

check_symbol_exists(CLOCK_MONOTONIC time.h HAVE_CLOCK_MONOTONIC)
if (HAVE_CLOCK_MONOTONIC)
  add_definitions(-DIW_HAVE_CLOCK_MONOTONIC)
endif()

time.h里面确实没有CLOCK_MONOTONIC,不明白哪里有问题。改成下面的代码:

check_symbol_exists(CLOCK_MONOTONIC time.h HAVE_CLOCK_MONOTONIC)
if (HAVE_CLOCK_MONOTONIC and WIN32)
  add_definitions(-DIW_HAVE_CLOCK_MONOTONIC)
  message(INFO "HAVE_CLOCK_MONOTONIC=1")
endif()

以上白做了

最后发现以上白做了,只需要安装mingw-w64-x86_64-cmake

pacman -S --needed base-devel git vim \
      mingw-w64-x86_64-toolchain \
      mingw-w64-x86_64-cmake

然后在mingw64的 shell 执行:

cmake .. -G "MSYS Makefiles" -DCMAKE_C_COMPILER=gcc -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./output -DBUILD_SHARED_LIBS=OFF -DENABLE_HTTP=OFF
make
make install

不需要改动任何代码就可以正常编译和运行。

静态链接

cmake .. -G "MSYS Makefiles" -DCMAKE_C_COMPILER=gcc -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./output -DBUILD_SHARED_LIBS=OFF -DENABLE_HTTP=OFF
make
make install

自己的项目静态链接需要链接以下库文件:

ejdb2-2
iowow-1
iberty
winpthread
mingwex
mingw32
gcc
msvcrt

动态链接

  • 修改 cmake/modules/AddIOWOW.cmake, 把-DBUILD_SHARED_LIBS=OFF 改为-DBUILD_SHARED_LIBS=${BUILD_SHARED_LIBS}

  • 使用自定义toolchain,用mingw64里的工具导出dll和相关定义文件

#my-win64-tc.cmake
if (WIN32)
    add_custom_target(wintools_init)

    macro(add_w32_importlib tgt libname wdir)
    add_custom_command(
        TARGET ${tgt}
        POST_BUILD
        COMMAND dlltool.exe -d ${libname}.def  -e ${libname}.exp -l ${libname}.lib -D ${libname}.dll
        WORKING_DIRECTORY ${wdir}
    )
    endmacro(add_w32_importlib)
endif()
  • 编译
cmake .. -G "MSYS Makefiles" -DCMAKE_C_COMPILER=gcc -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=./output -DBUILD_SHARED_LIBS=ON -DENABLE_HTTP=OFF -DCMAKE_TOOLCHAIN_FILE=my-win64-tc.cmake
make
make install

自己的项目动态链接需要链接以下库文件:

libejdb2
libwow

最后

ejdb2 rust binding: https://github.com/Joylei/ejdb2-rs

参考