为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

保护模式指南

2011-11-17 48页 pdf 489KB 52阅读

用户头像

is_747246

暂无简介

举报
保护模式指南 保护模式指南 v 0.02 author:Till Gerken 译 者:ZivWang ...
保护模式指南
保护模式指南 v 0.02 author:Till Gerken 译 者:ZivWang November 21, 2004 目录 零 章 译者前言和修订 1 译者前言 2 修订记录 第一章 简 介 第二章 开 始 第三章 表格、描述符、选择子和诸多令人困惑的表格 第四章 有趣的资料 第五章 例程缺少的部分 第六章 保护模式下的一点编程技巧 第七章 错误,陷阱和异常 第八章 有用的中断服务 第九章 虚拟 DMA 第十章 虚拟控制程序接口(VCPI) 空 [翻译很糟糕,因为我对其一无所知] 第十一章 DOS 保护模式接口(DPMI) 第十二章 另外的资料 第十三章 鸣谢和想法 第十四章 译者最后的话 零章 译者前言和修订记录 1 译者前言 只有在保护模式下,现在的计算机才能发挥出全部能力。但是关于保护模式中文资料 屈指可数。一本是清华大学出版社出版的周明德主编的《保护方式下的 80386 及其编程》, 我没有读过;另一本也是清华大学出版社出版的,扬季文主编的《80X86 汇编语言程序 教程》,也就是常提起的“黑皮”,这本书我看过。保护模式讲的很多,但是上面的例子有 错误,费很大力气才调试通过 3 个程序。此外,还有李彦昌的电子书《80x86 保护模式系列 教程》。这份电子文档上面说是参考了上面提到的两本书,尤其是和《8》这本书,内容非常 非常相似,例子上的错误好像也是参考的结果。 我想以后应当写一份关于这个电子文档的考证。在这个想法之后,看到了 Till Gerken 撰写的“Protected Mode Tutorial v0.02”,遂打算先翻译这篇文章先。 文章不算长,翻译大约用了 2 周时间。 阅读中如果你有任何问题,请在 www.aogosoft.com 的论坛上留言提问。 文章中“[ ]”中的内容是我加上的,并不在原文中。 文章中的程序经过我的修改,保证都能正常编译运行。编译环境是 MASM6.11,在 VMWARE 4.5.2 B8848 FREE DOS BETA 9 中运行结果正确。 2 修订记录 版 本 修订原因 修订内容 0.02 从原文翻译 暂时空白页 第一章 简 介 这是一篇的为保护模式初学者编写的教程,我尽量使其简短并尽可能做到循序渐进。 本文介绍了编写保护模式程序所需的一切,诸如环境的要求,这也是为没有任何经验的人 准备的。你所要做的只是用你头脑(我想你一定有,是吧?)理解,同时还需要你有一些 汇编语言知识。 你所需要的全部信息都在这份文档中,但是我不会对任何由于使用这份文档提供的资料 而导致的任何直接的或者间接的损害负责。 如果你将本文所示用于商业用途,请从所在地给我邮寄一张明信片(当然,如果你只是 阅读本文也可以这样,但请不要用 E-MAIL 来代替!) 我的地址是: Till Gerken Wiefelsteder Str. 2a 26127 Oldenburg Germany 电子信箱: Till.Gerken@ngosub0.ngo.ol.ni.schule.de Fido BBS 标号:[注 1] 2:2426/2190.16 同样,如果你有任何的意见或者建议以及批评都可以通过上面的任何一种方式联系我。 本文将解释 80386 保护模式基本原理,并提供一个完整的模式切换的源程序。这个小程序会 向你展示保护模式的基本规则。为了便于理解和阅读,它并没有经过优化。如果你有扩展的 问题或者建议,请给我发邮件。 下面的代码使用汇编语言写成,遵循 TASM 2.01 语法。对于 TASM 新版本来说,下面 代码同样可以工作,也许只需要增加一个宏,将 dwords 转化为 words 以便编译。[译者:我已 经以 MASM 6.11 为准修正了这些代码]。本文有许多图表,如果你打印下来会看得更清楚。TAB 字符设置为 8 个字符长度。 本文的全部代码放于 pmtut.asm 文件中。如果你打算在其他的编译器中运行 pmtut.asm, 比如 masm,[译者:此处删去关于如何从 TASM 2.01 移植到 MASM 方法介绍。因为,我已经移植好了, 下面的程序都是能在 masm 6.11 中编译运行通过的,并且这段有些复杂,我翻译起来很困难:] 我觉得应该把文章分为几个文件,每一个不同的主题对应一个文件(比如使用 DPMI, VCPI, XMS 和 RAW 进行模式切换,异常处理等等)。这样以来便于阅读。你认为如何呢? [第一章 完] 注释 1:原文是 Fido address。Fido 是一种专用电子公告牌,类似于今天 BBS,更像今天的论坛 1988 年 左右很火:) Fido address 是这个的标识。可以在下面的网址得到更多介绍 http://member.netease.com/~net653/fidonet.htm 第二章 开 始 好的,现在开始了。这篇文章就是关于保护模式...首先要了解进入保护模式所需。这里 需要注意的地方,第一点就是需要检查你的处理器是否支持。进入保护模式需要 80386 机器 以上的处理器。通过检测标志寄存器可以知道,标志位 12-14 位是特权级标志位,所以 只需要检测这些位是否可改写就可以了(8086/8088,80186 不使用这些位,并默认为 0,80286 开始有这些位,但是只有在保护模式下才可以修改[注 2]。dos 不能在保护模式下运行,所以 即使是 80286 处理器,在实模式下也是不能修改的) ; checks for a 386 .model Tiny .data no386e db 'Sorry, at least a 80386 is needed!',13,10,'$' yes386up db 'Ok,it can enter PM!',13,10,13,10,'$' .code .startup pushf ; save flags xor ah,ah ; clear high byte push ax ; push AX onto the stack popf ; pop this value into the flag register pushf ; push flags onto the stack pop ax ; ...and get flags into AX and ah,0f0h ; try to set the high nibble cmp ah,0f0h ; the high nibble is never 0f0h on a je no386 ; 80386! mov ah,70h ; now try to set NT and IOPL push ax popf pushf pop ax and ah,70h ; if they couldn't be modified, there jz no386 ; is no 80386 installed popf ; restore the flags mov dx,offset yes386up mov ah,09h int 21h mov ax,4c00h int 21h no386: ; if there isn't a 80386, put a msg mov dx,offset no386e ; and exit jmp err16exit ; exits with a msg ; In: DS:DX - pointer to msg err16exit: mov ah,9 ; select DOS' print string function int 21h ; do it mov ax,4cffh ; exit with 0ffh as exit code int 21h ; good bye... .exit end [已经按照 masm6.11 修正并验证,注意,要编译为 com 文件] 通过上面这段程序,我们可以很容易的检测出是否为 80386 或者更高的处理器。下面要做的 第二件事情是检查程序运行的环境。扩展内存管理程序,诸如 EMM386,QEMM 等等, 是通过切换到 v86 模式下提供服务的。我们的程序只能在实模式(REAL MODE)下工作, 我们需要另外的程序来检测当前所处状态。 区分实模式和 v86 模式的方法是检测控制寄存器 CR0 的第 0 位[注 3],为 0 说明在实模式下, 否则不是。 ; checks if we are running in Real Mode .model tiny .data nrme db 'You are currently running in V86 mode!',13,10,'$' yesreal db 'You are currently running in Real mode!',13,10,'$' .386 .code .startup mov eax,cr0 ; get CR0 to EAX and al,1 ; check if PM bit is set jnz not_real_mode ; yes, it is, so exit ; no, it isn't, show message mov ah,09h mov dx,offset yesreal int 21h mov ax,4c00h int 21h not_real_mode: mov ah,09h mov dx,offset nrme int 21h .exit end [我做了一些修改,masm 6.11 可以编译运行。我在 FREEDOS 中运行,发现启动加载 HIMEM.SYS 后仍然 是实模式,而加载 EMM386 之后就处于 V86 MODE 了] 上面的测试完成后,我们可以确信能进入保护模式。DMPI 和 VCPI(甚至 BIOS)都可 以用来切换实模式和保护模式,使用这样功能调用很简单,留给你练习(最后面描述了部分 接口)。 注意,上面的程序中我使用了 mov eax,cr0 Jerzy Tarasiuk 指出在保护模式下是不允许这样做的,特别是在 286 的机器上。如果你的机器不容许,请使用 smsw ax 这条指令只在 386 及其以上才有, 它在任何环境下都可以使用。与之匹配,你也可以用 lmsw ax 来代替 mov cr0,ax 切换到 保护模式. 需要注意的是你只能使用 lmsw 进入保护模式。但不能用这条指令无法令从保护模式 退出。如果想退出的话只有关机或者重启[译者 这篇文章只讲进入没讲退出]。就先说这么多, 对于初学者这个问题过于复杂:) [第二章 完] 注释 2: IA-32 Intel® Architecture Software Developer’s Manual Volume 1: Basic Architecture 3-13页 Figure 3-7. EFLAGS Register 注释 3: IA-32 Intel® Architecture Software Developer’s Manual Volume 3: System Programming Guide 2-12 页 Figure 2-5. Control Registers 下面的表来自2-13页 PG Paging (bit 31 of CR0). Enables paging when set; disables paging when clear. When paging is disabled, all linear addresses are treated as physical addresses. The PG flag has no effect if the PE flag (bit 0 of register CR0) is not also set; in fact, setting the PG flag when the PE flag is clear causes a general-protection exception (#GP) to be generated. See Section 3.6., “Paging (Virtual Memory) Overview”, for a detailed description of the processor’s paging mechanism. CD Cache Disable (bit 30 of CR0). When the CD and NW flags are clear, caching of memory locations for the whole of physical memory in the processor’s internal (and external) caches is enabled. When the CD flag is set, caching is restricted as described in Table 10-5. To prevent the processor from accessing and updating its caches, the CD flag must be set and the caches must be invalidated so that no cache hits can occur (see Section 10.5.3., “Preventing Caching”). See Section 10.5., “Cache Control”, for a detailed description of the additional restrictions that can be placed on the caching of selected pages or regions of memory. NW Not Write-through (bit 29 of CR0). When the NW and CD flags are clear, write-back (for Pentium 4, Intel Xeon, P6 family, and Pentium processors) or write-through (for Intel486 processors) is enabled for writes that hit the cache and invalidation cycles are enabled. See Table 10-5 for detailed information about the affect of the NW flag on caching for other settings of the CD and NW flags. AM Alignment Mask (bit 18 of CR0). Enables automatic alignment checking when set; disables alignment checking when clear. Alignment checking is performed only when the AM flag is set, the AC flag in the EFLAGS register is set, the CPL is 3, and the processor is operating in either protected or virtual-8086 mode. WP Write Protect (bit 16 of CR0). Inhibits supervisor-level procedures from writing into user-level read-only pages when set; allows supervisor-level procedures to write into user-level read-only pages when clear. This flag facilitates implementation of the copyon- write method of creating a new process (forking) used by operating systems such as UNIX*. NE Numeric Error (bit 5 of CR0). Enables the native (internal) mechanism for reporting x87 FPU errors when set; enables the PC-style x87 FPU error reporting mechanism when clear. When the NE flag is clear and the IGNNE# input is asserted, x87 FPU errors are ignored. When the NE flag is clear and the IGNNE# input is deasserted, an unmasked x87 FPU error causes the processor to assert the FERR# pin to generate an external interrupt and to stop instruction execution immediately before executing the next waiting floating-point instruction or WAIT/FWAIT instruction. The FERR# pin is intended to drive an input to an external interrupt controller (the FERR# pin emulates the ERROR# pin of the Intel 287 and Intel 387 DX math coprocessors). The NE flag, IGNNE# pin, and FERR# pin are used with external logic to implement PC-style error reporting. (See “Software Exception Handling” in Chapter 8, and Appendix D in the IA-32 Intel Architecture Software Developer’s Manual, Volume 1, for more information about x87 FPU error reporting and for detailed information on when the FERR# pin is asserted, which is implementation dependent.) ET Extension Type (bit 4 of CR0). Reserved in the Pentium 4, Intel Xeon, P6 family, and Pentium processors. (In the Pentium 4, Intel Xeon, and P6 family processors, this flag is hardcoded to 1.) In the Intel386 and Intel486 processors, this flag indicates support of Intel 387 DX math coprocessor instructions when set. TS Task Switched (bit 3 of CR0). Allows the saving of the x87 FPU, MMX, SSE, and SSE2 context on a task switch to be delayed until an x87 FPU, MMX, SSE, or SSE2 instruction is actually executed by the new task. The processor sets this flag on every task switch and tests it when executing x87 FPU, MMX, SSE, and SSE2 instructions. • If the TS flag is set and the EM flag (bit 2 of CR0) is clear, a device-not-available exception (#NM) is raised prior to the execution of any x87 FPU, MMX, SSE, and SSE2 instruction, with the exception of the PAUSE, PREFETCHh, SFENCE, LFENCE, MFENCE, MOVNTI, and CLFLUSH instructions. (See the paragraph below for the special case of the WAIT/FWAIT instructions.) • If the TS flag is set and the MP flag (bit 1 of CR0) and EM flag are clear, an #NM exception is not raised prior to the execution of an x87 FPU WAIT/FWAIT instruction. • If the EM flag is set, the setting of the TS flag has no affect on the execution of the x87 FPU, MMX, SSE, and SSE2 instructions. Table 2-1 shows the actions taken when the processor encounters an x87 FPU instruction based on the settings of the TS, EM, and MP flags. Tables 11-1 and 12-1 show the actions taken when the processor encounters an MMX and or an SSE or SSE2 instruction, respectively. The processor does not automatically save the context of the x87 FPU, XMM, and MXCSR registers on a task switch. Instead it sets the TS flag, which causes the processor to raise an #NM exception whenever it encounters an x87 FPU, MMX, SSE, or SSE2 instruction in the instruction stream for the new task (with the exception of the instructions listed above). The fault handler for the #NM exception can then be used to clear the TS flag (with the CLTS instruction) and save the context of the x87 FPU, XMM, and MXCSR registers. If the task never encounters an x87 FPU, MMX, SSE, or SSE2 instruction, the x87 FPU, MMX, SSE, and SSE2 context is never saved. Table 2-1. Action Taken By x87 FPU Instructions for Different Combinations of EM, MP and TS EM Emulation (bit 2 of CR0). Indicates that the processor does not have an internal or external x87 FPU when set; indicates an x87 FPU is present when clear. This flag also affects the execution of MMX, SSE, and SSE2 instructions. When the EM flag is set, execution of an x87 FPU instruction generates a device-notavailable exception (#NM). This flag must be set when the processor does not have an internal x87 FPU or is not connected to an external math coprocessor. Setting this flag forces all floating-point instructions to be handled by software emulation. Table 9-2 shows the recommended setting of this flag, depending on the IA-32 processor and x87 FPU or math coprocessor present in the system. Table 2-1 shows the interaction of the EM, MP, and TS flags. Also, when the EM flag is set, execution of an MMX instruction causes an invalidopcode exception (#UD) to be generated (see Table 11-1). Thus, if an IA-32 processor incorporates MMX technology, the EM flag must be set to 0 to enable execution of MMX instructions. Similarly for the SSE and SSE2 extensions, when the EM flag is set, execution of most SSE and SSE2 instructions causes an invalid opcode exception (#UD) to be generated (see Table 12-1). Thus, if an IA-32 processor incorporates the SSE and/or SSE2 extensions, the EM flag must be set to 0 to enable execution of these extensions. Those SSE and SSE2 instructions that are not affected by the EM flag are the PAUSE, PREFETCHh, SFENCE, LFENCE, MFENCE, MOVNTI, and CLFLUSH instructions. MP Monitor Coprocessor (bit 1 of CR0). Controls the interaction of the WAIT (or FWAIT) instruction with the TS flag (bit 3 of CR0). If the MP flag is set, a WAIT instruction generates a device-not-available exception (#NM) if the TS flag is set. If the MP flag is clear, the WAIT instruction ignores the setting of the TS flag. Table 9-2 shows the recommended setting of this flag, depending on the IA-32 processor and x87 FPU or math coprocessor present in the system. Table 2-1 shows the interaction of the MP, EM, and TS flags. PE Protection Enable (bit 0 of CR0). Enables protected mode when set; enables realaddress mode when clear. This flag does not enable paging directly. It only enables segment-level protection. To enable paging, both the PE and PG flags must be set. See Section 9.9., “Mode Switching”, for information using the PE flag to switch between real and protected mode. 第三章 表格、描述符、选择子和诸多令人困惑的表格 在模式切换之前,我们需要在内存中建立几张表以及描述符。这些是叫做 GDT,LDT 和 IDT 的东西。 GDT(Global Descriptor Table)是全局描述符表的简称,它包含基本段描述符。这些段 描述符存放内存不同部分的信息。实模式下,一个段最大只有 64KB,与下一个段最小相距 16 个字节。保护模式下可将段放在内存的任何位置。最大可达 4GB。LDT 是可选的,同样 也是包含段描述符信息,但主要是为应用程序的。例如,一个操作系统,可以为系统建立 GDT,为每一个任务建立一个 LDT 包含应用程序的描述符。 LDT 是同 GDT 一样是描述符。他的作用是为不同的任务划分内存格局[译者 原文是 memory-layouts]。我们的程序 LDT 不是必需的。IDT 被称作中断描述符表。它的作用是告诉 处理器如何找到每一个中断的入口[译者 real mode 下中断的入口存放于最低的 1k 空间里, 部分是 dos 建立的,部分是 bios 建立的]。它包含了每一个中断的入口,使得中断同实模式下的一 样,但是这些入口的同实模式截然不同。下面是这些描述符的基本格式: AVL — Available for use by system software BASE — Segment base address D/B — Default operation size (0 = 16-bit segment; 1 = 32-bit segment) DPL — Descriptor privilege level G — Granularity LIMIT — Segment Limit P — Segment present S — Descriptor type (0 = system; 1 = code or data) TYPE — Segment type [注4] [实际上这些描述符有一点区别,请参考注释 5] 从上面可以看出,基本段描述符大小为 4*16=64byte。 下面是汇编语言的段描述符的结构,对照上面可以很容易理解。 ; contains a segment descriptor struc segment_descriptor seg_length0_15 dw ? ; low word of the segment length base_addr0_15 dw ? ; low word of base address base_addr16_23 db ? ; low byte of high word of base addr. flags db ? ; segment type and misc. flags access db ? ; highest nibble of segment length ; and access flags base_addr24_31 db ? ; highest byte of base address ends segment_descriptor 这样的结构方便了 GDT 和 LDT 的使用。我们用同样的方法表示 IDT,不过和上面的结构 不同。 Figure 4-7. Call-Gate Descriptor [注6] 汇编语言格式如下: ; contains an interrupt descriptor struc interrupt_descriptor offset0_15 dw ? ; low word of handler offset selector0_15 dw ? ; segment selector zero_byte db 0 ; unused in this descriptor format flags db ? ; flag-byte offset16_31 dw ? ; high word of handler offset ends interrupt_descriptor 现在,你应该了解段和中断描述符的格式了。下面的问题就是如何填充使用呢。在 解释之前,请先看各个位的名称以及作用 G 这个位指定了段的“粒度”,为 0 时,描述符中的长度位指示段的以 byte 为单位的 实际长度。如果为 1,实际长度为长度位指示的乘上 4096。 D 操作的大小 D=0 16 位操作数 D=1 32 位操作数 这个位指定一些操作的默认长度,比如 rep xxx 这样的指令该位为 0,默认的操作数为 16 位,处理器像 80286 一样工作。该位为 1,默认操作大小为 32 位。但是 d=0 并不意味 不能使用 32 位指令,它只影响需要默认大小的指令。 AVL 在 80286-80486 都没有定义。出于向后兼容考虑最好保证为 0。如果你的程序 在 PENTIUM 以下的机器运行,也可以使用这个为你自己软件使用。 P 存在位 P=0 => 描述符对地址转换无效 P=1 => 描述符对地址转换有效 若该位无效,当加载指向该段的段选择符时,处理器会产生异常(#NP)。 [原文不清楚,下面是我参照 3-11 进一步说明] 比如:程序想要使用大于当前的内存,就可以将最少使用的段(从 A 位可以确定) 保存到磁盘,然后清除段中 P 位。如果有访问这个段就会产生一般保护错误。捕获这个 异常后,将这位设置为 1 再读入内存。 处理器在产生一般保护异常错误之前,会检测 P 位,这样在 P 位清 0 条件下描述符 可以保持你虚拟内存关程序 VMM 描述符其余的信息。 DPL 描述符特权级描述符 取值范围从 0(级别最高)到 3(级别最低)。如果程序试图访问级别高于他的 程序处理器将产生一个一般保护错误。 注意:本文中每次我提到特权级别,我的意思都是高优先级的数字小,特权级 低的数字大。例如:segment 1: dpl=1 -->segment 1 的特权级别高于 segment 2:dpl=2 DT 描述符类型 DT=0 => 系统描述符 (系统段或者门) DT=1 => 应用程序描述符(数据或代码) 若这位为 0,描述符描述的是系统软件的段,或者是门描述符 TYPE 段属性 有 4 位来表述段属性 Bit 3 2 1 0 Type Description Name T E W A 0 0 0 0 Data read-only 0 0 0 1 Data read-only, accessed 0 0 1 0 Data read/write 0 0 1 1 Data read/write, accessed 0 1 0 0 Data read-only, expand down 0 1 0 1 Data read-only, exp. down, acc. 0 1 1 0 Data read-write, expansion down 0 1 1 1 Data read-write, exp. down, acc. 1 0 0 0 Code exec-only --------------------------------------------------------------- Name T C R A 1 0 0 1 Code exec-only, accessed 1 0 1 0 Code exec-read 1 0 1 1 Code exec-read, accessed 1 1 0 0 Code exec-only, conforming 1 1 0 1 Code exec-only, conf., acc. 1 1 1 0 Code exec-read, conforming 1 1 1 1 Code exec-read, conf., acc. 只读意味着你只能够读取这个段,读/写只写意味着你可以从这个段读取或者向这个段 写入。只执行意思是这个段可以读或者执行。与实模式不同的是你的程序不可以自己修改执 行代码。关于这一点可以在下面找到介绍。每次程序试图访问这个段的时候,访问位都会置 如果你想指出交换哪一个段(vmm 是非常好的例子),可以为它设置一个计数器加一次 计数器增加 1。这样,计数最小的可以交换到磁盘上,也就是说最不常用的可以放到磁盘上。 但是请注意:如果 A 位置 1,程序必须执行这个段!交换这些段会出问题!扩展方向是一个 严重问题。当该位为 0,扩展方向是向上的,就是段向高增长。你可以访问从 0 到 limit 中 的每一个地址。limit 意思是段的实际长度。如果段的粒度设置为 0,limit 就是长度。但如果 粒度为 1,需要将 limit 乘以 4096(4k)才是真正的段长度。当这个位设置为 1 时,问题 很麻烦。扩展方向是向低方向,长度递减。可访问的范围是 G=0 : Limit-1 <= Address <= 0ffffh G=1 : Limit-1 <= Address <= 0ffffffffh. 因为 4G 是 wrap-around 的,当 e 是 0 或产生一般保护错误,c=1 意味着可以调用特权 级低于或等于调用者的段。当前特权级别不会改变!如果你在 c=0 的段(不通过任务门) 直接调用不同特权级段,会导致一般保护错误。 讲到这里你也许会觉得太复杂了,是不是?! 刚开始这些会让人很迷惑,但是当你看代 码的时候,就会觉得简单[译者 对照着图表,填写就容易看清楚得多,不是考试,没有必要 背下] 下面的事情就是如何用代码解释什么是选择符: 段选择子选择的就是段描述符! 格式很容易理解: 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00 [ Pointer into a Descriptor Table ] TI [ RPL ] RPL 是请求特权级。如果段描述符表中的描述符的特权级高于 rpl,会产生一般保护错误。 TI 被称为引用描述符表指示位,用来指示描述符来源 TI=0 意思是来自 GDT,TI=1 意味着来自 LDT。 这个指针包含指向能够找到描述符的段描述符表的偏移。 [第三章 完] 注释 4:IA-32 Intel® Architecture Software Developer’s Manual Volume 3: System Programming Guide 3-10 页 figure 3-8 注释 5:IA-32 Intel® Architecture Software De
/
本文档为【保护模式指南】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索