Linux应用程序开发(三)---移植kvm到arm linux
移植环境(红色粗字体字为修改后内容,蓝色粗体字为特别注意内容)
1,主机环境:VMare下CentOS 5.5 ,1G内存。
2,集成开发环境:Elipse IDE
3,编译编译环境:arm-linux-gcc v4.4.3,arm-none-linux-gn?i-gcc v4.5.1。
4,开发板:mini2440,2M nor flash,128M nand flash。
5,u-boot版本:u-boot-2009.08
6,linux 版本:linux-2.6.32.2
7,参考文章:
尽管前面基于php的嵌入式web服务器已经能够运行java脚本文件,而且也能够执行xajax运行库。但是还是有些系统用不到php那样复杂的功能,而且php作为嵌入式的移植也比较复杂。java技术在嵌入式领域的应用已经流行起来。下面就有关java在嵌入式方面的移植和应用问题做些探讨。
移植kvm到arm-linux
【1】安装jdk
(1)检查系统中是否已经安装jkd,因为j2me_cldc/api目录下的源代码是用javac编译的,所以这里只需查询下javac的安装位置即可。
[root@localhost ~]# find / -name javac
/var/lib/alternatives/javac
/usr/lib/jvm/java-1.6.0-openjdk-1.6.0.0/bin/javac
/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/bin/javac
/usr/bin/javac
/etc/alternatives/javac
[root@localhost ~]#
查看下javac版本
[root@localhost ~]# java -version
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.8) (rhel-1.22.1.9.8.el5_6-i386)
OpenJDK Client VM (build 19.0-b09, mixed mode)
[root@localhost ~]#
如果系统中没有找到javac,可安装下面方式安装。
方式1,j2me 1.4.2 版,这里不安装。
j2sdk-1_4_2_19-linux-i586-rpm.bin.gz :http://file.ajava.org/tool/java/j2sdk-1_4_2_19-linux-i586-rpm.bin.gz
方式2,jdk1.6.0_26版,可以是最新版,这里不安装。
下载完解压
(2)添加环境变量
打开/etc/profile文件,定位到27行,确认如下修改:
# Path manipulation
if [ "$EUID" = "0" ]; then
pathmunge /sbin
pathmunge /usr/sbin
pathmunge /usr/local
pathmunge /usr/local/bin
pathmunge /usr/local/sbin
pathmunge /usr/local/lib
pathmunge /usr/local/arm/4.4.3/bin
pathmunge /usr/local/CodeSourcery/Sourcery_G++_Lite/bin
pathmunge /usr/local/JLink_Linux_V422
fi
添加上面一行的目的是为了让编译器找到libiconv所在的路径。
... ...
#set to java jre
JAVA_HOME=/usr/local/jdk1.6.0_26
CLASSPATH=$JAVA_HOME/lib:/root/java
PATH=$JAVA_HOME/bin:$PATH
export JAVA_HOME PATH CLASSPATH
根据你自己的实际解压后安装位置作适当的修改,我的解压后安装位置为/usr/local文件夹下,其中PATH是命令的搜索路径,比如说用javac去编译一个.java文件,直接在命令行中用javac就可以,而不要代绝对路径了。CALSSPATH中的$JAVA_HOME/lib 为你的java代码运行的类库路径,/root/JAVA 为编译你的java代码生成的class所放置的路径。
文件修改并保持完以后,运行source etc/profile命令即可使修改操作立即生效。
(3)运行测试
在/root/java下新建一个文件名helloworld.java文件,并添加如下内容:
class HelloWorldApp
{
p lic static void main(String args[])
{
System.out.println("welcome to java!");
}
}
然后保存,并按下面操作编译后运行
[root@localhost java]# javac helloworld.java
[root@localhost java]# java HelloWorldApp
welcome to java!
[root@localhost java]#
【2】下载并解压kvm
j2me_cldc-1_1-fcs-src-winunix:http://www.oracle.com/technetwork/java/javame/javamobile/download/overview/index.html
在上面打开的页面左侧点击 REFERENCE IMPLEMENTATIONS->Connected Limited Device Configuration (CLDC) 1.1->download,如果您没有注册用户名,那么需要您注册一个并登录后才能下载。
下载后解压
[root@localhost linux-test]# unzip /root/linux-test/j2me_cldc-1_1-fcs-src-winunix.zip -d ./
【3】j2me_cldc文档结构
j2me_cldc解压缩的目录有以下几个:
api-目录下包含了java类库的代码
bin—包含了编译过的class文件以及一些常用的与平台相关的可执行文件
build---与平台相关的makefile文件存放目录
docs ---与开发或移植相关说明文档
jam---应用管理代码
kvm---kvm代码
samples--实例代码
tools---随kvm代码的工具
说明:跟平台移植无关的代码都在kvm/vmcommon目录,平台有关的代码在kvm/vmextra目录下。
【4】java在kvm中的执行流程
在j2me中,程序流程从java源码(.java)开始的。Java源码被编译平台编译后生成与平台无关的Java字节码文件。该字节码文件经过一个外表校验器来验证字节码的合法性。当验证通过后,字节码会送到kvm中执行。其流程如下图所示。
【5】修改j2me_cldc/kvm/VmUnix/build/Makefile文件
(1)先改变其只读属性
[root@localhost linux-test]# cd j2me_cldc/kvm
[root@localhost kvm]# chmod u+w VmUnix/build/Makefile
(2)修改VmUnix/build下的Makefile,定位到文件开始处,修改如下:
export PLATFORM=linux
TOP=../../..
incl? $(TOP)/build/Makefile.inc
ifeq ($(PLATFORM), linux)
export GCC=tr
endif
(3)定位到需要定位到95行,指定交叉编译器,修改如下:
ifeq ($(GCC), tr)
CC = arm-linux-gcc
CFLAGS = -Wall $(CPPFLAGS) $(ROMFLAGS) $(OTHER_FLAGS)
DEBUG_FLAG = -g
OPTIMIZE_FLAG = -O2
FP_OPTIMIZE_FLAG =
else
CC = cc
CFLAGS = -Xa $(CPPFLAGS) $(ROMFLAGS) $(OTHER_FLAGS)
DEBUG_FLAG = -g -xsb
OPTIMIZE_FLAG = -xO2
FP_OPTIMIZE_FLAG =
endif
修改完后保存退出。
【6】修改j2me_cldc/kvm/VmUnix/src/runtime_md.c文件
(1)改变其只读属性
[root@localhost build]# cd ../src
[root@localhost src]# chmod u+w runtime_md.c
(2)把这个文件中void InitializeFloatingPoint()函数中的下面两句注释掉
[root@localhost src]# vim runtime_md.c
定位到171行,修改如下:
void InitializeFloatingPoint() {
#if defined(LINUX) && PROCESSOR_ARCHITECTURE_X86
/* Set the precision FPU to do le precision */
// fpu_control_t cw = (_FPU_DEFAULT & ~_FPU_EXTENDED) | _FPU_DO LE;
// _FPU_SETCW(cw);
#endif
}
保存退出。
【7】修改j2me_cldc/tools/preverifier/build/linux目录下的Makefile
[root@localhost ~]# cd linux-test/j2me_cldc/tools/preverifier/build/linux
[root@localhost linux]# chmod u+w Makefile
[root@localhost linux]# vim Makefile
定位到39行附近,修改如下:
CC = gcc
LD = gcc
DEBUG_FLAG =
LDFLAGS = -l iconv
ifeq ($(DEBUG), tr)
DEBUG_FLAG = -g
endif
加入上面一行的目的是引入iconv库,不然在编译是会出现如下错误:
convert_md.o: In function `open_iconv':
convert_md.c:(.text+0x16): undefined reference to `libiconv_open'
convert_md.o: In function `native2utf8':
convert_md.c:(.text+0x239): undefined reference to `libiconv'
convert_md.c:(.text+0x276): undefined reference to `libiconv_close'
convert_md.o: In function `utf2native':
convert_md.c:(.text+0x365): undefined reference to `libiconv'
convert_md.c:(.text+0x3a2): undefined reference to `libiconv_close'
collect2: ld 返回 1
make: *** [preverify] 错误 1
【6】修改j2me_cldc/api目录下的Makefile,使其向下兼容1.4版本
需要先改变其只读属性
[root@localhost api]# chmod u+w Makefile
然后打开j2me_cldc/api/Makefile,定位到16行,修改如下:
DEBUGFLAG=":none"
endif
JAVAC = javac -source 1.4
ifneq ($(findstring win, $(PLATFORM)),)
这样修改的目的是使其向下兼容1.4版本,不然在编译时出现如下错误:
src/java/lang/Object.java:132: cannot access java.lang.StringBuilder
class file for java.lang.StringBuilder not found
return getClass().getName() + "@" + Integer.toHexString(hashCode());
网上说问题是出在几个String相加原因是JDK1.5太新,wtk2.2只能用JDK1.4.2的。JDK1.5使用StringBuilder类来代替JDK1.4中的StringB?r类。
【7】进入j2me_cldc/build/linux目录编译
[root@localhost j2me_cldc]# cd build/linux
[root@localhost linux]# make
... ...
src/java/lang/Object.java:132: cannot access java.lang.StringBuilder
class file for java.lang.StringBuilder not found
return getClass().getName() + "@" + Integer.toHexString(hashCode());
^
1 error
94 warnings
make[1]: *** [compilefiles] Error 1
make[1]: Leaving directory `/root/linux-test/j2me_cldc/api'
make: *** [all] 错误 1
【8】在/usr/lib目录下建立libiconv库文件
因为gcc默认的库文件路径为/usr/lib,需要将/usr/local/lib目录下的libiconv.so,libiconv.so.2,libiconv.so.2.5.0三个文件复制到此目录下或者在此目录先建立到/usr/local/lib/libiconv.so的软连接。不然会出现下面错误:
... ...
../tools/preverifier/build/linux/preverify -d classes tmpclasses
../tools/preverifier/build/linux/preverify: error while loading shared libraries: libiconv.so.2: cannot open shared object file: No s h file or directory
make[1]: *** [compilefiles] Error 1
make[1]: Leaving directory `/root/linux-test/j2me_cldc/api'
make: *** [all] 错误 1
【9】修改j2me_cldc/tools/jcc/src/util目录下ClassReader.java
需要先改变其只读属性
[root@localhost api]# chmod u+w Makefile
然后打开其目录下的ClassReader.java,定位到85行修改如下:
p lic int
readZip (String fileName, Vector done) throws IOException
{
int i = 0;
ZipFile zf = new ZipFile(fileName);
Enumeration myenum = zf.entries();
while (myenum.hasMoreElements()) {
ZipEntry ent = (ZipEntry)myenum.nextElement();
String name = ent.getName();
if (!ent.isDirectory() &&
不然会出现如下错误:
... ...
src/util/ClassReader.java:85: as of release 5, 'enum' is a keyword, and may not be used as an identifier
(use -source 1.4 or lower to use 'enum' as an identifier)
Enumeration enum = zf.entries();
^
src/util/ClassReader.java:86: as of release 5, 'enum' is a keyword, and may not be used as an identifier
(use -source 1.4 or lower to use 'enum' as an identifier)
while (enum.hasMoreElements()) {
^
src/util/ClassReader.java:87: as of release 5, 'enum' is a keyword, and may not be used as an identifier
(use -source 1.4 or lower to use 'enum' as an identifier)
ZipEntry ent = (ZipEntry)enum.nextElement();
... ...
【10】参考Memo for Explanting KVM to ARM-Linux,针对如下错误需要安装低版本的编译器。
... ...
../../../kvm/VmCommon/src/verifierUtil.c: In function 'verifyClass':
../../../kvm/VmCommon/src/verifierUtil.c:547: error: invalid storage class for function 'Vfy_verifyMethod'
../../../kvm/VmCommon/src/verifierUtil.c:571: warning: implicit declaration of function 'Vfy_verifyMethod'
../../../kvm/VmCommon/src/verifierUtil.c: At top level:
../../../kvm/VmCommon/src/verifierUtil.c:1595: error: static declaration of 'Vfy_verifyMethod' follows non-static declaration
../../../kvm/VmCommon/src/verifierUtil.c:571: note: previous implicit declaration of 'Vfy_verifyMethod' was here
../../../kvm/VmCommon/src/verifierUtil.c: In function 'Vfy_verifyMethod':
../../../kvm/VmCommon/src/verifierUtil.c:1598: error: invalid storage class for function 'Vfy_checkNewInstr tions'
../../../kvm/VmCommon/src/verifierUtil.c:1633: warning: implicit declaration of function 'Vfy_checkNewInstr tions'
../../../kvm/VmCommon/src/verifierUtil.c: At top level:
../../../kvm/VmCommon/src/verifierUtil.c:1671: error: conflicting types for 'Vfy_checkNewInstr tions'
../../../kvm/VmCommon/src/verifierUtil.c:1633: note: previous implicit declaration of 'Vfy_checkNewInstr tions' was here
make[1]: *** [obj/verifierUtil.o] Error 1
make[1]: Leaving directory `/root/linux-test/j2me_cldc/kvm/VmUnix/build'
make: *** [all] 错误 1
[root@localhost linux]#
(1)下载arm-linux-gcc-3.3.2.tar.bz2 : http://code.google.com/p/mini4020/downloads/detail?name=3.3.2.tar.bz2&can=2&q=label%3AMini4020
(2)修改第【4】中的Makefile,修改如下:
ifeq ($(GCC), tr)
CC = /usr/local/arm/3.3.2/arm-linux/bin/gcc
CFLAGS = -Wall $(CPPFLAGS) $(ROMFLAGS) $(OTHER_FLAGS)
DEBUG_FLAG = -g
OPTIMIZE_FLAG = -O2
FP_OPTIMIZE_FLAG =
【11】进入j2me_cldc/kvm/VmUnix/build编译
[root@localhost src]# cd ../build
[root@localhost build]# make
... ...
Linking ... kvm
make[1]: Leaving directory `/root/linux-test/j2me_cldc/kvm/VmUnix/build'
<<<Finished Recursively making ../../kvm/VmUnix/build all.
[root@localhost linux]#
OK,编译成功。
【12】查看下面两个重要文件
[root@localhost ~]# file /root/linux-test/j2me_cldc/kvm/VmUnix/build/kvm
/root/linux-test/j2me_cldc/kvm/VmUnix/build/kvm: ELF 32-bit LSB executable, ARM, version 1 (ARM), for GNU/Linux 2.0.0, dynamically linked (uses shared libs), for GNU/Linux 2.0.0, not stripped
[root@localhost ~]# file /root/linux-test/j2me_cldc/tools/preverifier/build/linux/preverify
/root/linux-test/j2me_cldc/tools/preverifier/build/linux/preverify: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), for GNU/Linux 2.6.9, not stripped
[root@localhost ~]#
可以看到,生成的kvm可执行文件是用来在目标板上运行java程序的,生成的preverify是用来预校验用。
【13】kvm验证测试
(1)进入到/root/java目录下,在linux主机上编译上面【1】jdk的测试文件helloworld.java
[root@localhost ~]# cd /root/java
[root@localhost java]# javac helloworld.java
[root@localhost java]
(2)将编译生成的j2me_cldc/tools/preverifier/build/linux/preverify文件复制到linux主机/bin目录下
[root@localhost ~]# cp linux-test/j2me_cldc/tools/preverifier/build/linux/preverify /bin
将linux-test/j2me_cldc/tools/jcc目录下classes.zip 和classesUnix.zip 分别解压到/root/java目录的classes 和classesUnix目录下
[root@localhost ~]# cd linux-test/j2me_cldc/tools/jcc
[root@localhost jcc]# unzip classes.zip -d /root/java/classes/
[root@localhost jcc]# unzip classesUnix.zip -d /root/java/classesUnix/
用下面命令进行预校验
preverify -classpath classesdir myclass
[root@localhost ~]# cd java
[root@localhost java]# preverify -classpath classes/classes classes/classesUnix HelloWorldApp
段错误
[root@localhost java]#
参考http://www.cnblogs.com/oomusou/archive/2008/06/14/j2me_cpld_preverify.html,知是使用的语系问题。Solution如下:
<1>显示目前使用的语系
[root@localhost java]# echo $LANG
zh_CN.UTF-8
不管目前使用的中文还是英文,只要是UTF-8就会出现上面错误。
<2>暂时修改成en_US.ISO8859-1语系
[root@localhost java]# export LANG=en_US.ISO8859-1
[root@localhost java]# echo $LANG
en_US.ISO8859-1
<3>再次执行preverify
[root@localhost java]# preverify -classpath classes/classes classes/classesUnix HelloWorldApp
[root@localhost java]#
执行成功。
(3)在目标板上运行测试
先将/root/linux-test/j2me_cldc/kvm/VmUnix/build目录下的kvm复制到开发板根目录即nfsboot/rootfs/usr/local/sbin目录下
[root@localhost ~]# cp linux-test/j2me_cldc/kvm/VmUnix/build/kvm /nfsboot/rootfs/usr/local/sbin
[root@localhost ~]#
然后将校验后生成的Java/output目录下HelloWorldApp.class复制到开发板的/root目录下
[root@localhost ~]# cp /root/java/output/HelloWorldApp.class /nfsboot/rootfs/root/
[root@localhost ~]#
(4)在开发板串口终端运行
在执行之前先确认开发板/etc/profile文件中的PATH系统变量有如下路径:
PS1='[\u@\h \W]\$'
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:$PATH
HOSTNAME='/bin/hostname'
export USER LOGNAME PS1 PATH
然后再串口终端中执行
[root@mini2440 /]#kvm
-/bin/sh: kvm: not found
[root@mini2440 /]#
问题分析:kvm是用arm-linux-gcc-3.3.2编译的,而内核是用arm-linux-gcc-4.4.3编译的。在【12】中用file指令查看结果是" for GNU/Linux 2.0.0 ”,显然出现了兼容问题。
由此,我突然想到一个问题,之前用arm-linux-gcc-4.4.3编译应用程序时,通过file查看,可以看到如"for GNU/Linux 2.6.32" 之类的信息,而编译kvm则是出现的" for GNU/Linux 2.0.0 ”这样的信息,arm-linux-gcc是如何知道内核版本信息的呢?