Chinaunix首页 | 论坛 | 博客
  • 博客访问: 357487
  • 博文数量: 127
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 333
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-27 14:44
个人简介

兴趣是最好的学习契机!

文章分类

全部博文(127)

文章存档

2017年(1)

2016年(3)

2015年(54)

2014年(58)

2013年(11)

我的朋友

分类: 嵌入式

2015-08-11 13:38:40

SOC FPGA客户在采用bm模式或简单实时操作系统时,有时会需要修改工程代码链接的起始地址和地址空间长度。或者在链接脚本中预留栈空间和定义全局变量等。本文以骏龙公司提供的baremetal 模式代码工程的脚本为例,介绍一下链接脚本的基本知识,希望起到抛砖引玉的作用。
1,初识链接脚本
下面的就是骏龙公司提供的baremetal工程的链接脚本。
MEMORY { .sram : ORIGIN = 0x01000000, LENGTH = (16 * 1024 * 1024) }
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
.text :
{
start.o (.text)
*(.text*)
} >.sram
. = ALIGN(4);
.rodata : { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) } >.sram
. = ALIGN(4);
.data : { *(SORT_BY_ALIGNMENT(.data*)) } >.sram
. = ALIGN(4);
.bss : {
. = ALIGN(4);
__bss_start = .;
*(.bss*)
. = ALIGN(4);
__bss_end__ = .;
} >.sram
}
在介绍脚本基本语法之前,先介绍一下上面脚本各个部分的功能,以便给大家一个整体的认识,避免陷入具体的语法细节。
1.1 代码运行空间
在脚本中以MEMORY关键字定义存储空间,其中以ORIGIN定义地址空间的起始地址,LENGTH定义地址空间的长度。如下下面的语句定义了该工程运行的起始地址为0x01000000,地址空间的长度为16MB。这部分内容是大家最经常修改的内容。
MEMORY { .sram : ORIGIN = 0x01000000, LENGTH = (16 * 1024 * 1024) }
1.2 入口地址
定义了本工程代码运行时的入口函数。在本例中入口函数为“_start”,即汇编
代码start.s中的入口函数。
ENTRY(_start)
需要注意如下备注:
备注1,链接过程中,需要首先链接start.o文件,以保证_start 函数位于程序地址空间的最前端。后面会有说明。
备注2,_start 函数需要位于start.s最前端。_start通常是上电复位函数。如下图所示
1.3 程序段链接
SECTIONS
{
.text :
{
start.o (.text)
*(.text*)
} >.sram
在上面的语句的start.o (.text)指示将编译的工程文件中的start.o的.text段(即代码段)链接到MEMORY定义的sram中。
*(.text*)指示将工程中所有目标文件的.text段链接到sram中。在链接*(.text*) 时,不会重复链接start.o的.text段。
另外,此处的链接过程是按照顺序进行链接的,即先链接start.o,再链接其他的目标文件。这样可以保证start.o被链接到运行地址空间的最前端。
2,链接脚本基本语法介绍
2.1 内存区域定义命令
在默认情形下,连接器可以为section 分配任意位置的存储区域。你也可以用
MEMORY 命令定义存储区域,并通过输出section 描述的> REGION 属性显
示地将该输出section 限定于某块存储区域,当存储区域大小不能满足要求时,
连接器会报告该错误。
MEMORY 命令的文法如下,
MEMORY {
NAME1 [(ATTR)] : ORIGIN = ORIGIN1, LENGTH = LEN2
NAME2 [(ATTR)] : ORIGIN = ORIGIN2, LENGTH = LEN2

}
NAME :存储区域的名字,这个名字可以与符号名、文件名、section 名重复,
因为它处于一个独立的名字空间。
ATTR :定义该存储区域的属性,ATTR 属性内可以出现以下7 个字符,
R 只读section
W 读/写section
X 可执行section
A ?可分配的?section
I 初始化了的section
L 同I
! 不满足该字符之后的任何一个属性的section
ORIGIN :关键字,区域的开始地址,可简写成org 或o
LENGTH :关键字,区域的大小,可简写成len 或l
例子1,
MEMORY
{
rom (rx) : ORIGIN = 0, LENGTH = 256K
ram (!rx) : org = 0×40000000, l = 4M
}
例子 2
MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) } MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }MEMORY { .sram : ORIGIN = 0x00200000, LENGTH (32 * 1024 1024) }
OUTPUT_ARCH(arm) OUTPUT_ARCH(arm) OUTPUT_ARCH(arm)OUTPUT_ARCH(arm) OUTPUT_ARCH(arm) OUTPUT_ARCH(arm) OUTPUT_ARCH(arm)OUTPUT_ARCH(arm) OUTPUT_ARCH(arm)
ENTRY(start) ENTRY(start)ENTRY(start)ENTRY(start)ENTRY(start)ENTRY(start)ENTRY(start) ENTRY(start)
SECTIONSSECTIONSSECTIONS
{
.text.text.text.text :
{
BSP/start.o (.text) BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text) BSP/start.o (.text) BSP/start.o (.text) BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text) BSP/start.o (.text)
*(.text) *(.text)*(.text)*(.text)*(.text) *(.text)
}>.sram}>.sram }>.sram}>.sram
. = ALIGN(4); . = ALIGN(4); . = ALIGN(4);. = ALIGN(4); . = ALIGN(4);. = ALIGN(4);. = ALIGN(4);
.rodata : { .rodata : {.rodata : { .rodata : {.rodata : { .rodata : {.rodata : { *(.rodata*) }>.sram *(.rodata*) }>.sram*(.rodata*) }>.sram*(.rodata*) }>.sram*(.rodata*) }>.sram *(.rodata*) }>.sram*(.rodata*) }>.sram *(.rodata*) }>.sram *(.rodata*) }>.sram *(.rodata*) }>.sram*(.rodata*) }>.sram*(.rodata*) }>.sram
. = ALIGN(4); . = ALIGN(4); . = ALIGN(4);. = ALIGN(4); . = ALIGN(4);. = ALIGN(4);. = ALIGN(4);
.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram .data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram
. = ALIGN(4); . = ALIGN(4); . = ALIGN(4);. = ALIGN(4); . = ALIGN(4);. = ALIGN(4);. = ALIGN(4);
.bss : { .bss : { .bss : { .bss : { .bss : { .bss : {
bss_start = .; bss_start = .;bss_start = .; bss_start = .;bss_start = .; bss_start = .; bss_start = .; bss_start = .;
*(.bss*) *(.bss*) *(.bss*) *(.bss*) *(.bss*) *(.bss*) *(.bss*)
. = ALIGN(4); . = ALIGN(4); . = ALIGN(4);. = ALIGN(4); . = ALIGN(4);. = ALIGN(4);. = ALIGN(4);
bss_end = .; bss_end = .;bss_end = .; bss_end = .; bss_end = .;bss_end = .; bss_end = .;
}>.sram}>.sram }>.sram}>.sram
}
2.2 脚本中设置入口符号
ENTRY(SYMBOL) : 将符号SYMBOL 的值设置成入口地址。
会将SYMBOL处指令连接到下面的ORIGIN指定的地址。
MEMORY { .sram : ORIGIN = 0x01000000, LENGTH = (16 * 1024 * 1024) }
ld 有多种方法设置进程入口地址, 按一下顺序: (编号越前, 优先级越高)
1, ld 命令行的-e 选项
2, 连接脚本的ENTRY(SYMBOL)命令
3, 如果定义了start 符号, 使用start 符号值
4, 如果存在.text section, 使用.text section 的第一字节的位置值
5, 使用值0
2.3定位器符号的使用
在脚本中经常看到类似“. = 0×10000;”的语句,该语句表示将当前地址设置为0x10000。在链接开始处没有定位器符号,则表示将输入段定位到MEMORY定义的空间最开始处,即offset为0。
以下脚本将输出文件的text section 定位在0×10000, data section 定位在0×8000000:
SECTIONS
{
. = 0×10000;
.text : { *(.text) }
. = 0×8000000;
.data : { *(.data) }
.bss : {
. = ALIGN(4);
__bss_start = .;
*(.bss*)
. = ALIGN(4);
__bss_end__ = .;
} >.sram}
2.4. SECTIONS 命令
SECTIONS 命令告诉ld 如何把输入文件的sections 映射到输出文件的各个
section: 如何将输入section 合为输出section; 如何把输出section 放入程
序地址空间
SECTIONS
{
SECTIONS-COMMAND
SECTIONS-COMMAND

}
2.4.1 SECTION-COMMAND 有四种:
(1) ENTRY 命令
(2) 符号赋值语句
(3) 一个输出section 的描述(output section description)
(4) 一个section 叠加描述(overlay description)
2.4.2 输出section 描述
.text : { *(.text) }>.sram
SECTION [ADDRESS] [(TYPE)] : [AT(LMA)]
{
OUTPUT-SECTION-COMMAND
OUTPUT-SECTION-COMMAND

} [>REGION] [AT>LMA_REGION] [:PHDR HDR ...] [=FILLEXP]
2.4.2.1 SECTION:
section 名字
SECTION 左右的空白、圆括号、冒号是必须的,换行符和其他空格是可选的。
2.4.2.2 OUTPUT-SECTION-COMMAND
每个OUTPUT-SECTION-COMMAND 为以下四种之一,
符号赋值语句
一个输入section 描述 //可以看到,输入section描述是输出的一部分
直接包含的数据值
一个特殊的输出section 关键字
2.4.2.3 ADDRESS
输出section 地址(ADDRESS):
ADDRESS是一个表达式,它的值用于设置VMA。如果没有该选项且有REGION
选项,那么连接器将根据REGION 设置VMA;如果也没有 REGION 选项,那
么连接器将根据定位符号?.?的值设置该section 的VMA,将定位符号的值调整
到满足输出section 对齐要求后的值,输出 section 的对齐要求为:该输出
section 描述内用到的所有输入section 的对齐要求中最严格的。
.text . : { *(.text) }

.text : { *(.text) }
这两个描述是截然不同的,第一个将.text section 的VMA 设置为定位符号的
值,而第二个则是设置成定位符号的修调值,满足对齐要求后的。
ADDRESS 可以是一个任意表达式,比如ALIGN(0×10)这将把该section 的
VMA 设置成定位符号的修调值,满足16 字节对齐后的。
注意:设置ADDRESS 值,将更改定位符号的值。
另外,可以直接修改的值如 另外,可以直接修改的值如 另外,可以直接修改的值如 另外,可以直接修改的值如 另外,可以直接修改的值如 另外,可以直接修改的值如 . = 0×10000;
2.4.2.4 TYPE
TYPE :每个输出section 都有一个类型,如果没有指定TYPE 类型,那么连接
器根据输出section 引用的输入section 的类型设置该输出section 的类型。
它可以为以下五种值,
NOLOAD :该section 在程序运行时,不被载入内存。
DSECT,COPY,INFO,OVERLAY :这些类型很少被使用,为了向后兼容才被保
留下来。这种类型的section 必须被标记为“不可加载的”,以便在程序运行不为
它们分配内存。
2.4.2.4 LMA,指示存放在ROM中代码或数据的地址(如果VMA和LMA不同的话),使用时需要根据该地址加载到ram中
输出section 的LMA :默认情况下,LMA 等于VMA,但可以通过关键字AT()
指定LMA。
用关键字AT()指定,括号内包含表达式,表达式的值用于设置LMA。如果不用
AT()关键字,那么可用AT>LMA_REGION 表达式设置指定该section 加载地
址的范围。
这个属性主要用于构件ROM 境象。
例子,
SECTIONS
{
.text 0×1000 : { *(.text) _etext = . ; } //代码在rom中运行
.mdata 0×2000 :
AT ( ADDR (.text) + SIZEOF (.text) ) //表示数据存在rom中
{ _data = . ; *(.data); _edata = . ; }//__data 指示0x2000,即ram地址
.bss 0×3000 :
{ _bstart = . ; *(.bss) *(COMMON) ; _bend = . ;}
}
程序如下,
extern char _etext, _data, _edata, _bstart, _bend;
char *src = &_etext;//指示rom中text后的地址,即rom中数据开始地址
char *dst = &_data; //指示数据在RAM中的地址
/* ROM has data at end of text; copy it. */
while (dst < &_edata) {
*dst++ = *src++;
}
/* Zero bss */
for (dst = &_bstart; dst< &_bend; dst++)
*dst = 0;
此程序将处于ROM 内的已初始化数据拷贝到该数据应在的位置(VMA 地址),
并将为初始化数据置零。
读者应该认真的自己分析以上连接脚本和程序的作用。
输出section 区域:可以将输出section 放入预先定义的内存区域内,例子,
MEMORY { rom : ORIGIN = 0×1000, LENGTH = 0×1000 }
SECTIONS { ROM : { *(.text) } >rom }
2.4.2.5
输出section 区域:可以将输出section 放入预先定义的内存区域内,例子,
MEMORY { rom : ORIGIN = 0×1000, LENGTH = 0×1000 }
SECTIONS { ROM : { *(.text) } >rom }
2.4.3 输入section描述
在输出描述.text : { *(.text) }中的*(.text)是输入描述,此处指所有输入文件的.text section。
基本语法:FILENAME([EXCLUDE_FILE (FILENAME1 FILENAME2 ...)
SECTION1 SECTION2 ...)
FILENAME 文件名,可以是一个特定的文件的名字,也可以是一个字符串模式。
SECTION 名字,可以是一个特定的section 名字,也可以是一个字符串模式
例子是最能说明问题的,
*(.text) :表示所有输入文件的.text section
(*(EXCLUDE_FILE (*crtend.o *otherfile.o) .ctors)) :表示除crtend.o、
otherfile.o 文件外的所有输入文件的.ctors section。
data.o(.data) :表示data.o 文件的.data section
data.o :表示data.o 文件的所有section
*(.text .data) :表示所有文件的.text section 和.data section,顺序是:
第一个文件的.text section,第一个文件的.data section,第二个文件的.text
section,第二个文件的.data section,...
*(.text) *(.data) :表示所有文件的.text section 和.data section,顺序是:
第一个文件的.text section,第二个文件的.text section,...,最后一个文件
的.text section,第一个文件的.data section,第二个文件的.data
section,...,最后一个文件的.data section
SECTIONSSECTIONSSECTIONS
{
.text.text.text.text :
{
BSP/start.o (.text) BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text) BSP/start.o (.text) BSP/start.o (.text) BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text)BSP/start.o (.text) BSP/start.o (.text)
*(.text) *(.text)*(.text)*(.text)*(.text) *(.text)
}>.sram}>.sram }>.sram}>.sram
. = ALIGN(4); . = ALIGN(4); . = ALIGN(4);. = ALIGN(4); . = ALIGN(4);. = ALIGN(4);. = ALIGN(4);
.rodata : { *(.rodata*) }>.sram .rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram .rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram .rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram .rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram .rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram .rodata : { *(.rodata*) }>.sram .rodata : { *(.rodata*) }>.sram .rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram.rodata : { *(.rodata*) }>.sram
. = ALIGN(4); . = ALIGN(4); . = ALIGN(4);. = ALIGN(4); . = ALIGN(4);. = ALIGN(4);. = ALIGN(4);
.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram.data : { *(.data*) }>.sram .data : { *(.data*) }>.sram .data : { *(.data*) }>.sram .data : { *(.data*) }>.sram.data : { *(.data*) }>.sram
. = ALIGN(4); . = ALIGN(4); . = ALIGN(4);. = ALIGN(4); . = ALIGN(4);. = ALIGN(4);. = ALIGN(4);
.bss : { .bss : { .bss : { .bss : { .bss : { .bss : {
bss_start = .; bss_start = .;bss_start = .; bss_start = .;bss_start = .; bss_start = .; bss_start = .; bss_start = .;
*(.bss*) *(.bss*) *(.bss*) *(.bss*) *(.bss*) *(.bss*) *(.bss*)
. = ALIGN(4); . = ALIGN(4); . = ALIGN(4);. = ALIGN(4); . = ALIGN(4);. = ALIGN(4);. = ALIGN(4);
bss_end = .; bss_end = .;bss_end = .; bss_end = .; bss_end = .;bss_end = .; bss_end = .;
}>.sram}>.sram }>.sram}>.sram
}
字符串模式内可存在以下通配符:
* :表示任意多个字符
? :表示任意一个字符
[CHARS] :表示任意一个CHARS 内的字符,可用-号表示范围,如:a-z
:表示引用下一个紧跟的字符
在文件名内,通配符不匹配文件夹分隔符/,但当字符串模式仅包含通配符*时除
外。
注意:
任何一个文件的任意section 只能在SECTIONS 命令内出现一次。看如下例子,
SECTIONS {
.data : { *(.data) }
.data1 : { data.o(.data) }
}
data.o 文件的.data section 在第一个OUTPUT-SECTION-COMMAND 命

内被使用了,那么在第二个OUTPUT-SECTION-COMMAND 命令内将不会再
被使用,也就是说即使连接器不报错,输出文件的.data1 section 的内容也是
空的。
再次强调:连接器依次扫描每个OUTPUT-SECTION-COMMAND 命令内的文
件名,任何一个文件的任何一个section 都只能使用一次。
2.4 PROVIDE 关键字
该关键字用于定义这类符号:在目标文件内被引用,但没有在任何目标文件内被
定义的符号。
例子:
SECTIONS
{
.text :
{
*(.text)
_etext = .;
PROVIDE(etext = .);
}
}
当目标文件内引用了etext 符号,确没有定义它时,etext 符号对应的地址被定
义为.text section 之后的第一个字节的地址。
2.5. PHDRS 命令
————
该命令仅在产生ELF 目标文件时有效。
ELF 目标文件格式用program headers 程序头(程序头内包含一个或多个
segment 程序段描述)来描述程序如何被载入内存。可以用objdump -p 命令
查看。
SOCSOC 用不到 用不到 该命令 该命令 ,需要自己加头信息。 ,需要自己加头信息。 ,需要自己加头信息。 ,需要自己加头信息。 ,需要自己加头信息。
阅读(1979) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~