ddwrt 交叉编译笔记
Published by SAGAN Jacques De on October 10th, 2014
在 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 就是一坨屎, 而且基本没有什么文档和说明.
根据路由器soc和ddwrt版本选择一个 toolchain (不同的 toolchain 使用不同的 libc, 对应不同 build 的 ddwrt 固件). 所有 toolchain 在这里下载, 解压缩后7G多.
brcm26: gcc linaro 4.5 uclibc 0.9.33.2
brcm3x: gcc linaro 4.8.
推荐使用 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 交叉编译文档:
$ 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
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
andLD
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.
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 make CC=mipsel-openwrt-linux-uclibc-gcc LD=mipsel-openwrt-linux-uclibc-ld | |
traceroute 2.0.20 | $(call set, CROSS, mipsel-openwrt-linux-uclibc-) 修改含有 LDFLAGS 的另一行 $(call set, LDFLAGS, -s -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
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
#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
已测试编译的内核模块
模块 | 选项 (.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 ) |
ip6_tables.ko nf_defrag_ipv6.ko ip6table_filter.ko ip6table_mangle.ko nf_conntrack_ipv6.ko | 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* insmod ip6_tables insmod ip6t_REJECT insmod ip6table_filter insmod nf_defrag_ipv6 insmod nf_conntrack_ipv6 |