参考修改:https://github.com/tbuktu/libntru/pull/47/files 该仓库是纯C代码,构建工具是make. 但是代码直接下下来是无法编译通过的,它在poly.h中定义了多个函数指针,然后在poly.c中根据平台架构等区分给这几个函数指针不同的实现。

例如

// poly.h
uint8_t (*ntru_invert)(NtruPrivPoly *a, uint16_t mod_mask, NtruIntPoly *Fq);
// poly.c
void ntru_set_optimized_impl_poly() {
...
    if (sizeof(void*) >= 8)   /* 64-bit arch */
        ntru_invert = ntru_invert_64;
    else
        ntru_invert = ntru_invert_32;
...
#if _LP64
    ntru_invert = ntru_invert_64;
#else
    ntru_invert = ntru_invert_32;
#endif

这样如果多个源文件包含poly.h,必然会存在重复定义的问题,违反ODR(one-definition-rule)。而解决方法首先想到在poly.h中为符号加上extern,改定义为声明。然后这样一来,引发的效果还是符号未定义。

$ gcc poly.c -c -o poly.o
$ nm poly.o | grep ntru_invert
                 U ntru_invert
0000000000005228 T ntru_invert_32
0000000000005bc9 T ntru_invert_64

该项目将函数指针的初始化放在一个init函数里,要求使用者先调用init函数,可这样导致其他依赖这个库的文件编译错误,报错找不到这个符号的定义。

而修改的方案也很简单,只需要在poly.c中再定义一次这个符号,那么该符号就变成了为初始化的全局数据(B)。

// poly.c
 
// 补一个定义即可
uint8_t (*ntru_invert)(NtruPrivPoly *a, uint16_t mod_mask, NtruIntPoly *Fq);
...
void ntru_set_optimized_impl_poly() {
...
    if (sizeof(void*) >= 8)   /* 64-bit arch */
        ntru_invert = ntru_invert_64;
    else
        ntru_invert = ntru_invert_32;
...
#if _LP64
    ntru_invert = ntru_invert_64;
#else
    ntru_invert = ntru_invert_32;
#endif
$ nm poly.o | grep ntru_invert
0000000000000000 B ntru_invert
00000000000041f0 T ntru_invert_32
0000000000003b70 T ntru_invert_64

这样符号至少有定义,但未初始化。其他依赖单元编译时就不会报错。

参考PR:https://github.com/tbuktu/libntru/pull/47/files

这个项目还牵扯出一个问题,就是编译出的可执行文件在运行时报错,找不到编出来的动态库:

$ ./testham
./testham: error while loading shared libraries: libntru.so: cannot open shared object file: No such file or directory
$ ldd testham                                                                                           127 19:44
        linux-vdso.so.1 (0x00007ac6eec8a000)
        libntru.so => not found
        libm.so.6 => /usr/lib/libm.so.6 (0x00007ac6eeb5a000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007ac6ee969000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ac6eec8c000)
$ ls
changelog    LICENSE   Makefile.bsd    Makefile.os2  Makefile.win  README.md  testham*
libntru.so*  Makefile  Makefile.linux  Makefile.osx  PATENTS       src/       tests/

可见不会搜索当前目录,此时需要给链接器传额外参数,让他搜索当前目录,在makefile里加上

CFLAGS+=-Wl,-rpath,./

即可,参考:

 $ ldd testham
        linux-vdso.so.1 (0x000079365271f000)
        libntru.so => ./libntru.so (0x00007936526d7000)
        libm.so.6 => /usr/lib/libm.so.6 (0x00007936525b9000)
        libc.so.6 => /usr/lib/libc.so.6 (0x00007936523c8000)
        /lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x0000793652721000)
$ ./testham
Running tests...
  test_ntruprime_inv_int
  test_ntruprime_inv_poly
  test_mult_int
  test_mult_tern
  test_mult_prod
  test_inv
  test_arr
  test_ntruprime_keygen
  test_ntru_keygen
  test_encr_decr
  test_idxgen
  test_bitstring
  test_key
  test_hash
All tests passed     

NM命令参考

  1. https://voidint.github.io/post/tool/nm/
  2. https://linuxtools-rst.readthedocs.io/zh-cn/latest/tool/nm.html