分散加载

合集下载

ARM中的RO、RW和ZI DATA说明

ARM中的RO、RW和ZI DATA说明
{
;
}
Prog2:
#include <stdio.h>
const char a = 5;
void main(void)
Prog3:
#include <stdio.h>
void main(void)
{
;
}
Prog4:
#include <stdio.h>
3; ZI
再看两个程序,他们之间的差别是一个未初始化的变量“a”,从之前的了解中,应该可以推测,这两个程序之间应该只有ZI大小有差别。
Prog3:
#include <stdio.h>
void main(void)
实际上,RO中的指令至少应该有这样的功能:
1. 将RW从ROM中搬到RAM中,因为RW是变量,变量不能存在ROM中。
2.
将ZI所在的RAM区域全部清零,因为ZI区域并不在Image中,所以需要程序根据编译器给出的ZI地址及大小来将相应得RAM区域清零。ZI中也是变量,同理:变量不能存在ROM中
Total RO Size(Code + RO Data) 1008 ( 0.98kB)
Total RW Size(RW Data + ZI Data) 97 ( 0.09kB)
Total ROM Size(Code + RO Data + RW Data) 1009 ( 0.99kB)
Code RO Data RW Data ZI Data Debug
948 60 1 96 0 Grand Totals
================================================================================

Keil分散文件加载

Keil分散文件加载

Keil分散文件加载分散加载能够将加载和运行时存储器中的代码和数据描述在被称为分散加载描述文件的一个文本描述文件中,以供连接时使用。

(1)分散加载区分散加载区域分为两类:? 加载区,包含应用程序复位和加载时的代码和数据。

? 执行区,包含应用程序执行时的代码和数据。

应用程序启动过程中,从每个加载区可创建一个或多个执行区。

映象中所有的代码和数据准确地分为一个加载区和一个执行区。

(2)分散加载文件示例ROM_LOAD 0x0000 0x4000{ROM_EXEC 0x0000 0x4000; Root region{* (+RO); All code and constant data}RAM 0x10000 0x8000{* (+RW, +ZI); All non-constant data}}(3)分散加载文件语法load_region_name start_address | "+"offset [attributes] [max_size]{execution_region_name start_address | "+"offset [attributes][max_size] {module_select_pattern ["("("+" input_section_attr | input_section_pattern)([","] "+" input_section_attr | "," input_section_pattern)) *")"]}}load_region:加载区,用来保存永久性数据(程序和只读变量)的区域;execution_region:执行区,程序执行时,从加载区域将数据复制到相应执行区后才能被正确执行;load_region_name:加载区域名,用于“Linker”区别不同的加载区域,最多31个字符;start_address:起始地址,指示区域的首地址;+offset:前一个加载区域尾地址+offset 做为当前的起始地址,且“offset”应为“0”或“4”的倍数;attributes:区域属性,可设置如下属性:PI 与地址无关方式存放;RELOC 重新部署,保留定位信息,以便重新定位该段到新的执行区;OVERLAY 覆盖,允许多个可执行区域在同一个地址,ADS不支持;ABSOLUTE 绝对地址(默认);max_size:该区域的大小;execution_region_name:执行区域名;start_address:该执行区的首地址,必须字对齐;+offset:同上;attributes:同上;PI 与地址无关,该区域的代码可任意移动后执行;OVERLAY 覆盖;ABSOLUTE 绝对地址(默认);FIXED 固定地址;UNINIT 不用初始化该区域的ZI段;module_select_pattern:目标文件滤波器,支持通配符“*”和“?”;*.o匹配所有目标,* (或“.ANY”)匹配所有目标文件和库。

scatter分析

scatter分析
; UNINIT:未初始化,ZI段将不会被初始化为0,仅仅保留了内存单元,而没有将各初始写入内存单元,或者将内存单元初始化为0
;执行域包含1个或多个输入段
;输入段书写格式:包括模块描述和段描述
;
;模块描述:指定包含模块的文件(包括目标文件.o和库文件.LIB)搜索范围,可以使用通配符*和?
问题分析:
分散加载描述文件是一个文本文件,它向链接器描述目标系统的存储器映射。如果通过命令行使用链接器,则描述文件的扩展名并不重要。分散加载文件指定:
1)每个加载区的加载地址和最大尺寸;
2)每个加载区的属性;
3)从每个加载区派生的执行区;
4)每个执行区的执行地址和最大尺寸;
5)每个执行区的输入节。
{
*(+RW) ; RW被紧接着放置,不能移动
}
ER_ZI +0 ; ER_ZI执行域在ER_RW执行域后面
{
*(+ZI) ; 所有ZI段被连续放置
}
}
在上面的例程中, 代码从地址0x00000000处开始存放,并且将RESET程序段放在最开始处, 而可读写的数据从地址0x30000000处开始存放. 分散加载文件的段的名字(例如LR_ROM1, ER_ROM1等)可以是任意的名字。
ADS分散加载文件语法:
;如果片外RAM起始地址不为0x8000 0000,则需要修改mem_.scf文件
{
* (+RW,+ZI)
}
HEAP +0 UNINIT ;+0表示接着上一段,UNINIT 表示不初始化
; OVERLAY:覆盖,允许加载域互相重叠,可以在相同地址上建立多个执行域,ADS不支持本属性

STM32 分散加载文件

STM32 分散加载文件

STM32 分散加载文件 IAP —MDK(2011-07-25 14:42:30); *************************************************************; *** Scatter-Loading Description File generated by uVision ***; *************************************************************LR_IROM1 0x08000000 0x00004000 ; load region size_region 第一个加载域,起始地址0x08000000,{ 大小0x00004000ER_IROM1 0x08000000 0x00004000 ; load address = execution address 第一个运行时域, { 起始0x08000000,大小0x00004000*.o (RESET, +First) IAP第一阶段还是在FLASH中运行*(InRoot$$Sections)startup_stm32f10x_md.o}ER_IROM2 0x20008000 0x00004000 ; load address = execution address第二个运行时域, { 起始0x20008000,大小0x00004000.ANY (+RO) IAP第二阶段加载到SDRAM中运行}RW_IRAM1 0x20000000 0x00008000 ; RW data 把可读写的数据和初始化为0的数据放在内存SDRAM的开头{.ANY (+RW +ZI)}}做个比喻:就像一列火车在起始地址0x08000000装上大小0x00004000的货物,然后把特定的货物送到指定的地方拿下来运行或者存放。

上面这辆火车就停了3个地方。

编译时出现一下警告:warning: L6314W: No section matches pattern address(RO).在Target中的Linker中有一栏Misc controls,键入--diag_suppress=L6314即可,如下--diag_suppress taglist禁用所有具有指定标签的诊断消息。

[M3_SN] ARM分散加载原理

[M3_SN] ARM分散加载原理

ARM分散加载原理摘要从ARM ELF目标文件主要构成出发,详细介绍了分散加载的基本原理、分散加载文件的语法、分散加载时连接器生成的预定义符号及要重新实现的函数等;以定位目标外设和定义超大型结构体数组两项应用案例加以说明,并给出完整的工程实例和Bootloader代码。

这些都已经在实际工程中多次应用和验证,是笔者实际工程的萃取。

关键词分散加载嵌入式系统Scatter Loading Bootloader ARM ELF引言在当今的嵌入式系统设计中,ARM处理器以价格便宜、功耗低、集成度高、外设资源丰富和易于使用的特点而得到广泛的应用;在速度和性能方面已达到或超过了部分PC104嵌入式计算机的性能,而成本却比相应的PC104计算机低很多,广泛应用于手机、GPS接收机、地图导航、路由器、以太网交换机及其他民用和工业电子设备。

在一个采用ARM处理器的实时嵌入式系统中,目标硬件常常由Flash、SRAM、SDRAM 和NVRAM(非易失性RAM)等存储器组成,并定位于不同的物理地址范围,那么,怎样通过软件更好地访问和利用这些不同的存储器并让系统高效地运行?分散加载(Scatter loading)就提供了这样一种机制。

它可以将内存变量定位于不同的物理地址上的存储器或端口,通过访问内存变量即可达到访问外部存储器或外设的目的;同时通过分撒加载,让大多数程序代码在高速的内部RAM中运行,从而使得系统的实时性大大增强。

1.ARM ELF目标文件的主要构成ARM ELF(Executable and Linking Format)目标文件主要由.Text段、.Data段、.BSS段构成,其他段如.debug段、.comment段等与本文关系部大,不作介绍。

.Text 段由可执行代码组成,段类型为Code,属性为RO;.Data 段由已初始化数据组成,段类型为Data, 属性为RO;.BSS 段由未初始化数据组成,段类型为Zero, 属性为RW,在应用个程序启动时对该段的数据初始化为零。

基于分散加载的ARM软件加密方案设计

基于分散加载的ARM软件加密方案设计

本 文 以 ARM Co r t e x—M3芯 片 为 例 , 设 计 并 实 现 了
引 言
ARM 芯 片作 为嵌 入 式 系 统 的 主 流 芯 片 , 已 经 广 泛 应 用 于手 机 、 路 由 器 及 其 他 工 业 和 民 用 电 子 设 备 】 。 在
ARM 处 理 器 得 到越 来 越 多 的 应 用 的 同 时 , 如 何 防 止 黑 客

基 于 分 散 加 载 的 ARM 软 件 加 密 方 案 设 计
关峰 , 谢 晓 明
( 北 京 化 工 大 学 信 息 科学 与技 摘 要 :针 对 黑客 非 法 获取 ARM 芯 片 程 序 的 问题 , 提 出 了一 种 能 同 时 防 止 ARM 芯 片 程 序 被 非 法 复 制 及 源 码 窃 取 的 方
法 。该 方 法 以 分散 加 栽 方 式规 划存 储 器 , 以 ARM 芯 片 内全 球 唯 一 序 列 号 为 密钥 , 对 核 心 程 序 进 行 加 密 处 理 。在 程 序 运
行 时, 芯 片 内的唯 一序 列 号使 非 法拷 贝的 程 序在 同类 型 ARM 设 备 上 无 法运 行 ; 对核 心程序 进行加 密后存储 , 确 保 源 代

种 基 于 分 散 加 载 的 ARM 芯 片 加 密 方 案 , 此 方 案 将 经 过
加 密 的程 序 烧 写 到 存 储 器 上 , 从 而 完 全 打乱 程 序 在 存 储 器 上 的正 常 顺 序 , 使 其 反 汇 编 完 全 不 可 读 。以 A R M 芯片 内 全 球 唯 一 的序 列 号 为 密 钥 , 确 保 被 整 片 复制 的 程 序 无 法 正
Ab s t r ac t :A c c o r d i n g t O t he p r o bl e m t ha t h a c ke r s s t e a l A RM p r o gr a m c od e b y i l l e ga l me t ho ds ,a me t ho d t h a t c a n s i m ul t a ne o us l y pr e ve n t A RM pr o gr a m c o de be i ng i l l e ga l l y c op i e d a nd c r a c ke d i s pr op os e d. The me t ho d p l a n s me m or y uni t b a s e d o n s c a t t e r l o a di ng a n d e nc r yp t s

MTK Scatter文件学习

MTK Scatter文件学习

MTK Scatter文件学习概述:分散加载(scatter loading)是ARM 连接接器提供的一个机制,该机制可以把一个可执行映像文件(即Bin文件)分割放置到内存中不同的独立段。

映像(Image)文件有两个视图:加载视图(Load view) 和执行视图(execution view)。

在下载的时候Image regions被放置在memory map当中,而在执行Image前,或许你需要将一些regions放置在它们执行时的地址上,并建立起ZI regions。

例如,你初始化的RW数据需要从它在下载时的在ROM中的地址处移动到执行时RAM的地址处。

在scatter 文件中可以为每一个代码或数据段在装载和执行时指定不同的存储区域地址,Scatlertoading的存储区块可以分成二种类型:装载区:当系统启动或加载时应用程序的存放区。

执行区:系统启动后,应用程序进行执行和数据访问的存储器区域,系统在实时运行时可以有一个或多个执行区。

映像中所有的代码和数据都有一个装载地址和运行地址(二者可能相同也可能不同,视具体情况而定)。

在系统启动时,C函数库中的__main初始化代码会执行必要的复制及清零操作,使应用程序的相应代码和数据段从装载状态转入执行状态。

为什么需要Scatter文件:制定存储器映射(memory map)的方法基本上有二种,一是在link时使用命令行选项,并在程序执行前利用linker pre-define symbol使用汇编语言制定section的段初始化,二是使用scatter file,即采用“分散加载机制”。

以上二种方法依应用程序的复杂度而定,一针对简单的情况,二针对复杂的情况。

手机属于复杂的情况,必须使用scatter file。

Scatter文件语法:scatter文件是一个简单的文本文件,包含一些简单的语法(分号后面的内容是注释):My_Region 0x0000 0x1000 ;区域名称区起始地址区长度{the context of region ;区内容}每个区由一个头标题开始定义,头中至少包含区的名字和起始地址,另外还有最大长度和其他一些属性选项。

试图搞懂MDK程序下载到flash(二)--分散加载文件scatter

试图搞懂MDK程序下载到flash(二)--分散加载文件scatter

试图搞懂MDK程序下载到flash(二)--分散加载文件scatter分散加载文件概念对于分散加载文件的概念,在《ARM体系结构与编程》书第11章有明确介绍。

分散加载文件(即scatter file,后缀为 .scf)是一个文本文件,通过编写一个分散加载文件来指定ARM连接器在生成映像文件时如何分配RO、RW、ZI等数据的存放地址。

如果不用SCATTER文件指定,那么ARM连接器会按照默认的方式来生成映像文件,一般情况下我们是不需要使用分散加载文件的。

但在某些场合,我们希望把某些数据放在知道那个的地址处,那么这时候SCATTER文件就发挥了非常大的作用,而且SCATTER文件用起来非常简单好用。

我越看这个分散加载文件越感觉它的作用和uboot的连接脚本lds 一样。

分散加载文件的格式分散加载描述文件是一个文本文件,它向链接器描述目标系统的存储器映射。

如果通过命令行使用链接器,则描述文件的扩展名并不重要。

分散加载文件指定:①每个加载区的加载地址和最大尺寸;②每个加载区的属性;③从每个加载区派生的执行区;④每个执行区的执行地址和最大尺寸;⑤每个执行区的输入节。

从描述文件的格式就可以看出加载区、执行区和输入节的层次关系。

分散加载文件基本点①编译后输出的映像文件中各段是首尾相连的,中间没有空闲的区域,他们的先后关系是根据链接时参数的先后次序决定的armlinker -file1.o file2.o ...② scatter用于将编译后的映像文件中的特定段加载到多个分散的指定内存区域③有两类域(region):执行域(execution region,一般是ram区域)和加载域(load region,一般是rom区域)④加载域:就是编译之后得到的二进制文件烧写到rom中的这一段区域,所有的代码R0、预定义变量RW、堆栈之类和清不清空无关紧要的大片内存区域ZI,都包括在其中。

⑤执行域:就是把加载域进行“解压缩”后的样子。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

ARM处理器的分散加载及特殊应用研究摘要从ARM ELF目标文件主要构成出发,详细介绍了分散加载的基本原理、分散加载文件的语法、分散加载时连接器生成的预定义符号及要重新实现的函数等;以定位目标外设和定义超大型结构体数组两项应用来加以说明,并给出完整的工程实例和Bootloader代码。

这些都已经在实际工程中多次应用和验证,是笔者实际工程项目的萃取。

关键词分散加载嵌入式系统 Scatter Loading Bootloader ARM ELF引言在当今的嵌入式系统设计中,ARM处理器以价格便宜、功耗低、集成度高、外设资源丰富和易于使用的特点而得到广泛的应用;在速度和性能方面已达到或超过部分PC104嵌入式计算机的性能,而成本却比相应的PC104计算机低很多,广泛应用于手机、GPS接收机、地图导航、路由器、以太网交换机及其他民用和工业电子设备。

在一个采用ARM处理器的实时嵌入式系统中,目标硬件常常由Flash、SRAM、SDRAM和NVRAM(非易失性RAM)等存储器组成,并定位于不同的物理地址范围,那么,怎样通过软件更好地访问和利用这些不同的存储器并让系统高效地运行?分散加载(scatter loading)就提供了这样一种机制。

它可以将内存变量定位于不同的物理地址上的存储器或端口,通过访问内存变量即可达到访问外部存储器或外设的目的;同时通过分散加载,让大多数程序代码在高速的内部RAM中运行,从而使得系统的实时性大大增强。

1 ARM ELF目标文件的主要构成ARM ELF(Executable and Linking Format)目标文件主要由.Text段、.Data 段、.BSS段构成,其他段如.debug段、.comment段等与本文关系不大,不作介绍。

.Text段由可执行代码组成,段类型为Code,属性为RO;.Data段由已初始化数据组成,段类型为Data,属性为RO;.BSS段由未初始化数据组成,段类型为Zero,属性为RW,在应用程序启动时对该段的数据初始化为零。

如果在分散加载文件中指定了UNINIT属性,则在应用程序启动时不初始化该段。

2 分散加载的基本原理假设一个采用ARM处理器的实时嵌入式系统目标硬件的存储器由ROM存储器和RAM存储器组成。

当一个嵌入式系统在仿真环境下调试完毕,需要脱机运行的时候,就需要将源程序编译连接成可执行目标代码并烧写到ROM存储器中。

由于ROM存储器存取数据的速率比RAM存储器慢,因此,让程序在ROM存储器中运行。

CPU每次取指令和取数据操作都要访问ROM存储器,这样需要在CPU的总线周期中插入等待周期,通过降低总线的速率来满足访问慢速的ROM存储器,这样势必会降低CPU的运行速率和效率,因此,分散加载就显得非常必要。

ARM的连接器提供了一种分散加载机制,在连接时可以根据分散加载文件(.scf文件)中指定的存储器分配方案,将可执行镜像文件分成指定的分区并定位于指定的存储器物理地址。

这样,当嵌入式系统在复位或重新上电时,在对CPU相应寄存器进行初始化后,首先执行ROM存储器的Bootloader(自举) 代码,根据连接时的存储器分配方案,将相应代码和数据由加载地址拷贝到运行地址,这样,定位在RAM存储器的代码和数据就在RAM存储器中运行,而不再从 ROM 存储器中取数据或取指令,从而大大提高了CPU的运行速率和效率。

分散加载的基本原理如图1所示。

3 分散加载文件语法在一个实时嵌入式系统中,分散加载文件是对目标硬件中的多个存储器块的分块描述,它直接对应目标硬件存储器的起始地址和范围。

同时,它在应用程序连接时用于告诉连接器用户程序代码和数据的加载地址和运行地址,在连接时由连接器产生相应的加载地址和运行地址符号,包括代码和数据的加载起始地址、运行地址和长度等。

这些符号用于上电后执行启动代码的数据拷贝工作,启动代码根据这些符号,将指定代码和数据由ROM中的加载地址拷贝到RAM中的运行地址中,从而实现代码在高速RAM存储器中的脱机运行。

其语法格式如下:注意:①每一个分散加载文件必须至少包含一个根区,每个根区的加载地址等于执行地址。

②每一个引导区必须至少包含一个执行区,每一个执行区必须至少包含一个代码段或数据段;一个引导区可以包含几个执行区,每一个执行区只能属于一个引导区。

4 分散加载时连接器生成的预定义符号在编译连接时如果指定了分散加载文件(.scf文件),在连接后会自动生成如下变量:5 重新实现_user_initial_stEickheap()函数分散加载机制提供了一种指定代码和静态数据布局的方法。

使用分散加载时,必须重新放置堆栈和堆。

应用程序的堆栈(stack)和堆(heap)是在C库函数初始化过程中建立起来的,在ADSl.2或更新版本中,在缺省状态下C库函数初始化代码会将连接器生成的符号Image$$ZI$$Limit地址作为堆的基地址。

在分散加载时,连接器会将用户的 __user_initidl_stackheap()函数代替C库函数默认的堆栈和堆初始化函数,并将其连接到用户的镜像文件中,用户可通过重新实现__user_initial_stackheap()函数来改变堆栈和堆的位置,从而适合自己的目标硬件。

__user_initial_stackheap()可以用C或汇编语言来实现。

它必须返回如下参数:r0—堆基地址;r1—堆栈基地址;r2—堆长度限制值(需要的话);r3—堆栈长度限制值(需要的话)。

当用户使用分散加载功能的时候,必须重新实现一user_initial_staacklaeap(),否则连接器会报错:Error:L6218E:Undefined symbol Imager$$ZI$$一Limit(referred from sys_stackheap.o)。

注:Image$$ZI$$Limit变量为零初始化段(ZI段)的末地址。

未使用分散加载时,堆默认就定位在ZI段的末地址,如图2所示。

__user_initial_stackheap()函数的实现有两种方法。

(1)共用一个存储区汇编语言如下:这种方式定义的堆栈和堆共用一个存储区,采用相向的增长方向,如图3所示。

(2)使用两个存储区汇编语言如下:这种方式定义的堆栈和堆分别采用两个不同存储区。

堆栈采用向下增长,从地址Ox40000到地址Ox20000;堆采用向上增长,从地址0x28000000到地址0x28080000,如图4所示。

6 特殊应用6. 1 定位目标外设使用分散加载,可以将用户定义的结构体或代码定位到指定物理地址上的外设,这种外设可以是定时器、实时时钟、静态SRAM或者是两个处理器间用于数据和指令通信的双端口存储器等。

在程序中不必直接访问相应外设,只需访问相应的内存变量即可实现对指定外设的操作,因为相应的内存变量定位在指定的外设上。

这样,对外设的访问看不到相应的指针操作,对结构体成员的访问即可实现对外设相应存储单元的访问,让程序员感觉到仿佛没有外设,只有内存。

例如,一个带有两个32位寄存器的定时器外设,在系统中的物理地址为Ox04000000,其C语言结构描述如下:要使用分散加载将上述结构体定位到Ox04000000的物理地址,可以将上述结构体放在一个文件名为timer_regs.c中,并在分散加载文件中指定即可,如下:属性UNINIT是避免在应用程序启动时对该执行段的ZI数据段初始化为零。

在程序连接后,通过Image map文件可查看该ZI数据段的存储器分配情况: Execution Region TIMER(Base:Ox04000000,Size:0x00000008,Max:0xffffffff,ABSOLUTE,UNINIT)Base Addr Size Type Attr Idx E Section Name 0bi ectOx04000000 0x00000008 Zero RW 32.bss tlmer_regs.o从Image map 文件可以看出,该TIMER执行区定位在物理地址0x04000000,即结构体timer_regs定位在Ox04000000,因此,在程序中对结构体的操作即是对定时器的操作。

6.2 定义超大型结构体数组分散加载机制在提供将指定代码和数据定位在指定物理地址的能力的同时,也提供了一种代码分割机制——可以将指定的零初始化段(ZI段)从可执行代码中分离出来。

这样最终生成的烧入ROM或Flash中的镜像文件就不包括那部分分割了的零初始化段,即使该零初始化段再大,也不影响最终生成的镜像文件的大小。

但不采用分散加载机制,零初始化段在编译连接后是直接生成到镜像文件中的。

它的大小直接影响最终要烧写的文件的大小,且零初始化段的大小还取决于内存的大小,它不能大到超过内存的大小;而采用分散加载机制,可以将某个零初始化段定位到非内存地址的一个存储器外设上,如NVRAM(非易失性随机存储器)。

笔者曾在一个实际工程中采用这种分散加载机制,将一个2MB的结构体数组定位到外部NVRAM中,用于记录设备在工作过程中采集到的数据;而在本系统中,ARM处理器的内存只有256 KB,Flash存储器也只有2 MB。

如果不采用分散加载,程序根本无法运行,也不能烧写到Flash中。

采用分散加载,把对复杂外设的访问变成对结构体数组的访问,使程序代码精简易懂。

对程序员来说,对结构体数组的操作还是和内存变量的操作一样的。

编者注:本文为期刊缩略版,全文见本刊网站www.mesnet.com.cn。

结语分散加载是嵌入式系统应用中不可或缺的一种加载方式,ARM、DSP、PowerPC 和MIPS等嵌入式处理器,都离不开分散加载。

这种分散加载的思想是通用的,只是不同处理器的实现方式不同。

本文详细阐述了基于ARM处理器的分散加载方法及其特殊应用,并以实际工程为例来说明怎样实现分散加载及使用分散加载的好处。

它是笔者在实际工程应用中的心得体会,同时也是笔者工作经验的总结,希望本文对从事嵌入式系统设计和应用的工程技术人员能有所帮助。

相关文档
最新文档