指定共享库搜索路径的几种方式

Linux 上有三种方式指定运行时动态链接库的搜索路径, 按照优先级顺序从高到低依次是:

  • rpath
  • LD_LIBRARY_PATH
  • runpath

假设我们有一个程序 a.out 动态链接 liba.so

然后将 liba.so 拷贝到两个独立的目录中:

目录结构如下

使用 -Wl,–enable-new-dtags 参数,通过 gcc 告诉 linker (ld, ld.gold, lld) 使用新的 dtags,即 runpath

运行时通过环境变量 LD_LIBRARY_PATH 指定共享库查找路径为目录 2;通过调试动态链接器,可以看到优先在目录 2 中寻找 liba.so,即 LD_LIBRARY_PATH 的优先级高于 runpath.

如果不指定 –enable-new-dtags 这个 linker 参数,或者使用 –disable-new-dtags,则会使用旧的 dtags,即 rpath

运行时通过环境变量 LD_LIBRARY_PATH 指定共享库查找路径为目录 2;通过调试动态链接器,可以看到优先在目录 1 中寻找 liba.so;即 rpath 的优先级高于 LD_LIBRARY_PATH.

 

C++ 多重继承的内存布局和指针偏移

在 C++ 程序里,在有多重继承的类里面。指向派生类对象的基类指针,其实是指向了派生类对象里面,该基类对象的起始位置,该位置相对于派生类对象可能有偏移。偏移的大小,等于派生类的继承顺序表里面,排在该类前面的所有的类的数据成员(含虚表指针)所占的空间大小总和。

下面以一个简单的程序为例,揭示有多重继承关系的派生类对象的内存布局:

注意该程序用 #pragma pack 指令指示数据在内存中按 4 字节来对齐。在 x64 平台上编译执行结果:

我们首先来分析这四个类的大小。

A 只有一个普通函数成员 foo,没有任何数据成员,是一个空类,其大小为 1 字节。之所以空类大小不为零,是需要标识类对象在内存中的位置,这 1 字节空间仅作占位用,不代表任何意义。

B 有一个成员变量 int b 和一个虚函数成员 func,其中 b 的大小为 4 字节。 由于存在虚函数,因此 B 类起始位置有一个虚表指针(vptr),在 64 平台上指针的大小为 8 字节。因此 B 的大小为 4 + 8 = 12 字节。

C 仅有一个成员变量 int c,因此其大小也就为 4 字节。

D 继承自A, B, C,它的大小等于 A, B, C 的所有数据成员的大小,加上其自身的数据成员和虚表指针的总的大小:4(b) + 4(c) + 4(d) + 8(vptr) = 20 字节。

注意:在 D 的继承关系链里面,只有基类 B 有虚函数,因此对于 D 对象而言,总体只有一个虚表指针,也就是(B)基类对象中的虚表指针。如果派生类的多个基类都有虚函数,则对应每个有虚函数的基类,在派生类对象里都有一个虚表指针。

因此,对于分析派生类 D 的对象,其内存布局如下:

分析结果与程序运行结果一致。

 

搭建 WireGuard VPN

WireGuardason A. Donenfeld 开发的一款点对点 VPN 协议,非常先进并且开源。在老司机的强烈推荐下,忍不住折腾了一番。本文旨在整理折腾记录并存档。

注:笔者的用的服务器是 GCE 上的 VM 实例,采用 Debian 9 发行版。其他发行版的安装过程可以参考 WireGuard Installstion

安装 WireGuard

首先需要安装当前 Linux 内核对应的 linux_headers:

由于 WireGuard 比较新,尚在 Debian 不稳定版的源里,需要手动导入:

WireGuard 服务端配置

开启 IPV4 流量转发:

创建 WireGuard 配置文件夹,并生成服务端和客户端密钥对:

我们还需要使用 ifconfig 命令获取服务器的网卡名称并记录下来,下面以 eth0 为例。

生成服务器配置文件 /etc/wireguard/wg0.conf

这样服务端就算配置完成,可以启动 WireGuard了,使用方法如下:

设置 wireguard 开机自启:

WireGuard 客户端配置

客户端也同样需要安装 WireGuard,例如笔者所使用的 ArchLinux 上安装步骤如下:

在配置服务端时,我们在服务器上分别创建了服务端和客户端的密钥对,这样也便于我们生成客户端的配置文件:

生成后的客户端文件在服务器上,我们可以将其拷贝到 PC 客户端,或者通过生成二维码的方式导到移动客户端:

这样使用手机客户端直接扫描生成二维码就可以快速完成配置。

多个 WireGuard 客户端

由于同一个客户端配置只能同时用于一台设备连接,所以如果需要同时使用多个客户端的话,可以建立多个客户端配置。

停止服务端的 WireGuard:

生成新的客户端密钥对:

在服务端配置文件中加入新的客户端公钥,并重新定义一段 AllowedIPs:

新建一个客户端文件,使用新客户端密钥的私钥:

注意这里的 Address 与上面服务端新加的 AllowedIPs 保持一致,Endpoint 和之前的一样,为服务端的 IP 和 ListenPort。

搞好服务端和新增客户端的配置后,再重新启动服务端的 WireGuard:

导出客户端配置文件方式依然可以采用上面介绍的方法导入到客户端。如果还需要添加新的 Peer,再次使用同样的方法操作即可。

经过实测,WireGuard 比笔者在另一台相同配置的 VM 实例上搭建的 V2Ray 结合 BBR 加速还要快,体验非常棒。

参考教程