交叉编译n2n,以openwrt为例子

交叉编译n2n,以openwrt为例子

预备知识

1 什么是交叉编译?

1.1 本地编译

简单粗暴的解释一下,在当前架构的pc下,直接由编译工具编译生成的程序或者是库文件,可以直接在当前的环境运行。这样的编译,可以叫做本地编译。在当前架构/平台下编译出来的程序,只能在当前架构/平台下运行。

1.2 交叉编译

交叉编译是一个相对的概念,就是在一种架构/平台下编译出来的程序或是库文件,放到别的架构/平台上运行。目标执行该程序的架构和环境不一样,属于交叉的编译。

交叉编译是做嵌入是开发的基本概念。

1.3 例子

就拿n2n来说,开发者提供了源码给我们,想要在各个平台上运行该程序,就需要交叉编译出不同目标平台/架构的应用程序,才能保证程序可以运行。

2 为什么需要交叉编译?

嵌入式系统中的资源太少!!

具体的解释就是:交叉编译出来的程序,所要运行的目标环境中,各种资源,都相对有限,所以很难进行直接的本地编译。

3 常见的平台,并非所有的

darwin_amd64
freebsd_386
freebsd_amd64
linux_386
linux_amd64
linux_arm
linux_arm64
linux_mips
linux_mips64
linux_mips64le
linux_mipsle
windows_386
windows_amd64

简单解释一下:amd64,说的就是64位的机器,386,说的是32位的机器。至于其他i686,x86等,大家可以自行搜索相关的关键字,了解一下,这里不做阐述了。

至于前缀,一目了然,不做说明。

4 还需要了解的基本概念

这个文档针对的是n2n但不限于其他知识的了解,简单的描述一下linux相关的基本概念。

4.1 linux用户

Linux系统是一个多用户多任务的分时操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。

管理员用户:root,拥有最高的权限。

当你登录了linux的时候,绝大多数机器默认使用的是bash交互环境,可以通过提示符区分用户。

root@soul-PC:~#

注意到上面的那一行,root便是只的是你当前用户,@后面的是主机,~表示的是当前用户[root]的家目录,通常是/root。最后的#号是最容易区分的,一般#号标示的就是root用户。

下面看看普通用户:

soul@soul-PC:~$

$ 符号,通常标示的是普通用户,

在绝大多数的情况下,我们用到的都是普通用户,一般机器登录也是普通用户。

在以后的编译过程中,我用的都是普通用户做演示。

当然,当你需要用到超级用户权限的时候,有很多的方式可以获得管理员权限。你可以装sudo软件包,这样,需要管理员权限的时候,只需要在命令前面加上sudo就可以了。

soul@soul-PC:~$ sudo ifconfig

4.2 环境变量

还是简单的介绍一下吧。

我们为什么可以使用Linux的命令,是因为linux系统的命令所在的路径,基本上行都是设置了环境变量。

注意:每个用的的环境变量可能不是一样的,环境变量可以全局配置,也可以某个用户自己配置自己的。

一般个人的linux‘系统,不需要做全局环境变量的配置。只需要在当前用户的家目录下修改配置文件就可以了。

我个人习惯是配置在.bashrc文件里配置。

soul@soul-PC:~$ pwd
/home/soul
soul@soul-PC:~$ ls -al .bashrc
-rw-r--r-- 1 soul soul 3822 6月   4  2018 .bashrc

我们可以用下面的命令来查看当然用户的环境变量:

soul@soul-PC:~$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/sbin

看到我在soul用户下的环境变量路径有这些,意味着,在这个路径下的所有可以执行的文件,我都可以在任何地方使用他,比如ls 命令。

再说一下,一个命令它是放在哪里,它是什么,我怎么知道他?跟我做。举个例子,一ls命令为例子:

soul@soul-PC:~$ which ls #我要看ls在那个路径下
/bin/ls
soul@soul-PC:~$ whereis ls #我要看路径等其他的
ls: /bin/ls /usr/share/man/man1/ls.1.gz
soul@soul-PC:~$ type ls #我要看ls是什么类型的命令
ls 是 `ls --color=auto' 的别名

/bin/ 目录下,包含了我们常用的命令。就简单介绍到这里吧。环境变量的配置,后面会讲。

准备工作

要编译,就现在搭建环境,当然,你也得会一些常用的linux操作命令,基本常识。

一台linux机器(苹果也可以),虚拟机装linux也可以。

我的linux机子如下:

~$ uname -a
Linux soul-PC 4.15.0-21deepin-generic #21 SMP Mon May 21 05:20:09 UTC 2018 x86_64 GNU/Linux

OpenSSL 一会用的到

下载地址:http://distfiles.macports.org/openssl/

Openwrt 的 SDK 或 toolchain

首先你的知道openwrt的下载的官网,http://downloads.openwrt.org/,进入后你会看到各个版本和分支。这里我以Chaos Calmer 15.05.1为例子,我手里有一个联发科板子。

1546772790055

找到你对应的型号芯片

1546772871889

具体的芯片

1546772888985

进去里面有很多该芯片的各类固件!你不刷机,不要管。

1546772946464

找到sdk

1546772977778

我们只需要下载sdk开发工具就可以了!

下载以后解压出来,解压命令自己学习一下就行了。

注意,新的压缩包为了减小体积,是两层压缩,xz,结尾,tar结尾。所以你得解压两次。

soul@soul-PC:~/Downloads$ tar -jxvf OpenWrt-SDK-15.05.1-ramips-mt7688_gcc-4.8-linaro_uClibc-0.9.33.2.Linux-x86_64.tar.bz2

更改一下sdk的名字,路径太长,如果有报错,不便于分析。

soul@soul-PC:~/Downloads$ mv OpenWrt-SDK-15.05.1-ramips-mt7688_gcc-4.8-linaro_uClibc-0.9.33.2.Linux-x86_64 sdk

接着把sdk复制到你偏爱的目录。

找到sdk的gcc工具目录

soul@soul-PC:~/op/sdk/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin$ ls
c_rehash                         mipsel-openwrt-linux-strip
g++-uc                           mipsel-openwrt-linux-uclibc-addr2line
g++-uc+std                       mipsel-openwrt-linux-uclibc-ar
ldd                              mipsel-openwrt-linux-uclibc-as
mipsel-openwrt-linux-addr2line   mipsel-openwrt-linux-uclibc-c++
mipsel-openwrt-linux-ar          mipsel-openwrt-linux-uclibc-c++filt
mipsel-openwrt-linux-as          mipsel-openwrt-linux-uclibc-cpp
mipsel-openwrt-linux-c++         mipsel-openwrt-linux-uclibc-elfedit
mipsel-openwrt-linux-c++filt     mipsel-openwrt-linux-uclibc-g++
mipsel-openwrt-linux-cpp         mipsel-openwrt-linux-uclibc-gcc
mipsel-openwrt-linux-elfedit     mipsel-openwrt-linux-uclibc-gcc-4.8.3
mipsel-openwrt-linux-g++         mipsel-openwrt-linux-uclibc-gcc-ar
mipsel-openwrt-linux-gcc         mipsel-openwrt-linux-uclibc-gcc-nm
mipsel-openwrt-linux-gcc-4.8.3   mipsel-openwrt-linux-uclibc-gcc-ranlib
mipsel-openwrt-linux-gcc-ar      mipsel-openwrt-linux-uclibc-gcov
mipsel-openwrt-linux-gcc-nm      mipsel-openwrt-linux-uclibc-gdb
mipsel-openwrt-linux-gcc-ranlib  mipsel-openwrt-linux-uclibc-gprof
mipsel-openwrt-linux-gcov        mipsel-openwrt-linux-uclibc-ld
mipsel-openwrt-linux-gdb         mipsel-openwrt-linux-uclibc-ld.bfd
mipsel-openwrt-linux-gprof       mipsel-openwrt-linux-uclibc-nm
mipsel-openwrt-linux-ld          mipsel-openwrt-linux-uclibc-objcopy
mipsel-openwrt-linux-ld.bfd      mipsel-openwrt-linux-uclibc-objdump
mipsel-openwrt-linux-nm          mipsel-openwrt-linux-uclibc-ranlib
mipsel-openwrt-linux-objcopy     mipsel-openwrt-linux-uclibc-readelf
mipsel-openwrt-linux-objdump     mipsel-openwrt-linux-uclibc-size
mipsel-openwrt-linux-ranlib      mipsel-openwrt-linux-uclibc-strings
mipsel-openwrt-linux-readelf     mipsel-openwrt-linux-uclibc-strip
mipsel-openwrt-linux-size        openssl
mipsel-openwrt-linux-strings

接着添加环境比变量

soul@soul-PC:~/op/sdk/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin$ pwd
/home/soul/op/sdk/staging_dir/toolchain-mipsel_24kc_gcc-7.3.0_musl/bin
soul@soul-PC:~/op/sdk/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin$ gedit ~/.bashrc

在最后加上

export PATH=$PATH:/home/soul/op/sdk/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin
export STAGING_DIR=/home/soul/op/sdk/staging_dir

保存,退出

接着让配置文件生效,这样做:

soul@soul-PC:~/op/sdk/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin$ source ~/.bashrc
soul@soul-PC:~/op/sdk/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games:/sbin:/usr/sbin:/home/soul/op/sdk/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin

看到我们添加的环境变量已经生效了。此时你就可以在任何地方使用你的工具了。

在命令行敲mips[tab],应该能自动补全你的命令了。

接着编译一个简单的是程序,helloworld

#include <stdio.h>

int main(int argc,char* argv[])
{
    puts("Hello World!");
    return 0;
}

soul@soul-PC:~/op/c$ mipsel-openwrt-linux-gcc -o hello hello.c
soul@soul-PC:~/op/c$ ls
hello  hello.c
soul@soul-PC:~/op/c$ ./hello
bash: ./hello: cannot execute binary file: Exec format error
soul@soul-PC:~/op/c$

尝试在本机上执行文件,报错。不是可执行文件,那当然,这是在目标平台执行的文件,此时把文件放到你的目标机器上执行,就可以输出helloworld啦。

到此,基本环境已经搭建测试成功了。

交叉编译OpenSSL

下载源码,解压

下载地址:http://distfiles.macports.org/openssl/

soul@soul-PC:~/Downloads$ tar -zxvf openssl-1.0.2q.tar.gz

进入openssl目录,设置生成路径,不需要生成汇编

soul@soul-PC:~/op/openssl-1.0.2q$ ./config no-asm shared --prefix=/home/soul/op/sdk/staging_dir/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2

接着修改 MakeFile 文件 ,指定交叉编译的gcc

...
CC= mipsel-openwrt-linux-gcc
...
AR= mipsel-openwrt-linux-ar $(ARFLAGS) r
RANLIB= mipsel-openwrt-linux-ranlib
...
NM= mipsel-openwrt-linux-nm
...
MAKEDEPPROG= mipsel-openwrt-linux-gcc

同时,删除-m64,两个地方需要删除。

之后编译,最后,安装,编译时间看机器性能了。

soul@soul-PC:~/op/openssl-1.0.2q$ make

安装:

soul@soul-PC:~/op/openssl-1.0.2q$ make install

到此,openssl,编译结束

交叉编译n2n

好的 接下来我们下载源码:

~$ git clone https://github.com/meyerd/n2n.git

之后你就看到n2n的目录了。进去目录,看了下,可以使用cmake构建编译工程,我这里直接使用makefile编译,不使用cmake。

~$ cd n2n/n2n_v2/
~/n2n/n2n_v2(master)$ ls
benchmark.c            gen_keyfile.py  n2n.c             n2n_wire.h        supernode.1       twofish.c
benchmark_hashtable.c  HACKING         n2n.h             NEW_FEATURES.txt  transform_aes.c   twofish.h
cmake/                 INSTALL         n2n_keyfile.c     openwrt/          transform_null.c  unix-scm.c
CMakeLists.txt         lzoconf.h       n2n_keyfile.h     README            transform_tf.c    version.c
COPYING                lzodefs.h       n2n.spec          scm.h             tuntap_freebsd.c  win32/
debian/                minilzo.c       n2n_test.c        scripts/          tuntap_linux.c    wire.c
edge.8                 minilzo.h       n2n_transforms.h  sglib.h           tuntap_netbsd.c   wireshark/
edge.c                 munin/          n2n_v2.7          sn.c              tuntap_osx.c
~/n2n/n2n_v2(master)$

打开n2n工程目录,创建或者复制Makefile文件。

注意文件内容指定了“Makefile”,所以你创建文件的时候,名字也得大写M开头。

N2N_VERSION=2.1.0
N2N_OSNAME=$(shell uname -p)

########

CC= mipsel-openwrt-linux-gcc
DEBUG?=-g3
#OPTIMIZATION?=-O2
WARN?=-Wall -Wshadow -Wpointer-arith -Wmissing-declarations -Wnested-externs

#Ultrasparc64 users experiencing SIGBUS should try the following gcc options
#(thanks to Robert Gibbon)
PLATOPTS_SPARC64=-mcpu=ultrasparc -pipe -fomit-frame-pointer -ffast-math -finline-functions -fweb -frename-registers -mapp-regs

N2N_DEFINES=
N2N_OBJS_OPT=
LIBS_EDGE_OPT=

N2N_OPTION_AES?="yes"
#N2N_OPTION_AES=no

ifeq ($(N2N_OPTION_AES), "yes")
    N2N_DEFINES+="-DN2N_HAVE_AES"
    LIBS_EDGE_OPT+=-lcrypto
endif

CFLAGS+=$(DEBUG) $(OPTIMIZATION) $(WARN) $(OPTIONS) $(PLATOPTS) $(N2N_DEFINES)

INSTALL=install
MKDIR=mkdir -p

INSTALL_PROG=$(INSTALL) -m755
INSTALL_DOC=$(INSTALL) -m644

# DESTDIR set in debian make system
PREFIX?=$(DESTDIR)/usr
#BINDIR=$(PREFIX)/bin
SBINDIR=$(PREFIX)/sbin
MANDIR?=$(PREFIX)/share/man
MAN1DIR=$(MANDIR)/man1
MAN7DIR=$(MANDIR)/man7
MAN8DIR=$(MANDIR)/man8

N2N_LIB=n2n.a
N2N_OBJS=n2n.o n2n_keyfile.o wire.o minilzo.o twofish.o \
         transform_null.o transform_tf.o transform_aes.o \
         tuntap_freebsd.o tuntap_netbsd.o tuntap_linux.o tuntap_osx.o version.o unix-scm.o
LIBS_EDGE+=$(LIBS_EDGE_OPT)
LIBS_SN=

#For OpenSolaris (Solaris too?)
ifeq ($(shell uname), SunOS)
LIBS_EDGE+=-lsocket -lnsl
LIBS_SN+=-lsocket -lnsl
endif

APPS=edge
APPS+=supernode

DOCS=edge.8.gz supernode.1.gz n2n_v2.7.gz

all: $(APPS) $(DOCS)

edge: edge.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
    $(CC) $(CFLAGS) edge.c $(N2N_LIB) $(LIBS_EDGE) -o edge

test: test.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
    $(CC) $(CFLAGS) test.c $(N2N_LIB) $(LIBS_EDGE) -o test

supernode: sn.c $(N2N_LIB) n2n.h Makefile
    $(CC) $(CFLAGS) sn.c $(N2N_LIB) $(LIBS_SN) -o supernode

benchmark: benchmark.c $(N2N_LIB) n2n_wire.h n2n.h Makefile
    $(CC) $(CFLAGS) benchmark.c $(N2N_LIB) $(LIBS_SN) -o benchmark

.c.o: n2n.h n2n_keyfile.h n2n_transforms.h n2n_wire.h twofish.h Makefile
    $(CC) $(CFLAGS) -c $<

%.gz : %
    gzip -c $< > $@

$(N2N_LIB): $(N2N_OBJS)
    ar rcs $(N2N_LIB) $(N2N_OBJS)
#   $(RANLIB) $@

version.o: Makefile
    $(CC) $(CFLAGS) -DN2N_VERSION='"$(N2N_VERSION)"' -DN2N_OSNAME='"$(N2N_OSNAME)"' -c version.c

clean:
    rm -rf $(N2N_OBJS) $(N2N_LIB) $(APPS) $(DOCS) test *.dSYM *~

install: edge supernode edge.8.gz supernode.1.gz n2n_v2.7.gz
    echo "MANDIR=$(MANDIR)"
    $(MKDIR) $(SBINDIR) $(MAN1DIR) $(MAN7DIR) $(MAN8DIR)
    $(INSTALL_PROG) supernode $(SBINDIR)/
    $(INSTALL_PROG) edge $(SBINDIR)/
    $(INSTALL_DOC) edge.8.gz $(MAN8DIR)/
    $(INSTALL_DOC) supernode.1.gz $(MAN1DIR)/
    $(INSTALL_DOC) n2n_v2.7.gz $(MAN7DIR)/

至此,编译结束,看下生成的文件。

soul@soul-PC:~/n2n/n2n_v2$ ls
CMakeLists.txt         edge.8          n2n.h             scm.h             transform_tf.o    unix-scm.c
COPYING                edge.8.gz       n2n.o             scripts           tuntap_freebsd.c  unix-scm.o
HACKING                edge.c          n2n.spec          sglib.h           tuntap_freebsd.o  version.c
INSTALL                gen_keyfile.py  n2n_keyfile.c     sn.c              tuntap_linux.c    version.o
Makefile               lzoconf.h       n2n_keyfile.h     supernode         tuntap_linux.o    win32
NEW_FEATURES.txt       lzodefs.h       n2n_keyfile.o     supernode.1       tuntap_netbsd.c   wire.c
README                 minilzo.c       n2n_test.c        supernode.1.gz    tuntap_netbsd.o   wire.o
benchmark.c            minilzo.h       n2n_transforms.h  transform_aes.c   tuntap_osx.c      wireshark
benchmark_hashtable.c  minilzo.o       n2n_v2.7          transform_aes.o   tuntap_osx.o
cmake                  munin           n2n_v2.7.gz       transform_null.c  twofish.c
debian                 n2n.a           n2n_wire.h        transform_null.o  twofish.h
edge                   n2n.c           openwrt           transform_tf.c    twofish.o

看了下edge,supmode文件,还是有点大,怎么办?可以soul@soul-PC:~/n2n/n2n_v2$ mipsel-openwrt-linux-strip edge supernode一下,稍微减小了一些体积。

在Makefile文件可以适当的参考gcc工具的用法,编译输出选项,调整后,体积也适当减小,这里不做阐述了。

未完,待更新。有建议或者意见留言评论。


93.33%(14)

6.67%(1)
发表评论?

3 条评论。

  1. 新版n2n已经更新集成了openwrt的makefile了,放进去sdk里make就OK了

    • 请问有编译好的新版的n2n V2.4吗?可以用于LEDE的

    • 是的,现在简单一些了,但前提是你要有SD卡、USB之类的存储空间来放sdk。
      楼上的,目前没有lede的,需要自己编译。万一有了,我会放到这里去。
      supernode.ml

发表评论

*