Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1695834
  • 博文数量: 584
  • 博客积分: 13857
  • 博客等级: 上将
  • 技术积分: 11883
  • 用 户 组: 普通用户
  • 注册时间: 2009-12-16 09:34

分类: LINUX

2010-01-04 17:31:13

0 Preface

两个月前,笔者在ChinaUnix BLOG上发表了一篇日志Building arm-linux toolchain for ARM/XSCALE,详细介绍了手工建立ARM-linux的交叉编译工具链的全过程,但这种方法越来越体现出局限性和落后性,主要表现在:

(1)使用的软件包版本较老;

(2)使用linux-threads而非新的NPTL(Native POSIX Threading Library)线程模型;

(3)所建立的toolchain遵循旧的ABI标准,而非新的

(4)对目标系统采用了旧的根文件系统的布局方式,所有的目标库文件都放在/lib目录下;

(5)并没有实现真正意义上为某一处理器特性(例如PXA27x系列的iWMMX技术)进行量身定制toolchain的目标。

此外,手工建立toolchain的过程非常繁琐,虽然可以对建立的过程进行全程控制,但稍有疏忽就会导致失败,因此是非常耗时耗力的。

本文分析了工具的工作机制,并以PXA27x处理器的cross toolchain建立过程为例,介绍了使用crosstool工具自动建立交叉编译工具链的方法。新的cross toolchain的特点在于:

(1)使用最新版本的软件包和内核(gcc-4.2.1/glibc-2.6.1/kernel-2.6.22)

(2)支持新的NPTL(Native POSIX Threading Library)线程模型;

(3)所建立的toolchain遵循新的

(4)对目标系统采用了新的根文件系统的布局方式,库文件分开放置在/lib/usr/lib目录下;

(5)针对PXA27x处理器的特性(),对toolchain进行了量身定制。


笔者建立交叉编译工具链的主机环境:

2.6.20-1.2962.fc6binutils-2.17.50gcc-4.1.1make 3.81


更多相关问题的说明见文后附录,包括一些具有实际参考价值的链接。


1 Choose and Download Source Packages

本文所选用的软件包及补丁如下:

上述软件包的选择依据来自

将这些软件包下载到特定的目录下(根据你自己的喜好和对crosstool的修改而定,本文下载到/home/aaronwong/crosstool/build/arm-iwmmxt-linux-gnueabi/gcc-4.2.1-glibc-2.6.1目录下,用crosstool中的变量表示就是$BUILD_DIR),手工解压缩并打好补丁。

glibc为例:

 tar xvf glibc-2.6.1.tar.bz2
 cd glibc-2.6.1/

 tar xvf ../glibc-ports-2.6.1.tar.bz2
 mv -v glibc-ports-2.6.1 ports

 patch -Np1 -i ../glibc-2.6.1-libgcc_eh-1.patch
 patch -Np1 -i ../glibc-2.6.1-localedef_segfault-1.patch
 patch -Np1 -i ../glibc-2.6.1-cross_hacks-1.patch
 patch -Np1 -i ../glibc-2.6.1-RTLD_SINGLE_THREAD_P-1.patch

[说明] 事实上,crosstool可以自动为我们下载指定的软件包及相应版本的补丁,并自动解压缩和打补丁,这通过getandpatch.sh脚本来实现。由于本文不需要其他补丁和软件包,而且所需补丁的下载地址并不在getandpatch.sh的搜索范围内,所以这里采用手工下载软件包,解压并打补丁的方式,并修改crosstool的相关脚本以禁止调用getandpatch.sh


2 Custom crosstool

2.1 Crosstool Intro

Dan Kegel等人开发的一套自动建立linux交叉编译工具链的自由软件,支持多种处理器体系结构,目前最新版本是crosstool-0.43。不过,“原始的”crosstool建立的toolchain遵循的是old ABI,不支持新的ARM EABI

,使得它可建立符合ARM EABI标准的toolchain。鉴于此,本文使用来建立toolchain

在对crosstool进行修改和配置之前,强烈推荐阅读crosstool-howto;若有能力则还可以通读crosstool的几个核心脚本,弄清楚变量的定义、用途以及传递方式(在此过程中您可能需要参考或其他文档来了解建立toolchain的基本过程以及各软件包的configure选项的用途)

2.2 Crosstool Mechanism

这里简单介绍crosstool工具自动建立toolchain的机制。Crosstool有如下几个核心文件:

cpu.dat:指定如下参数(仅列出所关心的几个)

KERNELCONFIG

指定linux内核的配置文件,用于产生glibc所需的内核头文件;若已有“纯净的”内核头文件,则不需要设置此参数。

TARGET

目标名,指定要建立的交叉编译工具为何种系统产生代码。例如arm-unknow-linux-gnu

TARGET_CFLAGS

通常指定为-O”,也可以不指定。

GCC_EXTRA_CONFIG

在建立gcc-coregcc时,传递额外参数给gccconfigure脚本,主要用于指定处理器特性和对相应库的设置,例如:

--with-cpu=iwmmxt --enable-cxx-flags=-mcpu=iwmmxt

GLIBC_EXTRA_CONFIG

在建立glibc时,传递额外参数给glibcconfigure脚本,例如目标处理器没有硬件浮点运算单元时,可以指定--without-fp

USE_SYSROOT

若留空,目标系统根目录采用传统方式布局(所有库文件都放在/lib目录下);若为1(非空),采用新的布局方式(库文件分开放置在/lib/usr/lib目录下)

gcc-glibc.dat:指定所用软件包解压之后的目录名,通常附带版本号。一个典型的例子如下:

BINUTILS_DIR=binutils-2.18

GCC_DIR=gcc-4.2.1

LINUX_SANITIZED_HEADER_DIR=linux-libc-headers-2.6.22.6

GLIBC_DIR=glibc-2.6.1

GLIBCPORTS_FILENAME=glibc-ports-2.6.1

GLIBC_ADDON_OPTIONS=ports,nptl

需要指出,LINUX_SANITIZED_HEADER_DIR指的是“纯净的”内核头文件的目录名。另外,如果使用的gcc-core软件包与gcc软件包版本不同,还要指定GCC_CORE_DIR;如果要编译GDB,则还要指定GDB_DIR

demo-*.sh(例如demo-arm-iwmmxt.sh):设置源码包的下载目录TARBALLS_DIR,以及toolchain的安装的顶层目录RESULT_TOP,还设置最终的cross-compiler的程序语言支持(GCC_LANGUAGES,默认为c,c++),然后将cpu.datgcc-glibc.dat中定义的参数传递给all.sh脚本,开始自动建立toolchain的过程。

all.sh检查所接收到的参数值,并设置内部变量的值(值得注意的是BUILD_DIRSRC_DIR, PREFIX, GCC_HOST等变量,下文会再说明),检查要下载、解压缩和打补丁、以及要编译的软件包,清除和建立相关目录,调用getandpatch.sh脚本自动下载所需的软件包到$BUILD_DIR并打补丁,然后调用crosstool.sh脚本自动建立toolchain,建立完毕再视用户传递过来的参数判断是否要编译gdb,是否要测试编译linux内核等。

getandpatch.sh:被all.sh调用,用于自动下载软件包,打补丁。

crosstool.sh:被all.sh调用,是建立toolchain的核心脚本文件。

其他脚本就不作介绍了。

另外,在crosstool目录下还有两个小程序。一是config.guess脚本,如果不指定BUILD变量的值,则在crosstool.sh脚本中,会把config.guess的运行结果作为BUILD的值;在笔者的主机上运行结果为i686-pc-linux-gnu。二是fix-embedded-paths.c,在完成toolchain的建立之后,crosstool.sh脚本会把它编译为一个可执行文件,可以用来手动调整toolchain的安放位置。

其他说明:

(1)BUILDGCC_HOSTGCC_BUILD:在crosstool.sh中。

缺省情况下,$GCC_BUILD=$GCC_HOST=$BUILD`./config.guess`GCC_HOSTGCC_BUILD相同表示不是”Canadian cross”方式(所谓Canadian cross ,是指建立toolchain的机器平台GCC_BUILD与运行toolchain的机器平台GCC_HOST不同),而一般情况下我们建立toolchain的机器就是运行toolchain编译目标平台代码的机器。这样,$GCC_HOST就可作为参数传递给gccconfigure脚本的—host选项,即--host=i686-pc-linux-gnu,表示最后编译出来的cross-compiler所运行的主机类型。$BUILD则作为参数传递给glibcconfigure脚本的--build选项。

(2)SHARED_MODE:在crosstool.sh中。缺省情况下为—enable-shared

(3)什么是gcc-core?通俗的说,就是用来编译glibccompiler,亦即gcc stage_1,或说是bootstrap gcc


2.3 Custom our own Crosstool

2.3.1 下载crosstool-0.42并打补丁

下载,解压缩并打补丁。

  tar xvf crosstool-0.42.tar.gz
  cd crosstool-0.42
  patch -Np1 -i ../crosstool-0.42-mg2.patch

2.3.2 定制自己的crosstool

(1) 建立自己的crosstool目录,并拷贝必要的文件。

  #assuming $PWD=~/downloads/crosstool-0.42
  mkdir ~/crosstool                #build our own crosstool directory
  cp -v demo-arm-iwmmxt.sh ~/crosstool
  mkdir demo-script
  mv demo* demo-script/       #we do not need other demo-*sh scripts
  cp -av *.sh ~/crosstool     #copy all.sh,crosstool.sh,... to our own directory
  cp -av *.c ~/crosstool        #copy fix-embedded-paths.c,... to our own directory
                                        #you can skip this step if you do not need it
  cp config.guess ~/crosstool #we need config.guess to get the value for $BUILD

(2) 编辑cpu.datgcc-glibc.dat文件。

显然我们还缺少这两个核心文件来传递必要的参数,我们将这两个文件分别命名为arm-iwmmxt-eabi.datlatest.dat。编辑arm-iwmmxt-eabi.dat文件如下:

TARGET=arm-iwmmxt-linux-gnueabi

TARGET_CFLAGS="-O"

GCC_EXTRA_CONFIG="--with-float=soft --with-cpu=iwmmxt --with-arch=iwmmxt --enable-cxx-flags=-msoft-float"

GLIBC_EXTRA_CONFIG="--without-fp --disable-libunwind-exceptions"

GLIBC_EXTRA_CC_ARGS="-finline-limit=10000"

USE_SYSROOT=1

注意,TARGET的名字并不是随意的,必须以arm开头而不能是pxa27x等其他字符串开头,因为crosstool会根据TARGET的名字来解析$ARCH的值。gccconfigure文件也要根据TARGET名字来进行裁决,如果要编译新的ARM EABI标准的toolchainTARGET名字应该以arm*-*-linux-gnueabi形式出现。这里设置USE_SYSROOT是要按新的组织形式来安排目标系统根目录的库文件。

编辑latest.dat如下:

BINUTILS_DIR=binutils-2.18

GCC_DIR=gcc-4.2.1

LINUX_SANITIZED_HEADER_DIR=linux-libc-headers-2.6.22.6

GLIBC_DIR=glibc-2.6.1

GLIBCPORTS_FILENAME=glibc-ports-2.6.1

GLIBC_ADDON_OPTIONS=ports,nptl

(3) demo-arm-iwmmxt.sh重命名为arm-iwmmxt-eabi.sh,并编辑如下:

#!/bin/sh
set -ex

TARBALLS_DIR=$HOME/crosstool/downloads
RESULT_TOP=/opt/crosstool     
GCC_LANGUAGES="c,c++"
export GCC_LANGUAGES

PARALLELMFLAGS=-j3
export PARALLELMFLAGS

# Really, you should do the mkdir before running this,
# and chown /opt/crosstool to yourself so you don't need to run as root.
mkdir -p $RESULT_TOP

# Build the toolchain.  Takes a couple hours and a couple gigabytes.
time eval `cat arm-iwmmxt-eabi.dat latest.dat`  sh all.sh --notest

echo Done.

(4) 修改all.sh

注释掉getandpatch.sh的调用部分。因为我们是手工下载源码包并打补丁的,只要把处理好的源代码放到相应目录下即可,不需要getandpatch.sh自动下载源码包。

if test "$opt_no_unpack" = ""; then
   if test "$opt_builduserland" = "1"; then
      # Ah, nobody would want to change this :-)
      PTXDIST_DIR=ptxdist-testing-20031113
      export PTXDIST_DIR
   fi
   # Download and patch
##################commented out by aaron##############
#   if test -d "$BUILD_DIR"; then
    # Remove in background
#       mv $BUILD_DIR $BUILD_DIR.del.$$
#       rm -rf $BUILD_DIR.del.$$ &
#   fi
#   mkdir -p $BUILD_DIR
#   sh getandpatch.sh
#######################################################
fi

根据个人喜好修改PREFIX的值:

# Arbitrary locations for the input and output of the build.
# Change or override these to your taste.
#################commented out by aaron########################
#TARBALLS_DIR=${TARBALLS_DIR-$TOP_DIR/tarballs}
#RESULT_TOP=${RESULT_TOP-$TOP_DIR/result}
###############################################################
####################modified by aaron##########################
#PREFIX=${PREFIX-$RESULT_TOP/$TOOLCOMBO/$TARGET}
PREFIX=$RESULT_TOP

设置“PREFIX=$RESULT_TOP=/opt/crosstool”可使目标系统的根目录更“浅”,这是因为crosstool.sh中设置了SYSROOT=${PREFIX}/${TARGET}/sys-root,于是相当于有$SYSROOT=/opt/crosstool/arm-iwmmxt-linux-gnueabi/sys-root

禁止自动进行目录清理:

if test "$opt_no_build" = ""; then
    # Build
###################commented out by aaron################
#    if [ -d "$PREFIX" ]; then
    # Remove in background for speed
#    mv "$PREFIX" "$PREFIX.del.$$"
#    rm  -rf  "$PREFIX.del.$$" &
#    fi
#    mkdir -p $PREFIX
#    mkdir -p $BUILD_DIR
##########################################################
    cd $BUILD_DIR

由于arm-iwmmxt-eabi.sh中调用all.sh时没有传递--nobuild参数,因此上面被注释掉的语句会清除旧的$BUILD_DIR,建立一个新的干净的$BUILD_DIR,然后再调用crosstool.sh建立toolchain

另一方面,由于我们是要手工将源码包处理好放到SRC_DIR的,又因为没有给出SRC_DIR的值,all.sh认为SRC_DIR=$BUILD_DIR,这样实际上要求我们把源码包放到BUILD_DIR下,于是上面的语句恰好把我们的SRC_DIR连同其中处理好的源码包一起删除了。所以要把上面的语句注释掉。

[说明]另外一种简单解决方法是,在arm-iwmmxt-eabi.sh中调用all.sh时传递—nobuild参数,然后把手工处理好的源码包都放到SRC_DIR(实际上也就是相同的BUILD_DIR),这样就不必注释掉上面的语句了。

其他注释:

#    sh testhello.sh      #commented out by aaron

(5) 修改crosstool.sh

修正内核头文件的安装。

cp -r include/linux $HEADERDIR
cp -r include/asm-${ARCH} $HEADERDIR/asm
################added by aaron#########################
cp -r include/asm-generic $HEADERDIR/asm-generic

cd $BUILD_DIR

按照原来的脚本,会漏拷include/asm-generic目录,导致编译时找不到头文件而报错退出。

禁止修改GCC_HOST

# if host is cygwin and this is not a canadian build, modify GCC_HOST
########################commented out by aaron################################
#case "$GCC_HOST,$CANADIAN_BUILD," in
#*cygwin*,?*,) ;;
#*)            GCC_HOST=`echo $GCC_HOST | sed s/-/-host_/` ;;
#esac
##############################################################################

本来有GCC_HOST=`./config.guess`=i686-pc-linux-gnu,上面的语句会将其更改为GCC_HOST=i686-host_pc-linux-gnu,导致在编译时找不到主机的CC而出错。

2.3.3 源码包处理与目录组织

接下来要按上面脚本中的目录设置来组织源码包并建立相应目录。

(1) 必须手动建立的目录: RESULT_TOP=/opt/crosstool(arm-iwmmxt-eabi.dat),以及BUILD_DIR=`pwd`/build/$TARGET/$TOOLCOMBO (all.sh) = ~/crosstool/build/arm-iwmmxt-linux-gnueabi/gcc-4.2.1-glibc-2.6.1

  mkdir -p ~/crosstool/build/arm-iwmmxt-linux-gnueabi/gcc-4.2.1-glibc-2.6.1
  sudo mkdir /opt/crosstool
  sudo chown -Rv aaronwong /opt/crosstool

注意,用root用户建立交叉编译工具是不安全的,应该用普通用户登录;因此必须要改变安装目录/opt/crosstool的所有者,以让普通用户(笔者是aaronwong)拥有该目录的读写权限。

(2) 源码包处理:将所有的源码包放到$BUILD_DIR目录下,并解压缩,打补丁。最后的各源码包目录名应该与latest.dat中设定的一致。以linux內核头文件安装为例:

  tar xvf linux-headers-2.6.22.6-09032007.tar.bz2
  cd linux-headers-2.6.22.6
  install -dv ../linux-libc-headers-2.6.22.6
  cp -av include/{asm-generic,asm-arm,linux,mtd,scsi,sound} ../linux-libc-headers-2.6.22.6


2.3.4 开始自动安装

经过前面的定制之后,只要在终端下输入命令”sh arm-iwmmxt-eabi.sh”,计算机就开始自动编译安装toolchain,大约40 – 90分钟便可完成(视主机系统的配置而定)

[提示] 2.3节仅是笔者根据需要对crosstool所作的定制,采用手动下载源码包并打补丁的方法,建立支持PXA27x处理器iWMMX技术的符合ARM EABI标准的toolchain。读者可以在理解crosstool工作机制的前提下,根据自己的实际情况作相应的定制。


3 Install and Test Toolchain

3.1 自动安装及路径设置

其实上面一节已经介绍了执行脚本(arm-iwmmxt-eabi.sh)进行自动安装的步骤,这里重提仅是为了章节的连续性~ ^_^

自动安装完毕还应该将toolchain的路径添加到环境变量PATH中去,这样只需用可执行文件名便可调用toolchain,而不必使用绝对路径。

vim编辑~/.bash_profile,加入下面一行:

PATH=/opt/crosstool/bin:$PATH

保存退出,执行”source ~/.bash_profile”,使刚才的修改生效(仅对当前终端有效,若要全局有效则需要注销或重启)


3.2 Toolchain测试

最简单和最基本的测试如下:

对于所建立的交叉编译工具链的测试,更严格有效的测试是对目标系统的内核进行编译,并在构建好目标系统后,能编译出在目标平台上正常运行的应用程序。

Appendix A. About ARM EABI

ARM Ltd.200312月发布了ARM ABI(v1),于2005年第一季度发布了ARM ABI (v2),官方主页是

The Application Binary Interface (ABI) for the ARM® Architecture is a collection of standards, some open and some specific to the ARM architecture. The standards regulate the inter-operation of binary code, development tools, and a spectrum of ARM core-based execution environments from bare metal to platform operating systems such as ARM Linux.

Version 1.0 of the base standard was principally concerned with governing inter-operation between relocatable files. This release - version 2.0 - also sets standards for executable files.”

                                                                                                 ——from

Question: What is the ABI for the ARM Architecture? Is it the same as the ARM EABI?

Answer: The ABI for the ARM Architecture is a standard developed by ARM and its partners (including CodeSourcery) that explains how compilers, assemblers, linkers, and other similar tools should generate object files and executable files. Tools that correctly implement the ABI for the ARM Architecture can interoperate; i.e., objects files built with one toolchain can be combined with object files built with another toolchain if both compilers use the ABI for the ARM Architecture. The "ARM EABI" is an informal name for the ABI for the ARM Architecture.

Question: Why is the configuration name for GNU/Linux arm-none-linux-gnueabi instead of just arm-none-linux-eabi? Is there a GNU variant of the EABI?

Answer: The Free Software Foundation prefers that configuration names for GNU/Linux contain both the string linux and the string gnu.The configuration arm-none-linux-gnu refers to the legacy ARM ABI for GNU/Linux. Some tools depends on the fact that configuration names have at most three hyphens, so gnu and eabi were combined into a single word.

The ABI used on GNU/Linux is not a special GNU variant of the EABI; it is just the EABI.

Question: Is it possible to run both EABI and non-EABI binaries on the same system?

Answer: Yes, it is possible. However, running a dual-ABI system requires care. If you have an old-ABI system, and want to run EABI binaries, you have several choices. One option is to link your programs statically, using the -static option. However, linking dynamically is generally a superior approach. CodeSourcery's toolchains use a different dynamic loader (ld.so.3), so it is possible for old-ABI and EABI binaries to coexist. However, you will have to ensure that when you install the EABI libraries you do not overwrite existing libraries, and you will have to set LD_LIBRARY_PATH so that the dynamic loader can find them.

                                                                                      ——from

另外,Debian Wiki上也有关于,非常精彩。

Russell King也曾有关于的论述。


Appendix B. LinuxThreads and NPTL

“LinuxThreads项目最初将多线程的概念引入了Linux,但LinuxThreads并不遵守POSIX线程标准。”

“LinuxThreads项目使用这个调用(笔者注:指clone()系统调用)来完成在用户空间模拟对线程的支持。……这种方法有一些缺点,尤其是在信号处理、调度和进程间同步原语方面都存在问题。另外,这个线程模型也不符合POSIX的要求。”

“NPTL,或称为Native POSIX Thread LibraryLinux线程的一个新实现,它克服了LinuxThreads的缺点,同时也符合POSIX的需求。与LinuxThreads相比,它在性能和稳定性方面都提供了重大的改进。”

——摘自:Linux线程模型的比较:LinuxThreadsNPTL (Vikaram Shukla)


Appendix C. About Linux kenerl version and headers

1. Linux内核头文件

编译Glibc必须要有linux内核头文件。最初人们直接使用内核源码包中的头文件(/usr/include目录下),后来由于内核开发者强烈反对这种做法,于是出现了linux-libc-headers项目,以为Linux头文件维护一个稳定版本的API。最近该项目已经停止发展(该项目提供的最新版本的“纯净的”头文件版本是2.6.12.0);从2.6.18版本开始,内核开发组负责维护一份统一的、“纯净的”内核头文件,获取编译glibc所需的头文件只需要使用命令”make headers_install”即可。

您也许会对以及感兴趣。


2. Linux内核版本选择

为目标系统选择一个Linux内核版本,除了要考虑实际应用的需求之外,还要考虑到与交叉编译工具链的匹配、内核新特性等诸多方面。

以本文为例,建立交叉编译工具链时使用2.6.22.6版本的内核头文件来编译Glibc,那么使用这个Glibc编译得到的应用程序可以用于2.6.22.6以及以前版本的linux系统,这是其向后兼容(backwards compatability)特性决定的——那么最早可以用于哪个版本的Linux系统呢?这是在编译Glibc时使用—enable-kernel选项传递给configure脚本来限制的,例如--enable-kernel=2.6.14,就表示运行应用程序的Linux系统内核版本至少要求为2.6.14。这个应用程序可否用于2.6.22.6以上版本的Linux系统呢?原则上也是可以的,只要更高版本的Linux系统配置为向后兼容,不过,即使这个应用程序可以运行在高版本的内核系统上,也不会支持高版本系统的新特性。

那么目标系统的Linux内核版本与交叉编译工具的匹配要考虑哪些方面呢?这其实没有一个公认的标准,其匹配性也受错综复杂的内在因素的影响。一般而言,内核没有发生重大变化的条件下,可以使用建立toolchain所用内核版本(2.6.22.6)及其之前的一些版本,也可能可用于后续的几个版本(没有保证)——这是因为随着内核版本的更新,会有越来越多的新特性添加进来,甚至会丢弃一些旧的特性。例如,ARM EABI就需要内核本身对它的支持,另外,本文所编译的Glibc也需要NPTL的内核支持;而Linux 2.6.16是第一个具有上述所有特性的内核发行版,因此,与本文的toolchain匹配性较好的内核版本应该是介于2.6.162.6.22.6之间。

若要使用2.6.14版本的内核,则至少需要打上EABINPTL的相关补丁(3101/1, 3102/1, 3103/1, 3104/1, 3105/4, 3106/2, 3107/3, 3108/2, 3109/1, 3110/2, 3111/2, 3112/1, 3205/1, 3210/1, 3270/1, 3271/1, 3339/1, 3477/1, 3484/1, 3495/1, 3524/1, 3626/1, 3729/3, 3750/3)

Referece:


Appendix D. References

这里仅列出主要参考资料,文中已给出链接的部分参考资料不再列出。

[1] , crosstool main page

[2] , and

[3] ,

[4] ,

[5]

[6]

[7]

[8]

[9]

[10]

[11]

[12] ,

[13]

[14] youbest(冲天飞豹)

[15] youbest(冲天飞豹)

[16] Building arm-linux toolchain for ARM/XSCALE (Old ABI)Aaron Wong


About the author

Aaron Wong (黄振华), received the B.S.degree in Electronic Engineering from Fudan 
University in 2005, and now study for the M.S. degree of Communication & Information
System in East China Normal University.

[注1]纰漏之处,恳请指正。

[注2]如需转载,请注明作者及出处。

[注3]您可以使用该链接引用这篇文章:http://blog.chinaunix.net/u/26710/showart_394113.html

阅读(921) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~