ddwrt 交叉编译笔记

在 PC (x86, linux) 上编译 ddwrt / openwrt mips 路由器上的 C 程序

根据路由器soc和ddwrt版本选择一个 toolchain (不同的 toolchain 使用不同的 libc, 对应不同 build 的 ddwrt 固件). 所有 toolchain 在这里下载, 解压缩后7G多.

对应不同 soc 的 toolchain 版本选择:
brcm26: gcc linaro 4.5 uclibc 0.9.33.2
brcm3x: gcc linaro 4.8.

brcm47XX:

推荐使用 OpenWrt 的 toolchain, ddwrt 的 toolchain 就是一坨屎, 而且基本没有什么文档和说明.
注意这些 toolchain 里的编译工具上都是64位 (x86-64) )Linux下可执行文件.

# 或者, 下载 debian x86 下的 toolchain (这是一个比较旧的版本, 不过是32位的),使用里面的 4.1.0 mips版gcc编译
$ wget ftp://ftp.dd-wrt.com/sourcecode/toolchains.x86.debian.sp1.tar.bz2
$ tar -jxf toolchains.x86.debian.sp1.tar.bz2
$ export PATH=~/toolchains/4.1.0-uclibc-0.9.28/bin:$PATH
$ mipsel-linux-uclibc-gcc helloworld.c -o helloworld
$ file helloworld
helloworld: ELF 32-bit LSB executable, MIPS, version 1 (SYSV), dynamically linked (uses shared libs), not stripped

使用 Makefile 时, 需要修改 Makefile 里相应路径
CROSS = mipsel-openwrt-linux-uclibc-
CC = $(CROSS)gcc
...


OpenWRT 交叉编译文档:

配置 PATH 和 STAGING_DIR 环境变量
~/.bashrc:
export PATH=$PATH:/path/to/toolchain_root/bin
export STAGING_DIR=$PATH:/path/to/toolchain_root

Pass the host and build to the build system of the package to trigger cross-compile
  • For GNU configure, use --build=architecture-unkown-linux-gny --host=architecture-openwrt-linux-uclibc (for example: ./configure --build=x86_64-unkown-linux-gnu –host=mips-openwrt-linux-uclibc)
    • Run ./config.guess to get the --build= option.
    • Check the output and ensure that 'checking whether we are cross compiling… yes' is yes.
  • For GNU make, override the CC and LD environment variables (usually not needed if GNU configure was used)
    • make CC=architecture-openwrt-linux-uclibc-gcc LD=architecture-openwrt-linux-uclibc-ld
architecture: mipsel, arm, x86_64

GNU configure

对于 brcm47xx mipsel 架构的路由器:
./configure --build=x86_64-unkown-linux-gnu --host=mipsel-openwrt-linux-uclibc

但很多软件包的源码并不支持这种 ./configure 这种配置方式 (虽然也用了 GNU Autoconf).

GNU make

make CC=mipsel-openwrt-linux-uclibc-gcc LD=mipsel-openwrt-linux-uclibc-ld

如果需要链接第三方动态库 / 头文件, 用 CPPFLAGS 和 LDFLAGS 参数:
CPPFLAGS=-I/path/to/header/files LDFLAGS=-L/path/to/dynamic/library/files

貌似最新版的 OpenWrt toolchain (OpenWrt toolchain brcm47xx mipsel (barrier breaker 14.07) ) 里存在一个 Bug, 可能导致编译器链接时出现 "undefined reference to `dlclose'" 错误, 解决方法:

in toolchain/bin/mips-openwrt-linux-uclibc-wrapper.sh,
TOOLCHAIN_SYSROOT="$TOOLCHAIN_BIN_DIR/../.."
This pointed to the parent of the actual toolchain directory, correcting it by changing to
TOOLCHAIN_SYSROOT="$TOOLCHAIN_BIN_DIR/.."
Then whole process works fine again.

已测试交叉编译软件包

路由器: Asus RT-N16 (mipsel 32位架构)
固件: ddwrt K3 r24461 big (3.10.44 内核)
工具链: OpenWrt toolchain brcm47xx mipsel (barrier breaker 14.07) (gcc-4.8-linaro_uClibc-0.9.33.2)
编译宿主环境: Ubuntu 14.04 X86_64

注: 部分包可以通过 ipkg / OPTW2 下载, 建议优先下载已有的package, 自己编译麻烦. 如果 ipkg / OPTW2 没有可以到 OpenWrt 的 Repository 里找找看 ipk 下载.

软件包交叉编译情况备注
netstat-nat-1.4.10.tar.gz (查看当前系统 NAT 连接信息工具)./configure
修改生成的 Makefile 里 CC 编译器命令为 mipsel-openwrt-linux-uclibc-gcc
make CC=mipsel-openwrt-linux-uclibc-gcc LD=mipsel-openwrt-linux-uclibc-ld

traceroute 2.0.20修改 Make.rules, 设置 CROSS 变量:
$(call set, CROSS, mipsel-openwrt-linux-uclibc-)
修改含有 LDFLAGS 的另一行
$(call set, LDFLAGS, -s -L../libsupp)
make 即可

make CROSS=mipsel-openwrt-linux-uclibc- LDFLAGS= -L../libsupp

修改 traceroute.c/traceroute.c, 注释掉 getaddr 函数里下面一行代码 (否则程序在路由器上运行出错):
//hints.ai_flags = AI_IDN;

iputils-s20121221 (提供 ping, arping, traceroute6 等工具)修改 Makefile 里三行:
CC=mipsel-openwrt-linux-uclibc-gcc
ADDLIB= -lm -L$(STAGING_DIR)/lib
USE_CAP=no
make
貌似编译部分成功, 不过我就需要里面的 ping 工具.
iptables 1.4.21 (2013.11.22)
./configure --build=x86_64-unkown-linux-gnu --host=mipsel-openwrt-linux-uclibc --prefix=/opt/sagan --enable-libipq --with-xtlibdir=/opt/sagan/lib/iptables --sbindir=/opt/sagan/bin
make
make install

iproute2-3.16.0 (2014.08) (最新完整版的 iproute2, ddwrt 自带的不支持 -batch 选项)
修改 ip/ipnetns.c, 注释掉 #ifndef HAVE_SETNS 和对应的 #endif 两行, 让程序使用自己静态包装的 setns 函数, 否则编译出错

//#ifndef HAVE_SETNS
static int setns(int fd, int nstype)
{
#ifdef __NR_setns
return syscall(__NR_setns, fd, nstype);
#else
errno = ENOSYS;
return -1;
#endif
}
//#endif /* HAVE_SETNS */

make CC=mipsel-openwrt-linux-uclibc-gcc LD=mipsel-openwrt-linux-uclibc-ld
编译了一半出错, 不过需要的 ip 已经编译成功了 (ip/ip), 测试可运行, 所以报的错误就不用管了- -
libmnl-1.0.3.tar.bz2 ./configure --build=x86_64-unkown-linux-gnu --host=mipsel-openwrt-linux-uclibc
make
编译生成的动态库位于 src/.libs

这个包是单纯动态库, 没有任何依赖, OpenWrt repository 里有, 建议直接安装.
ipkg install libmnl
ipset-6.23.tar.bz2先交叉编译依赖的 libml;
./configure --build=x86_64-unkown-linux-gnu --host=mipsel-openwrt-linux-uclibc --with-kbuild=/storage/ddwrt/kernel/linux-3.10-svn24461

make CPPFLAGS=-I/home/sagan/files/dev/src/libmnl-1.0.3/include LDFLAGS=-L/home/sagan/files/dev/src/libmnl-1.0.3/src/.libs
configure 时需要用 --with-kbuild 链接内核源码地址 (可以从ddwrt svn下载对应版的)

make 时需要制定 libml 的头文件和动态库文件路径

编译好的可执行文件位于 src/ipset.
程序依赖 libmnl.so.0

这个包 OpenWrt 的 repository (14.07 / snapshot) 里有 6.20 版, 建议直接安装 ipkg install ipset






编译内核模块

首先看看自己需要的内核模块是否已经包含在固件里了:

find /lib/modules/$(uname -r) -name "*.ko"

如果有, 在启动脚本里加入命令加载:

insmod /lib/modules/$(uname -r)/path/to/module.ko

如果没有, 可以自己编译. 但网上有一些编译好的内核模块 package 可以下载, 如 OpenWrt 的 kmod-* 包, 不过需要与自己的路由器 soc 类型和内核版本严格对应. 对应 brcm47xx 3.10.44 内核的 OpenWrt 的 repository 是 barrier_breaker 14.07-rc1, 里面的 kmod-系列内核模块包大部分应该都可以在 RT-N16 的 ddwrt K3 r24461 big (3.10.44 内核)固件里加载 (不过 big 版的 ddwrt builld 内核已经编译进去很多功能, 还有很多模块则已经编译好包含在固件里了, 直接可以加载).

下载对应内核版本的 ddwrt linux kernel 源代码, 需要严格匹配内核版本和 ddwrt svn 版本

svn co svn://svn.dd-wrt.com/DD-WRT/src/linux/universal/linux-3.10 -r 24461

编译需要 ddwrt 自己的工具链.下载, 解压缩后有一堆工具链文件夹, 编译内核模块需要的是"toolchain-mipsel_gcc4.1.2" 文件夹里的工具链. 将 "toolchain-mipsel_gcc4.1.2/bin" 目录加入 PATH

cd /opt
wget ftp://ftp.dd-wrt.com/toolchains/toolchains.tar.xz
tar jxvf current-toolchains.tar.bz2
PATH=$PATH:/opt/toolchain-mipsel_gcc4.1.2/bin
根据路由器架构, 将对应的 .config_XX 文件复制为 .config. 以 brcm mips 的路由为例:

cp .config_bcmmips .config

配置编译选项 (这里 mips == mipsel) (或者手工编辑 .config, 将需要编译的内核模块设为 m, 但不推荐, 需要自己处理依赖关系)
make menuconfig ARCH=mips

编译 ( 先编译一次内核再编译一次内核模块?)

make
make modules ARCH=mips

如果遇到 "drivers/net/wireless/Kconfig:284: can't open file "drivers/net/wireless/rt3352/rt2860v2_ap/Kconfig"" 错误, 修改 drivers/net/wireless/Kconfig, 注释掉相关代码(Ralink 驱动, svn中没有)

#if RALINK_DEVICE
#source "drivers/net/wireless/rt3352/rt2860v2_ap/Kconfig"
#source "drivers/net/wireless/rt3352/rt2860v2_sta/Kconfig"
#endif
# if SOC_MT7620_OPENWRT
# source "drivers/net/wireless/rt7620/rt2860v2_ap/Kconfig"
# source "drivers/net/wireless/rt7620/rt2860v2_sta/Kconfig"
# source "drivers/net/wireless/rt5592/Kconfig"
# source "drivers/net/wireless/rt7612/rlt_wifi/Kconfig"
# endif

如果遇到 " ERROR: "emfc_exit" [drivers/net/wl/wl.ko] undefined!" 错误, 将.config 里 CONFIG_WL 编译选项设为 no

CONFIG_WL=n

如果遇到 "Debug Mismatch" 错误, 关闭CONFIG_DEBUG_SECTION_MISMATCH 编译选项

CONFIG_DEBUG_SECTION_MISMATCH=n

也可以用 make 的参数传入选项

make CONFIG_DEBUG_SECTION_MISMATCH=n modules

已测试编译的内核模块

Asus RT-N16 / ddwrt K3 r24461 big (3.10.44内核) svn 24461

模块选项 (.config)说明
xt_owner.ko (netfilters iptables owner模块)CONFIG_NETFILTER_XT_MATCH_OWNER=m增加 iptables 匹配进程 uid/pid / gid MATCH 选项: ( This module attempts to match various characteristics of the packet creator, for locally generated packets. This match is only valid in the OUTPUT and POSTROUTING chains. Forwarded packets do not have any socket associated with them. Packets from kernel threads do have a socket, but usually no owner. )

-m owner
[!] --uid-owner username
[!] --uid-owner userid[-userid]
Matches if the packet socket's file structure (if it has one) is owned by the given user. You may also specify a numerical UID, or an UID range.
[!] --gid-owner groupname
[!] --gid-owner groupid[-groupid]
Matches if the packet socket's file structure is owned by the given group. You may also specify a numerical GID, or a GID range.
[!] --socket-exists
Matches if the packet is associated with a socket.

除了内核模块, 还需要用户空间 iptables 的动态库 libipt_owner.so (ddwrt 默认 iptables 不包含任何 extension, 需要自己编译 iptables )
IPV6 相关

ip6_tables.ko
nf_defrag_ipv6.ko
ip6table_filter.ko
ip6table_mangle.ko
nf_conntrack_ipv6.ko
CONFIG_IP6_NF_FILTER=m
CONFIG_IP6_NF_MATCH_RPFILTER=m
CONFIG_IP6_NF_MATCH_RT=m
CONFIG_IP6_NF_RAW=m
CONFIG_IP6_NF_MANGLE=m
CONFIG_NF_NAT_IPV6=m
big 版的 ddwrt 已经内置了相关内核模块 (只是没有默认载入):
find /lib/modules/$(uname -r)/kernel/ -name *6*
在启动脚本里增加所需要的模块 ( ipv6, ip_tunnel, sit 三个模块 ddwrt 默认载入, 如果在 web 管理界面开启 IPV6 的话)

insmod ip6_tables
insmod ip6t_REJECT
insmod ip6table_filter
insmod nf_defrag_ipv6
insmod nf_conntrack_ipv6










comments powered by Disqus