Ubuntu下华硕工具链交叉编译SSR的ARM版本

Ubuntu下华硕工具链交叉编译SSR的ARM版本无评论

2017年5月19日 at 下午2:42分类:其他 阅读: 2,843 次

1、下载源码
先新建一个目录ss:
mkdir ss
全路径为/home/yushi/ss/

然后进入ss目录,下载源码
cd ss
wget https://tls.mbed.org/download/mbedtls-2.4.0-apache.tgz
tar zxvf mbedtls-2.4.0-apache.tgz
mkdir mbedtls

wget https://downloads.sourceforge.net/project/libpng/zlib/1.2.11/zlib-1.2.11.tar.gz
tar zxvf zlib-1.2.11.tar.gz
mkdir zlib

wget http://ftp.cs.stanford.edu/pub/exim/pcre/pcre-8.39.tar.gz
tar zxvf pcre-8.39.tar.gz
mkdir pcre

git clone https://github.com/shadowsocksr/shadowsocksr-libev.git
mkdir ss

2、设置环境变量
export PATH=$PATH:/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin
export STAGING_DIR=/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/lib
export CC=arm-uclibc-linux-2.6.36-gcc
export CXX=arm-uclibc-linux-2.6.36-g++
export AR=arm-uclibc-linux-2.6.36-ar
export RANLIB=arm-uclibc-linux-2.6.36-ranlib

3、编译
先安装Ubuntu依赖包
sudo apt-get install asciidoc
asciidoc包比较大

cd mbedtls-2.4.0
vi Makefile#修改第二行为DESTDIR=/home/yushi/ss/mbedtls
make && make install

cd ../
cd zlib-1.2.11
./configure --prefix=/home/yushi/ss/zlib
make && make install

cd ../
cd pcre-8.39
./configure --prefix=/home/yushi/ss/pcre --host=arm-uclibc-linux
make && make install

cd ../
cd shadowsocksr-libev
./configure --prefix=/home/yushi/ss/ss --with-mbedtls=/home/yushi/ss/mbedtls --with-zlib=/home/yushi/ss/zlib --with-pcre=/home/yushi/ss/pcre --host=arm-uclibc-linux --with-crypto-library=mbedtls --disable-ssp
make && make install

生成的文件在/home/yushi/ss/ss/bin目录
2017-05-19 15-19-15
可以压缩一下
cd /home/yushi/ss/ss/bin
arm-uclibc-strip ss-local ss-redir

编译基于openssl的版本:
新建/home/yushi/ssr目录
wget https://github.com/openssl/openssl/archive/OpenSSL_1_0_1t.zip
unzip OpenSSL_1_0_1t.zip
mkdir openssl

cd openssl-OpenSSL_1_0_1t
./config --prefix=/home/yushi/ssr/openssl no-asm no-shared os/compiler:arm-uclibc-linux-2.6.36-gcc
make && make instal

cd ..
git clone https://github.com/ywb94/shadowsocks-libev.git
mkdir ssr
cd shadowsocks-libev
./configure --prefix=/home/yushi/ssr/ssr --with-pcre=/home/yushi/ss/pcre --with-openssl=/home/yushi/ssr/openssl --with-zlib=/home/yushi/ss/zlib -host=arm-uclibc-linux --disable-ssp
make && make install

cd /home/yushi/ssr/ssr/bin
arm-uclibc-strip ss-local ss-manager ss-redir ss-server ss-tunnel

腾达AC9基于华硕RT-AC1200G+源码及梅林源码固件改造

腾达AC9基于华硕RT-AC1200G+源码及梅林源码固件改造有26条评论

2017年5月11日 at 上午9:44分类:其他 阅读: 12,155 次

固件下载地址:
https://pan.baidu.com/s/1slAWNKT

 

一、前言:开源固件的前世今生

为何华硕固件开源?

2003年Linksys公司推出WRT-54G,一款基于MIPS架构的无线路由器,WRT-54G操作系统以Linux取代vXworks,哥伦比亚大学法学院教授Eben Moglen向Linksys提出开源要求。
2003年7月,Linksys迫于压力,开源了WRT54G的firmware。
2004年1月出现所谓的OpenWRT,第一个版本是基于Linksys源码及uclibc中的buildroot项目。
2005年初,BrainSlayer发布了一个新的发行版:DD-WRT。接着又有HyperWRT、Tomato。

从此以后,开源路由器系统蓬勃发展,并吸引了一部分厂家的加入 。

 

华硕路由器固件(Asuswrt)就是基于Tomato-RT/Tomato-USB进行二次开发的版本,需遵守GPL开源协议,除了少部分比如 Broadcom/Ralink驱动以二进制格式闭源提供外,其他大部分模块都以源码格式开源提供。

 

何为梅林固件、padavan固件?

梅林固件基于华硕固件进行修改和增强,主要支持基于broadcom的设备,如ARM架构的平台(比如AC9):

原版梅林(Asuswrt-merlin)
https://asuswrt.lostrealm.ca
原始设备:
RT-N66U RAM (256Mb) FlashRAM (32Mb) CPU:BCM4706KPBG. Switch:BCM53125SKMML.

padavan也是基于华硕固件修改,padavan针对的是mips(如7620)架构的平台(比如K2)
原版padavan:
https://bitbucket.org/padavan/rt-n56u
原始设备:
RT-N56U RAM (128Mb) FlashRAM (8Mb) CPU:Ralink RT3662F. Switch:Realtek RTL8367M WLAN: Ralink RT3092L
由于开发人员为俄罗斯人,又称老毛子固件

 

国内常用的梅林、padavan修改版本:

koolshare上面是小宝修改版merlin,Koolshare改版梅林主要就是多了以软件中心为主的第三方应用功能,譬如迅雷快鸟,SS,阿呆猫,kms自建服务器,广告过滤。

恩山上常见的是hiboy修改版padavan

还有一个俄罗斯人开发的Xwrt-Vortex,增加了支持设备
http://xvtx.ru/xwrt/
支持以下型号:
Netgear R7000
Linksys EA6900
Huawei WS880

 

二、华硕原版固件编译

编译环境:
Ubuntu 16.04 64位系统

1、下载源码

华硕3.0.0.4.380_4089源码:

http://dlcdnet.asus.com/pub/ASUS/wireless/RT-AC1200G+/GPL_RT_AC1200GPlus_30043804089.zip

2、安装依赖包

sudo apt-get install --no-install-recommends autoconf automake bash bison bzip2 diffutils file flex g++ gawk gcc-multilib gettext gperf groff-base libncurses-dev libexpat1-dev libslang2 libssl-dev libtool libxml-parser-perl make patch perl pkg-config python sed shtool tar texinfo unzip zlib1g zlib1g-dev

sudo apt-get install lib32stdc++6 lib32z1-dev

sudo apt-get --no-install-recommends install automake1.11

sudo apt-get install libelf-dev:i386 libelf1:i386

sudo apt-get --no-install-recommends install lib32z1-dev lib32stdc++6

3、修改编译

解压源码到/opt/ac1200g目录

修改GPIO设置适配AC9
asuswrt\release\src-rt-9.x\src\router\rc\init.c,搜索case MODEL_RTAC1200GP,修改GPIO:
nvram_set_int("btn_rst_gpio", 7|GPIO_ACTIVE_LOW);
nvram_set_int("btn_wps_gpio", 9|GPIO_ACTIVE_LOW);
nvram_set_int("led_pwr_gpio", 15);
nvram_set_int("led_wps_gpio", 10);
nvram_set_int("led_usb_gpio", 1);

设置环境变量

export PATH=$PATH:/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin

编译
cd /opt/ac1200g/asuswrt/release/src-rt-9.x/src/
make RT-AC1200G+

排错:
提示:
*** No rule to make target 'tcode.c', needed by 'tcode.o'。
解决:
修改src/router/shared/Makefile,将“%.o: prebuild/%.o”改变一下位置:

%.o: prebuild/%.o
@echo " [shared] cp $@"
@cp -f $< $@

%.o: %.c .%.depend
@echo " [shared] CC $@"
@$(CC) $(CFLAGS) -o $@ -c $<

.depend: $(OBJS:%.o=%.c)
@$(CC) $(CFLAGS) -M $^ > .depend
编译成功生成的固件在/opt/ac1200g/asuswrt/release/src-rt-9.x/src/image目录下
2017-05-11 11-15-39

4、模块定制
修改/opt/ac1200g/asuswrt/release/src-rt-9.x/src/target.mak文件,搜索RT-AC1200G+:

可以看到,RT-AC1200G+是基于RT-AC1200G_BASE进行模块、特性增加,可以修改相应的特性=y或=n来增减特性

也可以通过图形界面来进行定制:
cd /opt/ac1200g/asuswrt/release/src/router
export SRCBASE=/opt/ac1200g/asuswrt/release/src-rt-9.x/src
make menuconfig
2017-05-13 16-11-31

三、源码修改与特性增加

华硕AC1200G+与AC9硬件对比:
https://wikidevi.com/wiki/ASUS_RT-AC1200GP
AC1200G+:CPU:BCM47189、2.4G: BCM43217,5G:Broadcom BCM47189集成、交换芯片:Broadcom BCM 53125、16 MB Flash、128 MB RAM
AC9: CPU:BCM47189、2.4G: BCM43217,5G:Broadcom BCM47189集成、交换芯片:Broadcom BCM 53125、8 MB Flash(硬改16M)、128 MB RAM

硬件上基本一致,因此固件上可以兼容。下面为针对固件的修改过程。

 

【增加无线扫描功能】

我们首先参照梅林固件的源码来为AC9增加一个特性:搜索周围的无线网络
1、修改/opt/ac1200g/asuswrt/release/src/router/www/state.js,增加搜索页面
搜索“tabtitle[0]”,修改:
tabtitle[0] = new Array("", "<#menu5_1_1#>", "<#menu5_1_2#>", "WDS", "<#menu5_1_4#>", "<#menu5_1_5#>", "<#menu5_1_6#>", "Wi-Fi Proxy");
变为:
tabtitle[0] = new Array("", "<#menu5_1_1#>", "<#menu5_1_2#>", "WDS", "<#menu5_1_4#>", "<#menu5_1_5#>", "<#menu5_1_6#>", "Site Survey", "Wi-Fi Proxy");

搜索“tablink[0]”,修改:
tablink[0] = new Array("", "Advanced_Wireless_Content.asp", "Advanced_WWPS_Content.asp", "Advanced_WMode_Content.asp", "Advanced_ACL_Content.asp", "Advanced_WSecurity_Content.asp", "Advanced_WAdvanced_Content.asp", "Advanced_WProxy_Content.asp");
变为:
tablink[0] = new Array("", "Advanced_Wireless_Content.asp", "Advanced_WWPS_Content.asp", "Advanced_WMode_Content.asp", "Advanced_ACL_Content.asp", "Advanced_WSecurity_Content.asp", "Advanced_WAdvanced_Content.asp","Advanced_Wireless_Survey.asp", "Advanced_WProxy_Content.asp");

2、拷贝Advanced_Wireless_Survey.asp到/opt/ac1200g/asuswrt/release/src/router/www目录
从梅林源文件中拷贝Advanced_Wireless_Survey.asp到/opt/ac1200g/asuswrt/release/src/router/www目录

3、重新编译固件,并刷入AC9

【多语言支持】

如果想支持多语言,可以将tabtitle[0]中的Site Survey修改:

tabtitle[0] = new Array("", "<#menu5_1_1#>", "<#menu5_1_2#>", "WDS", "<#menu5_1_4#>", "<#menu5_1_5#>", "<#menu5_1_6#>", "<#menu5_1_7#>", "Wi-Fi Proxy");

然后在/opt/ac1200g/asuswrt/release/src/router/www/CN.dict中增加:

menu5_1_7=无线扫描

在/opt/ac1200g/asuswrt/release/src/router/www/EN.dict中增加:

menu5_1_7=Site Survey

【精简语言包】

修改/opt/ac1200g/asuswrt/release/src/router/tools/Lnx_AsusWrtDictPrep/dictctrl.txt,只保留中英文
[MODEL]
RT-AC1200G+
EN,CN

重新编译,固件由原来13M变为12M
2017-05-11 23-13-36

【版本号及界面修改】

主版本号位于/opt/ac1200g/asuswrt/release/src-rt/version.conf

子版本号位于/opt/ac1200g/asuswrt/release/src/router/extendno.conf

型号在dict字典文件Web_Title和Web_Title2字段

图标在/opt/ac1200g/asuswrt/release/src/router/www/images/New_ui/asustitle.png

修改后的版本及界面如下:

 

【增加用户脚本功能】

用户脚(User scripts)本功能是梅林固件一个比较重要的特性,它支持在不同的事件触发下执行用户脚本。比如系统启动后执行脚本(init-start)、网络连接后执行脚本(wan-start)或者在所有服务启动后执行脚本(services-start)。
参考:
https://github.com/RMerl/asuswrt-merlin/wiki/User-scripts

脚本由用户自己建立,存放在/jffs/scripts目录,并需赋予脚本可执行权限:
chmod a+rx /jffs/scripts/*

比如我要增加一个定时重启任务:
新建/jffs/scripts/init-start文件:
vi /jffs/scripts/init-start
输入如下语句:
#!/bin/sh
cru a ScheduledReboot "0 4 * * * /sbin/reboot"
保存退出后赋予文件执行权限,上述语句设置每晚4点重启路由器
chmod a+rx /jffs/scripts/*
重启路由器生效
cru是华硕固件中的定时任务程序,类似于crontab
# cru
Cron Utility
add: cru a <"min hour day month week command">
delete: cru d
list: cru l

要增加用户脚本功能,我们可以参照梅林固件的源码进行修改,主要是/opt/ac1200g/asuswrt/release/src/router/rc和/opt/ac1200g/asuswrt/release/src/router/share里面的部分c文件,修改后的文件打包如下:
user_script

【增加梅林工具箱】
首先要在界面上增加菜单,工具箱为一级菜单,里面有两个子菜单:系统信息和其他工具
参照梅林源码修改/opt/ac1200g/asuswrt/release/src/router/www/state.js
拷贝www目录下Tools_OtherSettings.asp、Tools_Sysinfo.asp文件
拷贝覆盖图标文件www/images/New_ui/icon_indexes.png,修改www/index_style.css文件

在Tools_Sysinfo.asp文件中调用了sysinfo命令,此命令由httpd进程处理,需拷贝/opt/ac1200g/asuswrt/release/src/router/httpd下的sysinfo.c、sysinfo.h,并修改web.c,映射WEB命令到处理函数。最后修改httpd的makefile进行重新编译。

增加的工具箱界面:

【移植ipset】
很多特性都需要ipset模块,而华硕固件缺省并没有集成ipset,因此我们需要进行移植

拷贝梅林源码目录/opt/ac1200g/asuswrt/release/src/router/ipset、ipset_arm、libmnl, libnfnetlink目录,并且修改router目录下的makefile及相关文件,开启dnsmasq的ipset特性

修改内核/opt/ac1200g/asuswrt/release/src-rt-9.x/src/linux下的相关文件(netfilter)

修改release/src/router/rom/rom/etc/modprobe.conf,加载ipset内核

修改完成后,重新编译、烧写,可以输入ipset creat test hash:net && ipset list来验证模块是否正常

【增加koolproxy】

下面我们在工具箱中增加koolproxy广告过滤特性

1、首先修改/opt/ac1200g/asuswrt/release/src/router/www/state.js文件,增加菜单

2、然后设计koolproxy的ASP页面

3、接下来是让页面上的元素和nvram中的参数对应,比如koolproxy使能按钮,它的id为koolproxy_enable,提交的表单一般为start_apply.htm,表单中有几个重要的参数



指定操作方式、等待时间及执行的脚本名称等。action_mode可以为apply_new或apply,区别是apply_new只有在参数改变时才执行脚本,apply则都会执行脚本。
提交表单后在start_apply.htm中会执行一个重要命令:
update_variables();
我们可以在web.c文件中看到此命令:
{ "update_variables", ej_update_variables},
映射到ej_update_variables函数,在此函数中会调用validate_apply函数,此函数会遍历nvram中的各个参数
for (t = router_defaults; t->name; t++)
比较nvram中的参数与表单中的参数值是否一致,如不一致则将新的值写入nvram
注:router_defaults存放着nvram的各个参数,在share/default.c文件中定义,我们需要先将新增的koolproxy参数(如koolproxy_enable)也在此文件定义

nvram写入后,下面是action_script脚本执行的操作,会调用notify_rc函数
strncpy(notify_cmd, action_script, 128);
..
nvram_set("freeze_duck", "15");
notify_rc(notify_cmd);
在notify_rc的处理中,会先打印日志:
logmessage_normal("rc_service", "%s %d:notify_rc %s", p2, getpid(), event_name);
然后通知rc程序执行响应命令:
nvram_set("rc_service", event_name);
nvram_set_int("rc_service_pid", getpid());
kill(1, SIGUSR1);

如果要直接调用脚本,可以修改ej_update_variables函数,在适当位置调用sys_script执行脚本

完成后的koolproxy界面如下:

小技巧:
调试时如果想修改固件中的文件,用如下命令:
mount --bind 目的名 原名

mount --bind /tmp/Tools_Koolproxy.asp /www/Tools_Koolproxy.asp
将/www/Tools_Koolproxy.asp映射到/tmp/Tools_Koolproxy.asp文件,你可以修改此文件,修改后的内容即时在WEB上显示。利用此特性,你可以修改任何现有系统的脚本或程序,指向/jffs.

【增加SSR】

首先第一步,你需要获得ssr for arm 的可执行程序
可以参照此文进行交叉编译获得:
http://iytc.net/wordpress/?p=2286

其次我们需要设计ssr的交互界面,由于SSR需要设置多个服务器配置,因此我们参照家长控制页面(ParentalControl.asp)进行修改

修改后的界面如下:

最后我们要将界面和脚本进行关联,在提交页面后,会先保存服务器参数到nvram中,然后会调用指定脚本,在脚本中会读取nvram中的服务器参数,并启动ssr程序及生成对应的防火墙规则。

交叉编译busybox for arm

交叉编译busybox for arm无评论

2017年5月5日 at 上午8:56分类:其他 阅读: 1,293 次

1、下载源码,解压
https://busybox.net/downloads/
2、配置
make menuconfig
在menuconfig中,我们进入Busybox Settings中的Build Options中,配置交叉编译路径:
/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin/arm-linux-
2017-05-05 08-53-34
然后根据需要选择要包含的工具包
3、编译
先设置环境变量:
export PATH=$PATH:/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin
export STAGING_DIR=/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3
export LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/lib

make
排错:
make: /opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin/arm-linux-strip:命令未找到
解决:
进入/opt/ac1200g/asuswrt/release/src-rt-9.x/src/toolchains/hndtools-arm-linux-2.6.36-uclibc-4.5.3/bin目录,输入:
ln -s arm-brcm-linux-uclibcgnueabi-strip arm-linux-strip

4、适配
由于ac9更改了init路径,还需修改init.c文件中的parse_inittab(void)函数,更改/etc为/etc_ro
搞定

初试spawn-fcgi

初试spawn-fcgi无评论

2017年5月2日 at 下午9:46分类:其他 阅读: 1,108 次

1、下载fcgi库源码
ftp://ftp.slackware.com/.2/gentoo/distfiles/fcgi-2.4.0.tar.gz

2、编译
./configure --host=arm-linux --prefix=/opt/lib/fcgi -enable-static --disable-shared
make出现错误:
fcgio.cpp error: 'EOF' was not declared in this scope
fcgio.cpp增加#define EOF (-1)
/home/yushi/fcgi-2.4.0/libfcgi/fcgiapp.c:615: undefined reference to `frexp'
先make clean,然后修改 libfcgi目录下的Makefile,在LIBS那一行增加 -lm 参数

3、编译测试程序
建立testcgi.c文件:
#include <fcgi_stdio.h>
#include
int main()
{
int count = 0;
while(FCGI_Accept() >= 0)
{
printf("Content-type: text/html\r\n"
"\r\n"
""
"FastCGI Hello!"
"Request number %d running on host%s "
"Process ID: %d\n",
++count,
getenv("SERVER_NAME"), getpid());
}
return 0;
}

编译:
arm-linux-gcc testcgi.c -o testcgi -I /opt/lib/fcgi/include/ /opt/lib/fcgi/lib/libfcgi.a -lm

4、测试验证
上传testcgi到路由器上,运行:
spawn-fcgi -a 127.0.0.1 -p 8288 /usr/bin/testcgi
修改nginx配置,增加:
location ~ \.cgi$ {
fastcgi_pass 127.0.0.1:8288;
fastcgi_index index.cgi;
include fastcgi.conf;
}
重启后访问:
http://192.168.0.1:8180/1.cgi
会在浏览器输出:

5、环境变量
GET请求,它将数据打包放置在环境变量QUERY_STRING中,CGI从环境变量QUERY_STRING中获取数据。常见的环境变量如下表所示:

环境变数 内容
AUTH_TYPE 存取认证类型。
CONTENT_LENGTH 由标准输入传递给CGI程序的数据长度,以bytes或字元数来计算。
CONTENT_TYPE 请求的MIME类型。
GATEWAY_INTERFACE 服务器的CGI版本编号。
HTTP_ACCEPT 浏览器能直接接收的Content-types, 可以有HTTP Accept header定义.
HTTP_USER_AGENT 递交表单的浏览器的名称、版本 和其他平台性的附加信息。
HTTP_REFERER 递交表单的文本的 URL,不是所有的浏览器都发出这个信息,不要依赖它
PATH_INFO 传递给cgi程式的路径信息。
QUERY_STRING 传递给CGI程式的请求参数,也就是用"?"隔开,添加在URL后面的字串。
REMOTE_ADDR client端的host名称。
REMOTE_HOST client端的IP位址。
REMOTE_USER client端送出来的使用者名称。
REMOTE_METHOD client端发出请求的方法(如get、post)。
SCRIPT_NAME CGI程式所在的虚拟路径,如/cgi-bin/echo。
SERVER_NAME server的host名称或IP地址。
SERVER_PORT 收到request的server端口。
SERVER_PROTOCOL 所使用的通讯协定和版本编号。
SERVER_SOFTWARE server程序的名称和版本。

环境变量的大小是有一定的限制的,当需要传送的数据量大时,储存环境变量的空间可能会不足,造成数据接收不完全,甚至无法执行CGI程序。因此后来又发展出另外一种方法:POST,也就是利用I/O重新导向的技巧,让CGI程序可以由STDIN和STDOUT直接跟浏览器沟通。
当我们指定用这种方法传递请求的数据时,web 服务器收到数据后会先放在一块输入缓冲区中,并且将数据的大小记录在CONTENT_LENGTH这个环境变数,然后调用CGI程式并将CGI程序的STDIN指向这块缓冲区,于是我们就可以很顺利的通过STDIN和环境变数CONTENT_LENGTH得到所有的资料,再没有资料大小的限制了。

6、乘法程序
修改testcgi.c
#include
#include
int main()
{
char *data;
long m,n;
while(FCGI_Accept() >= 0)
{
printf("Content-Type:text/html\n\n");
printf("乘法结果 ");
printf("

乘法结果

");
data = getenv("QUERY_STRING");
if(data == NULL)
printf("

错误!数据没有被输入或者数据传输有问题

");
else if(sscanf(data,"m=%ld&n=%ld",&m,&n)!=2)
printf("

错误!输入数据非法。表单中输入的必须是数字。

");
else
printf("

%ld和%ld的乘积是:%ld。

",m,n,m*n);
}
return 0;
}

执行结果