移植一个软件,很大一部分工作内容将花在软件编译上。这是编译环境(编译器、宿主机等)和目标运行环境的差异引起的。goAhead的移植也不例外。虽然它具有良好的移植性,但本次把goAhead移植到eCos中,编译时仍花费了不少时间。本节主要记录了编译goAhead时遇到的问题及其解决方法。
编译环境:虚拟机ubuntu9.10,交叉编译器:gcc 版本 4.6.3 (eCos GNU Tools 4.6.3-20120623),即ecos-gnutools-arm-eabi-20120623.i386linux,该版本交叉编译器可到官网上下载。
编译准备
请按上节介绍,修改好goAhead源码ECOS目录下的makefile文件。然后在shell中进入该目录,开始编译。
编译过程
问题(1)
reille@ubuntu:/mnt/hgfs/stm32_ecos/appl/web/webs-2-5/ECOS$ make
/opt/ecos/gnutools/arm-eabi/bin/arm-eabi-gcc -c -o main.o -mcpu=cortex-m3 -mthumb -g -Wall -O2 -DWEBS -DWEBS_PAGE_ROM -DOS=”eCos” -DECOS -D__ECOS -D__NO_FCNTL=1 -I.. -Wall -I/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include -ffunction-sections -fdata-sections -Wp,-MD,main.d main.c
main.c: 在函数‘initWebs’中:
main.c:104:2: 警告:隐式声明函数‘init_all_network_interfaces’ [-Wimplicit-function-declaration]
main.c:115:17: 错误:‘eth0_bootp_data’未声明(在此函数内第一次使用)
main.c:115:17: 附注:每个未声明的标识符在其出现的函数内只报告一次
main.c: 在函数‘send’中:
main.c:221:5: 警告:隐式声明函数‘write’ [-Wimplicit-function-declaration]
main.c: 在函数‘recv’中:
main.c:226:5: 警告:隐式声明函数‘read’ [-Wimplicit-function-declaration]
make: *** [main.o] Error 1
reille@ubuntu:/mnt/hgfs/stm32_ecos/appl/web/webs-2-5/ECOS$
解决方法:先把main.c 115行屏蔽掉。
问题(2)
../page.c: 在函数‘websPageSeek’中:
../page.c:140:30: 错误:‘SEEK_CUR’未声明(在此函数内第一次使用)
../page.c:140:30: 附注:每个未声明的标识符在其出现的函数内只报告一次
make: *** [../page.o] Error 1
reille@ubuntu:/mnt/hgfs/stm32_ecos/appl/web/webs-2-5/ECOS$
解决方法:因为page.c包含wsIntrn.h头文件,而wsIntrn.h头文件又包含uemf.h头文件。在uemf.h头文件中,可以看到与OS相关的头文件包含。找到ECOS分支,在其中添加#include <stdio.h> 。因为SEEK_CUR等定义在这个stdio.h头文件中。
问题(3)
ck.d ../sock.c
../sock.c:32:11: 警告:‘struct sockaddr’在形参表内部声明 [默认启用]
../sock.c:32:11: 警告:它的作用域仅限于此定义或声明,这可能并不是您想要的 [默认启用]
../sock.c: 在函数‘socketDoOutput’中:
../sock.c:472:21: 错误:‘server’的存储大小未知
../sock.c:493:23: 错误:‘AF_INET’未声明(在此函数内第一次使用)
../sock.c:493:23: 附注:每个未声明的标识符在其出现的函数内只报告一次
../sock.c:496:3: 警告:隐式声明函数‘sendto’ [-Wimplicit-function-declaration]
../sock.c:509:3: 警告:隐式声明函数‘send’ [-Wimplicit-function-declaration]
../sock.c:472:21: 警告:未使用的变量‘server’ [-Wunused-variable]
../sock.c: 在文件作用域:
../sock.c:543:11: 警告:‘struct sockaddr’在形参表内部声明 [默认启用]
../sock.c:542:12: 错误:与‘tryAlternateSendTo’类型冲突
../sock.c:31:12: 附注:‘tryAlternateSendTo’的上一个声明在此
../sock.c: 在函数‘socketFree’中:
../sock.c:632:3: 警告:隐式声明函数‘shutdown’ [-Wimplicit-function-declaration]
../sock.c:633:4: 警告:隐式声明函数‘recv’ [-Wimplicit-function-declaration]
../sock.c:638:3: 警告:隐式声明函数‘close’ [-Wimplicit-function-declaration]
../sock.c: 在文件作用域:
../sock.c:31:12: 警告:‘tryAlternateSendTo’使用过但从未定义 [默认启用]
../sock.c:542:12: 警告:‘tryAlternateSendTo’定义后未使用 [-Wunused-function]
make: *** [../sock.o] Error 1
解决方法:变量server类型是struct sockaddr_in,也就是说,上述错误应是未包含与sock相关头文件引起的。sock.c包含uemf.h头文件,所以在这个头文件的ECOS分支加上与LWIP协议栈相关的头文件,如下:
#ifdef CYGPKG_NET_LWIP #include <pkgconf/net_lwip.h> #include <lwip.h> #include <lwip/sockets.h> #endif
问题(4)
In file included from ../sockGen.c:26:0:
../uemf.h:261:0: 警告:“O_RDONLY”重定义 [默认启用]
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/fcntl.h:69:0: 附注:这是先前定义的位置
../sockGen.c: 在函数‘socketOpenConnection’中:
../sockGen.c:157:4: 警告:隐式声明函数‘gethostbyname’ [-Wimplicit-function-declaration]
../sockGen.c:157:12: 警告:赋值时将整数赋给指针,未作类型转换 [默认启用]
../sockGen.c:160:22: 错误:提领指向不完全类型的指针
../sockGen.c:161:22: 错误:提领指向不完全类型的指针
../sockGen.c:274:8: 错误:‘SOMAXCONN’未声明(在此函数内第一次使用)
../sockGen.c:274:8: 附注:每个未声明的标识符在其出现的函数内只报告一次
../sockGen.c: 在函数‘socketSelect’中:
../sockGen.c:673:30: 错误:‘NFDBITS’未声明(在此函数内第一次使用)
../sockGen.c: 在函数‘socketSetBlock’中:
../sockGen.c:912:3: 警告:隐式声明函数‘ioctl’ [-Wimplicit-function-declaration]
../sockGen.c:887:16: 警告:变量‘flag’被设定但未被使用 [-Wunused-but-set-variable]
make: *** [../sockGen.o] Error 1
解决方法:对于“错误:提领指向不完全类型的指针”,一般是由于没有包含该指针数据类型所在头文件。sockGen.c包含uemf.h头文件,所以在这个头文件的ECOS分支加上struct hostent结构体类型所在的头文件。lwip协议栈下struct hosten结构体需要DNS支持,因此暂时把该结构体声明在uemf.h头文件的ECOS分支。
再次编译后“错误:提领指向不完全类型的指针”问题解决,接下解决上面问题中的后两个问题。
错误:‘SOMAXCONN’未声明,SOMAXCONN为listen()函数的一个参数。在bsd_tcpip和open_tcpip协议栈中,SOMAXCONN默认值为128,定义了系统中每一个端口最大的监听队列的长度。但在lwip协议栈中,listen()函数实际上调用的是lwip_listen(int s, int backlog)函数,接着是调用netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)函数。从形参看,listen的第二个形参应该是backlog。这个参数需要开启LWIP的CYGFUN_LWIP_TCP_LISTEN_BACKLOG配置项才启作用。默认情况下,该配置项没有开启。
backlog,通过搜索,应该是:how many pending connections queue will hold的含义,在http://osdir.com/ml/os.ecos.general/2003-05/msg00172.html中,定义了backlog为10,另外,在eCos vnc-server.c中,定义了backlog为5,如下:
#define BACKLOG 5 /* Number of pending connections queue will hold */
所以解决方法:在uemf.h的ECOS分支定义SOMAXCONN宏,其值为5
对于错误:‘NFDBITS’未声明 问题,在uemf.h的ECOS分支,添加如下代码:
#include "sys/select.h" #define NFDBITS __NFDBITS
问题(5)
In file included from ../uemf.h:213:0,
from ../wsIntrn.h:96,
from ../security.c:18:
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/sys/select.h:63:0: 警告:“FD_SETSIZE”重定义 [默认启用]
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/lwip/sockets.h:274:0: 附注:这是先前定义的位置
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/sys/select.h:97:16: 错误:‘struct fd_set’重定义
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/lwip/sockets.h:280:18: 附注:原先在这里定义
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/sys/select.h:99:3: 错误:与‘fd_set’类型冲突
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/lwip/sockets.h:282:11: 附注:‘fd_set’的上一个声明在此
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/sys/select.h:101:0: 警告:“FD_SET”重定义 [默认启用]
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/lwip/sockets.h:275:0: 附注:这是先前定义的位置
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/sys/select.h:102:0: 警告:“FD_CLR”重定义 [默认启用]
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/lwip/sockets.h:276:0: 附注:这是先前定义的位置
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/sys/select.h:103:0: 警告:“FD_ISSET”重定义 [默认启用]
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/lwip/sockets.h:277:0: 附注:这是先前定义的位置
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/sys/select.h:112:0: 警告:“FD_ZERO”重定义 [默认启用]
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/lwip/sockets.h:278:0: 附注:这是先前定义的位置
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/sys/select.h:132:1: 错误:与‘lwip_select’类型冲突
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/lwip/sockets.h:320:5: 附注:‘lwip_select’的上一个声明在此
../security.c: 在函数‘websSecurityHandler’中:
../security.c:58:12: 警告:变量‘type’被设定但未被使用 [-Wunused-but-set-variable]
make: *** [../security.o] Error 1
解决方法:把上面中用于解决“错误:‘NFDBITS’未声明”问题的代码移到sockGen.c文件开头。
问题(6)
mfdb.d ../emfdb.c
../emfdb.c: 在函数‘dbSearchStr’中:
../emfdb.c:239:20: 警告:变量‘nColumns’被设定但未被使用 [-Wunused-but-set-variable]
../emfdb.c: 在函数‘dbSetTableNrow’中:
../emfdb.c:401:26: 警告:变量‘nColumns’被设定但未被使用 [-Wunused-but-set-variable]
../emfdb.c: 在函数‘dbWriteKeyValue’中:
../emfdb.c:680:3: 警告:隐式声明函数‘write’ [-Wimplicit-function-declaration]
../emfdb.c: 在函数‘dbSave’中:
../emfdb.c:712:2: 警告:隐式声明函数‘open’ [-Wimplicit-function-declaration]
../emfdb.c:713:3: 错误:‘O_CREAT’未声明(在此函数内第一次使用)
../emfdb.c:713:3: 附注:每个未声明的标识符在其出现的函数内只报告一次
../emfdb.c:713:13: 错误:‘O_TRUNC’未声明(在此函数内第一次使用)
../emfdb.c:713:23: 错误:‘O_WRONLY’未声明(在此函数内第一次使用)
../emfdb.c:781:2: 警告:隐式声明函数‘close’ [-Wimplicit-function-declaration]
../emfdb.c:789:3: 警告:隐式声明函数‘unlink’ [-Wimplicit-function-declaration]
../emfdb.c: 在函数‘dbLoad’中:
../emfdb.c:840:11: 错误:‘sbuf’的存储大小未知
../emfdb.c:851:2: 警告:隐式声明函数‘stat’ [-Wimplicit-function-declaration]
../emfdb.c:881:2: 警告:隐式声明函数‘read’ [-Wimplicit-function-declaration]
../emfdb.c:840:11: 警告:未使用的变量‘sbuf’ [-Wunused-variable]
make: *** [../emfdb.o] Error 1
解决方法:对于emfdb.c 713行的错误,在uemf.h的ECOS分支添加#include <fcntl.h>
对于emfdb.c 840行的错误,这是因为缺少struct stat结构体的头文件,所在在uemf.h的ECOS分支添加#include <sys/stat.h>
问题(7)
bs.d ../webs.c
In file included from ../wsIntrn.h:96:0,
from ../webs.c:19:
../uemf.h:279:0: 警告:“O_RDONLY”重定义 [默认启用]
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/include/fcntl.h:69:0: 附注:这是先前定义的位置
../webs.c: 在函数‘websParseRequest’中:
../webs.c:924:37: 警告:变量‘browser’被设定但未被使用 [-Wunused-but-set-variable]
../webs.c: 在函数‘websLog’中:
../webs.c:1992:60: 错误:‘struct tm’没有名为‘tm_gmtoff’的成员
make: *** [../webs.o] Error 1
解决方法:在eCos的struct tm结构体中,没有tm_gmtoff这个成员。分析webs.c的924行代码,发现这行代码是用来计算时间的zone的,我们可以不需要它,因此,修改代码如下:
#if ECOS // Added by reille 2013.08.23 int tmp = 0; snprintf(zoneStr, sizeof(zoneStr), "%+03d00", tmp); #else snprintf(zoneStr, sizeof(zoneStr), "%+03d00", (int)(localt.tm_gmtoff/3600)); #endif
问题(8)
a – ../webs.o
a – ../websuemf.o
/opt/ecos/gnutools/arm-eabi/bin/arm-eabi-gcc -o webs -mcpu=cortex-m3 -mthumb -g -Wall -O2 -DWEBS -DWEBS_PAGE_ROM -DOS=”eCos” -DECOS -D__ECOS -D__NO_FCNTL=1 -I.. \
main.o libwebs.a -nostartfiles -L/mnt/hgfs/stm32_ecos/build/obj/ecos/install/lib -Wl,–gc-sections -Ttarget.ld -nostdlib
main.o: In function `initWebs’:
/mnt/hgfs/stm32_ecos/appl/web/webs-2-5/ECOS/main.c:104: undefined reference to `init_all_network_interfaces’
libwebs.a(sockGen.o): In function `socketOpenConnection’:
/mnt/hgfs/stm32_ecos/appl/web/webs-2-5/ECOS/../sockGen.c:163: undefined reference to `gethostbyname’
collect2: ld 返回 1
make: *** [webs] Error 1
解决方法:
经过上述修改,编译通过,但链接有问题。init_all_network_interfaces()应该是包含#include <network.h>头文件就可以,但uemf.h的ECOS分支已经包含了这个头文件了,却还是出现了该函数未定义。估计是使用LWIP协议栈的缘故,所以修改代码如下:
#if (defined(CYGPKG_NET_LWIP) && defined(CYGFUN_LWIP_MODE_SEQUENTIAL)) cyg_lwip_sequential_init(); #else init_all_network_interfaces(); #endif
对于gethostbyname()函数未定义问题,应该是没有包含netdb.h文件的缘故。在uemf.h的ECOS分支添加#include <lwip/netdb.h>
现在的问题是,对于LWIP协议栈,<lwip/netdb.h>明明有gethostbyname()函数声明,确还是报错。仔细分析发现,<lwip/netdb.h>依赖于2个宏:LWIP_DNS && LWIP_SOCKET,再追踪,这2个宏又分别由CYGPKG_LWIP_DNS和CYGPKG_LWIP_SOCKET_API控制,分别对应LWIP协议栈的DNS协议配置项和API接口配置项。在我的eCos LWIP配置中,CYGPKG_LWIP_SOCKET_API已使能,但CYGPKG_LWIP_DNS没有使能。这就难怪即使在uemf.h的ECOS分支添加了#include <lwip/netdb.h>也没有起作用。进一步发现,struct hostent结构体也有了。
所以我们要做的就是:在eCos中,开启CYGPKG_LWIP_DNS,同时把在问题(4)中声明在uemf.h的ECOS分支下的struct hostent结构体注释掉,make clean后再重新make。
问题(9)
S -D__NO_FCNTL=1 -I.. \
main.o libwebs.a -nostartfiles -L/mnt/hgfs/stm32_ecos/build/obj/ecos/install/lib -Wl,–gc-sections -Ttarget.ld -nostdlib
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/lib/libtarget.a(net_lwip_tcpip_sockets.o): In function `lwip_recv’:
/mnt/hgfs/stm32_ecos/build/../ecos/packages/net/lwip_tcpip/current/src/api/sockets.c:636: multiple definition of `lwip_recv’
main.o:/mnt/hgfs/stm32_ecos/appl/web/webs-2-5/ECOS/main.c:230: first defined here
/mnt/hgfs/stm32_ecos/build/obj/ecos/install/lib/libtarget.a(net_lwip_tcpip_sockets.o): In function `lwip_send’:
/mnt/hgfs/stm32_ecos/build/../ecos/packages/net/lwip_tcpip/current/src/api/sockets.c:642: multiple definition of `lwip_send’
main.o:/mnt/hgfs/stm32_ecos/appl/web/webs-2-5/ECOS/main.c:225: first defined here
collect2: ld 返回 1
make: *** [webs] Error 1
解决方法:分析goAhead源码包ECOS目录下的main.c发现,在该文件末尾有定义send()和recv()函数。所以屏蔽掉就可以了。
问题(10)
在编译时发现老是提示O_RDONLY重复定义。
解决方法:把uemf.h文件ECOS分析下的O_RDONLY定义屏蔽掉,大概在273行。
// #define O_RDONLY 1
最后生成的文件如下:
其中,libwebs.a是编译链接生成的goAhead静态库,我们的应用程序可以链接该静态库,生成最终包含goAhead的用户应用程序。
webs,应是编译ECOS目录下main.c并链接libwebs.a和web网页文件生成的eCos应用程序映像文件(elf格式),主要就是个goAhead应用程序。
编译总结
上述是编译过程中产生的问题。编译时出现问题不可怕,只要认真分析,任何编译错误都可以得到解决。
总结上面的问题发现,产生的问题多缘于uemf.h文件ECOS分支未包含相关头文件所致。最后uemf.h文件ECOS分支代码如下:
#ifdef ECOS // Added start by reille 2013.08.23 #include <stdio.h> #include <fcntl.h> #include <sys/stat.h> #ifdef CYGPKG_NET_LWIP #include <pkgconf/net_lwip.h> #include <lwip.h> #include <lwip/sockets.h> #ifdef CYGPKG_LWIP_SOCKET_API #include <lwip/netdb.h> #else #include <netdb.h> #endif #define SOMAXCONN 5 #endif /* CYGPKG_NET_LWIP */ // Added end by reille 2013.08.23 #include <limits.h> #include <cyg/infra/cyg_type.h> #include <cyg/kernel/kapi.h> #include <time.h> #include <network.h> #include <errno.h> #endif /* ECOS */