国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 學(xué)院 > 開(kāi)發(fā)設(shè)計(jì) > 正文

理解APCS-- ARM過(guò)程調(diào)用標(biāo)準(zhǔn)

2019-11-08 19:49:40
字體:
來(lái)源:轉(zhuǎn)載
供稿:網(wǎng)友

原文地址:http://blog.csdn.net/skyflying2012/article/details/37510171

 

介紹

APCS,ARM 過(guò)程調(diào)用標(biāo)準(zhǔn)(ARM PRocedure Call Standard),提供了緊湊的編寫例程的一種機(jī)制,定義的例程可以與其他例程交織在一起。最顯著的一點(diǎn)是對(duì)這些例程來(lái)自哪里沒(méi)有明確的限制。它們可以編譯自  C、 Pascal、也可以是用匯編語(yǔ)言寫成的。

APCS 定義了:

對(duì)寄存器使用的限制。使用棧的慣例。在函數(shù)調(diào)用之間傳遞/返回參數(shù)??梢员弧厮荨幕跅5慕Y(jié)構(gòu)的格式,用來(lái)提供從失敗點(diǎn)到程序入口的函數(shù)(和給予的參數(shù))的列表。

APCS 不一個(gè)單一的給定標(biāo)準(zhǔn),而是一系列類似但在特定條件下有所區(qū)別的標(biāo)準(zhǔn)。例如,APCS-R (用于 RISC OS)規(guī)定在函數(shù)進(jìn)入時(shí)設(shè)置的標(biāo)志必須在函數(shù)退出時(shí)復(fù)位。在 32 位標(biāo)準(zhǔn)下,并不是總能知道進(jìn)入標(biāo)志的(沒(méi)有 USR_CPSR),所以你不需要恢復(fù)它們。如你所預(yù)料的那樣,在不同版本間沒(méi)有相容性。希望恢復(fù)標(biāo)志的代碼在它們未被恢復(fù)的時(shí)候可能會(huì)表現(xiàn)失常...

如果你開(kāi)發(fā)一個(gè)基于 ARM 的系統(tǒng),不要求你去實(shí)現(xiàn) APCS。但建議你實(shí)現(xiàn)它,因?yàn)樗浑y實(shí)現(xiàn),且可以使你獲得各種利益。但是,如果要寫用來(lái)與編譯后的 C 連接的匯編代碼(C與匯編混著編寫),則必須使用 APCS。編譯器期望特定的條件,在你的加入(add-in)代碼中必須得到滿足。一個(gè)好例子是 APCS 定義 a1 到 a4 可以被破壞,而 v1 到 v6 必須被保護(hù)?,F(xiàn)在我確信你正在撓頭并自言自語(yǔ)“a 是什么? v 是什么?”。所以首先介紹 APCS-R 寄存器定義...

 

寄存器命名

APCS 對(duì)我們通常稱為 R0 到 R14 的寄存器起了不同的名字。使用匯編器預(yù)處理器的功能,你可以定義 R0 等名字,但在你修改其他人寫的代碼的時(shí)候,最好還是學(xué)習(xí)使用 APCS 名字。

寄存器名字
Reg#APCS意義
R0a1工作寄存器
R1a2"
R2a3"
R3a4"
R4v1必須保護(hù)
R5v2"
R6v3"
R7v4"
R8v5"
R9v6"
R10sl棧限制
R11fp楨指針
R12ip內(nèi)部過(guò)程調(diào)用寄存器
R13sp棧指針
R14lr連接寄存器
R15pc程序計(jì)數(shù)器

譯注:ip 是指令指針的簡(jiǎn)寫。

這些名字不是由標(biāo)準(zhǔn)的 Acorn 的 objasm(版本 2.00)所定義的,但是 objasm 的后來(lái)版本,和其他匯編器(比如 Nick Robert 的 ASM)定義了它們。要定義一個(gè)寄存器名字,典型的,你要在程序最開(kāi)始的地方使用 RN宏指令(directive):

a1     RN      0a2     RN      1a3     RN      2    ...等...r13    RN      13sp     RN      13r14    RN      14lr     RN      r14pc     RN      15

這個(gè)例子展示了一些重要的東西:

寄存器可以定義多個(gè)名字 - 你可以定義‘r13’和‘sp’二者。寄存器可以定義自前面定義的寄存器 - ‘lr’定義自叫做‘r14’的寄存器。(對(duì)于 objasm 是正確的,其他匯編器可能不是這樣)

設(shè)計(jì)關(guān)鍵

函數(shù)調(diào)用應(yīng)當(dāng)快、小、和易于(由編譯器來(lái))優(yōu)化。函數(shù)應(yīng)當(dāng)可以妥善處理多個(gè)棧。函數(shù)應(yīng)當(dāng)易于寫可重入和可重定位的代碼;主要通過(guò)把可寫的數(shù)據(jù)與代碼分離來(lái)實(shí)現(xiàn)。但是最重要的是,它應(yīng)當(dāng)簡(jiǎn)單。這樣匯編編程者可以非常容易的使用它的設(shè)施,而調(diào)試者能夠非常容易的跟蹤程序。

 

一致性

程序的遵循 APCS 的部分在調(diào)用外部函數(shù)時(shí)被稱為“一致”。在程序執(zhí)行期間的所有時(shí)候都遵循 APCS (典型的,由編譯器生成的程序)被稱為“嚴(yán)格一致”。協(xié)議指出,假如你遵守正確的進(jìn)入和退出參數(shù),你可以在你自己的函數(shù)范圍內(nèi)做你需要的任何事情,而仍然保持一致。這在有些時(shí)候是必須的,比如在寫 SWI 偽裝(veneers)的時(shí)候使用了許多給實(shí)際的 SWI 調(diào)用的寄存器。

 

棧是鏈接起來(lái)的‘楨’的一個(gè)列表,通過(guò)一個(gè)叫做‘回溯結(jié)構(gòu)’的東西來(lái)鏈接它們。這個(gè)結(jié)構(gòu)存儲(chǔ)在每個(gè)楨的高端。按遞減地址次序分配棧的每一塊。寄存器 sp 總是指向在最當(dāng)前楨中最低的使用的地址。這符合傳統(tǒng)上的滿降序棧。在 APCS-R 中,寄存器 sl 持有一個(gè)棧限制,你遞減 sp不能低于它。在當(dāng)前棧指針和當(dāng)前棧之間,不應(yīng)該有任何其他 APCS 函數(shù)所依賴的東西,在被調(diào)用的時(shí)候,函數(shù)可以為自己設(shè)置一個(gè)棧塊。

可以有多個(gè)棧區(qū)(chunk)。它們可以位于內(nèi)存中的任何地址,這里沒(méi)有提供規(guī)范。典型的,在可重入方式下執(zhí)行的時(shí)候,這將被用于為相同的代碼提供多個(gè)棧;一個(gè)類比是 FileCore,它通過(guò)簡(jiǎn)單的設(shè)置‘狀態(tài)’信息和并按要求調(diào)用相同部分的代碼,來(lái)向當(dāng)前可獲得的 FileCore 文件系統(tǒng)(ADFS、RAMFS、IDEFS、SCSIFS 等)提供服務(wù)。

 

回溯結(jié)構(gòu)

寄存器 fp(楨指針)應(yīng)當(dāng)是零或者是指向棧回溯結(jié)構(gòu)的列表中的最后一個(gè)結(jié)構(gòu),提供了一種追溯程序的方式,來(lái)反向跟蹤調(diào)用的函數(shù)。

回溯結(jié)構(gòu)是:

地址高端   保存代碼指針        [fp]         fp 指向這里   返回 lr 值          [fp, #-4]    返回 sp 值          [fp, #-8]    返回 fp 值          [fp, #-12]  指向下一個(gè)結(jié)構(gòu)    [保存的 sl]   [保存的 v6]    [保存的 v5]    [保存的 v4]    [保存的 v3]    [保存的 v2]   [保存的 v1]   [保存的 a4]   [保存的 a3]   [保存的 a2]   [保存的 a1]   [保存的 f7]                          三個(gè)字   [保存的 f6]                          三個(gè)字   [保存的 f5]                          三個(gè)字   [保存的 f4]                          三個(gè)字   地址低端

這個(gè)結(jié)構(gòu)包含 4 至 27 個(gè)字,在方括號(hào)中的是可選的值。如果它們存在,則必須按給定的次序存在(例如,在內(nèi)存中保存的 a3 下面可以是保存的 f4,但 a2-f5 則不能存在)。浮點(diǎn)值按‘內(nèi)部格式’存儲(chǔ)并占用三個(gè)字(12 字節(jié))。

fp 寄存器指向當(dāng)前執(zhí)行的函數(shù)的?;厮萁Y(jié)構(gòu)。返回 fp 值應(yīng)當(dāng)是零,或者是指向由調(diào)用了這個(gè)當(dāng)前函數(shù)的函數(shù)建立的?;厮萁Y(jié)構(gòu)的一個(gè)指針。而這個(gè)結(jié)構(gòu)中的返回 fp 值是指向調(diào)用了調(diào)用了這個(gè)當(dāng)前函數(shù)的函數(shù)的函數(shù)的?;厮萁Y(jié)構(gòu)的一個(gè)指針;并以此類推直到第一個(gè)函數(shù)。

在函數(shù)退出的時(shí)候,把返回連接值、返回 sp 值、和返回 fp 值裝載到 pc、sp、和 fp 中。

  #include <stdio.h>  void one(void);  void two(void);  void zero(void);  int main(void)  {     one();     return 0;  }  void one(void)  {     zero();     two();     return;  }  void two(void)  {     printf("main...one...two/n");     return;  }  void zero(void)  {     return;  }  當(dāng)它在屏幕上輸出消息的時(shí)候,  APCS 回溯結(jié)構(gòu)將是:      fp ----> two_structure               return link               return sp               return fp  ----> one_structure               ...              return link                                return sp                                return fp  ----> main_structure                                ...              return link                                                 return sp                                                 return fp  ----> 0                                                 ...

所以,我們可以檢查 fp 并參看給函數(shù)‘two’的結(jié)構(gòu),它指向給函數(shù)‘one’的結(jié)構(gòu),它指向給‘main’的結(jié)構(gòu),它指向零來(lái)終結(jié)。在這種方式下,我們可以反向追溯整個(gè)程序并確定我們是如何到達(dá)當(dāng)前的崩潰點(diǎn)的。值得指出‘zero’函數(shù),因?yàn)樗呀?jīng)被執(zhí)行并退出了,此時(shí)我們正在做它后面的打印,所以它曾經(jīng)在回溯結(jié)構(gòu)中,但現(xiàn)在不在了。值得指出的還有對(duì)于給定代碼不太可能總是生成象上面那樣的一個(gè) APCS 結(jié)構(gòu)。原因是不調(diào)用任何其他函數(shù)的函數(shù)不要求完全的 APCS 頭部。

為了更細(xì)致的理解,下面是代碼是 Norcroft C v4.00 為上述代碼生成的...

        AREA |C$$code|, CODE, READONLY        IMPORT  |__main||x$codeseg|        B       |__main|        DCB     &6d,&61,&69,&6e        DCB     &00,&00,&00,&00        DCD     &ff000008        IMPORT  |x$stack_overflow|        EXPORT  one        EXPORT  mainmain        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4        CMPS    sp, sl        BLLT    |x$stack_overflow|        BL      one        MOV     a1, #0        LDMEA   fp, {fp,sp,pc}^        DCB     &6f,&6e,&65,&00        DCD     &ff000004        EXPORT  zero        EXPORT  twoone        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4        CMPS    sp, sl        BLLT    |x$stack_overflow|        BL      zero        LDMEA   fp, {fp,sp,lr}        B       two        IMPORT  |_printf|two        ADD     a1, pc, #L000060-.-8        B       |_printf|L000060        DCB     &6d,&61,&69,&6e        DCB     &2e,&2e,&2e,&6f        DCB     &6e,&65,&2e,&2e        DCB     &2e,&74,&77,&6f        DCB     &0a,&00,&00,&00zero        MOVS    pc, lr        AREA |C$$data||x$dataseg|        END

這個(gè)例子不遵從 32 為體系。APCS-32 規(guī)定只是簡(jiǎn)單的說(shuō)明了標(biāo)志不需要被保存。所以刪除 LDM 的‘^’后綴,并在函數(shù) zero 中刪除 MOVS 的‘S’后綴。則代碼就與遵從 32-bit 的編譯器生成的一樣了。

保存代碼指針包含這條設(shè)置回溯結(jié)構(gòu)的指令(STMFD ...)的地址再加上 12 字節(jié)。記住,對(duì)于 26-bit 代碼,你需要去除其中的 PSR 來(lái)得到實(shí)際的代碼地址。

現(xiàn)在我們查看剛進(jìn)入函數(shù)的時(shí)候:

pc總是包含下一個(gè)要被執(zhí)行的指令的位置。lr (總是)包含著退出時(shí)要裝載到 pc中的值。在 26-bit 位代碼中它還包含著 PSR。sp指向當(dāng)前的棧塊(chunk)限制,或它的上面。這是用于復(fù)制臨時(shí)數(shù)據(jù)、寄存器和類似的東西到其中的地方。在 RISC OS 下,你有可選擇的至少 256 字節(jié)來(lái)擴(kuò)展它。fp要么是零,要么指向回溯結(jié)構(gòu)的最當(dāng)前的部分。函數(shù)實(shí)參布置成(下面)描述的那樣。

 

實(shí)際參數(shù)

APCS 沒(méi)有定義記錄、數(shù)組、和類似的格局。這樣語(yǔ)言可以自由的定義如何進(jìn)行這些活動(dòng)。但是,如果你自己的實(shí)現(xiàn)實(shí)際上不符合 APCS 的精神,那么將不允許來(lái)自你的編譯器的代碼與來(lái)自其他編譯器的代碼連接在一起。典型的,使用 C 語(yǔ)言的慣例。

前 4 個(gè)整數(shù)實(shí)參(或者更少!)被裝載到 a1 - a4。前 4 個(gè)浮點(diǎn)實(shí)參(或者更少!)被裝載到 f0 - f3。其他任何實(shí)參(如果有的話)存儲(chǔ)在內(nèi)存中,用進(jìn)入函數(shù)時(shí)緊接在 sp 的值上面的字來(lái)指向。換句話說(shuō),其余的參數(shù)被壓入棧頂。所以要想簡(jiǎn)單。最好定義接受 4 個(gè)或更少的參數(shù)的函數(shù)。

 

函數(shù)退出

通過(guò)把返回連接值傳送到程序計(jì)數(shù)器中來(lái)退出函數(shù),并且:

如果函數(shù)返回一個(gè)小于等于一個(gè)字大小的值,則把這個(gè)值放置到 a1 中。如果函數(shù)返回一個(gè)浮點(diǎn)值,則把它放入 f0 中。sp、fp、sl、v1-v6、和 f4-f7 應(yīng)當(dāng)被恢復(fù)(如果被改動(dòng)了)為包含在進(jìn)入函數(shù)時(shí)它所持有的值。我測(cè)試了故意的破壞寄存器,而結(jié)果是(經(jīng)常在程序完全不同的部分)出現(xiàn)不希望的和奇異的故障。ip、lr、a2-a4、f1-f3 和入棧的這些實(shí)參可以被破壞。

在 32 位模式下,不需要對(duì) PSR 標(biāo)志進(jìn)行跨越函數(shù)調(diào)用的保護(hù)。在 26 位模式下必須這樣,并通過(guò)傳送 lr 到 pc 中(MOVS、或 LDMFD xxx^)來(lái)暗中恢復(fù)。必須從 lr 重新裝載 N、Z、C 和 V,跨越函數(shù)保護(hù)這些標(biāo)志不是足夠的。

 

建立?;厮萁Y(jié)構(gòu)

對(duì)于一個(gè)簡(jiǎn)單函數(shù)(固定個(gè)數(shù)的參數(shù),不可重入),你可以用下列指令建立一個(gè)棧回溯結(jié)構(gòu):

function_name_label        MOV     ip, sp        STMFD   sp!, {fp,ip,lr,pc}        SUB     fp, ip, #4

這個(gè)片段(來(lái)自上述編譯后的程序)是最基本的形式。如果你要破壞其他不可破壞的寄存器,則你應(yīng)該在這個(gè) STMFD 指令中包含它們。

下一個(gè)任務(wù)是檢查??臻g。如果不需要很多空間(小于 256 字節(jié))則你可以使用:

        CMPS    sp, sl        BLLT    |x$stack_overflow|這是 C 版本 4.00 處理溢出的方式。在以后的版本中,你要調(diào)用 |__rt_stkovf_split_small|。

接著做你自己的事情...

通過(guò)下面的指令完成退出:

        LDMEA   fp, {fp,sp,pc}^

還有,如果你入棧了其他寄存器,則也在這里重新裝載它們。選擇這個(gè)簡(jiǎn)單的 LDM 退出機(jī)制的原因是它比分支到一個(gè)特殊的函數(shù)退出處理器(handler)更容易和更合理。

用在回溯中的對(duì)這個(gè)協(xié)議的一個(gè)擴(kuò)展是把函數(shù)名字嵌入到代碼中。緊靠在函數(shù)(和 MOV ip, sp)的前面的應(yīng)該是:

        DCD     &ff0000xx

這里的‘xx’是函數(shù)名字符串的長(zhǎng)度(包括填充和終結(jié)符)。這個(gè)字符串是字對(duì)齊、尾部填充的,并且應(yīng)當(dāng)被直接放置在 DCD &ff....的前面。

所以一個(gè)完整的?;厮荽a應(yīng)當(dāng)是:

        DCB     "my_function_name", 0, 0, 0, 0        DCD     &ff000010my_function_name        MOV     ip, sp        STMFD   sp!, {fp, ip, lr, pc}        SUB     fp, ip, #4        CMPS    sp, sl                    ; 如果你不使用棧        BLLT    |x$stack_overflow|        ; 則可以省略        ...處理...        LDMEA   fp, {fp, sp, pc}^

要使它遵從 32-bit 體系,只須簡(jiǎn)單的省略最后一個(gè)指令的‘^’。注意你不能在一個(gè)編譯的 26-bit 代碼中使用這個(gè)代碼。實(shí)際上,你可以去除它,但這不是我愿意打賭的事情。 

如果你不使用棧,并且你不需要保存任何寄存器,并且你不調(diào)用任何東西,則沒(méi)有必要設(shè)置 APCS 塊(但在調(diào)試階段對(duì)跟蹤問(wèn)題仍是有用的)。在這種情況下你可以:

my_simple_function        ...處理...        MOVS    pc, lr

(再次,對(duì) 32 位 APCS 使用 MOV 而不是 MOVS,但是不要冒險(xiǎn)與 26 位代碼連接)。

 

APCS 標(biāo)準(zhǔn)

總的來(lái)說(shuō),有多個(gè)版本的 APCS (實(shí)際上是 16 個(gè))。我們只關(guān)心在 RISC OS 上可能遇到的。

APCS-A就是 APCS-Arthur;由早期的 Arthur 所定義。它已經(jīng)被廢棄,原因是它有不同的寄存器定義(對(duì)于熟練的 RISC OS 程序員它是某種異類)。它用于在 USR 模式下運(yùn)行的 Arthur 應(yīng)用程序。不應(yīng)該使用它。

sl = R13, fp = R10, ip = R11, sp = R12, lr = R14, pc = R15。PRM (p4-411) 中說(shuō)“用 r12 作為 sp,而不是在體系上更自然的 r13,是歷史性的并先于 Arthur 和 RISC OS 二者。”棧是分段的并可按需要來(lái)擴(kuò)展。26-bit 程序計(jì)數(shù)器。不在 FP 寄存器中傳遞浮點(diǎn)實(shí)參。不可重入。標(biāo)志必須被恢復(fù)。

APCS-R就是 APCS-RISC OS。用于 RISC OS 應(yīng)用程序在 USR 模式下進(jìn)行操作;或在 SVC 模式下的模塊/處理程序。

sl = R10, fp = R11, ip = R12, sp = R13, lr = R14, pc = R15。它是唯一的最通用的 APCS 版本。因?yàn)樗芯幾g的 C 程序都使用 APCS-R。顯式的棧限制檢查。26-bit 程序計(jì)數(shù)器。不在 FP 寄存器中傳遞浮點(diǎn)實(shí)參。不可重入。標(biāo)志必須被恢復(fù)。

APCS-U就是 APCS-Unix,Acorn 的 RISCiX 使用它。它用于 RISCiX 應(yīng)用程序(USR 模式)或內(nèi)核(SVC 模式)。

sl = R10, fp = R11, ip = R12, sp = R13, lr = R14, pc = R15。隱式的棧限制檢查(使用 sl)。26-bit 程序計(jì)數(shù)器。不在 FP 寄存器中傳遞浮點(diǎn)實(shí)參。不可重入。標(biāo)志必須被恢復(fù)。

APCS-32它是 APCS-2(-R 和 -U)的一個(gè)擴(kuò)展,允許 32-bit 程序計(jì)數(shù)器,并且從執(zhí)行在 USR 模式下的一個(gè)函數(shù)中退出時(shí),允許標(biāo)志不被恢復(fù)。其他事情同于 APCS-R。Acorn C 版本 5 支持生成 32-bit 代碼;在用于廣域調(diào)試的 32 位工具中,它是最完整的開(kāi)發(fā)發(fā)行。一個(gè)簡(jiǎn)單的測(cè)試是要求你的編譯器導(dǎo)出匯編源碼(而不是制作目標(biāo)代碼)。你不應(yīng)該找到:MOVS PC, R14或者LDMFD R13!, {Rx-x, PC}^

 

對(duì)編碼有用的東西

首先要考慮的是該死的 26/32 位問(wèn)題。 簡(jiǎn)單的說(shuō),不轉(zhuǎn)彎抹角絕對(duì)沒(méi)有方法為兩個(gè)版本的 APCS 匯編同一個(gè)通用代碼。但是幸運(yùn)的這不是問(wèn)題。APCS 標(biāo)準(zhǔn)不會(huì)突然改變。RISC OS 的 32 位版本也不會(huì)立刻變異。所以利用這些,我們可以設(shè)計(jì)一種支持兩種版本的方案。這將遠(yuǎn)遠(yuǎn)超出 APCS,對(duì)于 RISC OS 的 32 位版本你需要使用 MSR 來(lái)處理狀態(tài)和模式位,而不是使用 TEQP。許多現(xiàn)存的 API 實(shí)際上不需要保護(hù)標(biāo)志位。所以在我們的 32 版本中可以通過(guò)把 MOVS PC,... 變成 MOV PC,...,和把 LDM {...}^ 變成 LDM {...},并重新建造來(lái)解決。objasm 匯編器(v3.00 和以后)有一個(gè) {CONFIG} 變量可以是 26 或 32??梢允褂盟ㄔ旌?..

my_function_name        MOV     ip, sp        STMFD   sp!, {fp, ip, lr, pc}        SUB     fp, ip, #4        ...處理...        [ {CONFIG} = 26          LDMEA   fp, {fp, sp, pc}^        |          LDMEA   fp, {fp, sp, pc}        ]

我未測(cè)試這個(gè)代碼。它(或類似的東西)好象是保持與兩個(gè)版本的 APCS 相兼容的最佳方式,也是對(duì) RISC OS 的不同版本,26 位版本和將來(lái)的 32 位版本的最佳方法。

測(cè)試是否處于 32 位? 如果你要求你的代碼有適應(yīng)性,有一個(gè)最簡(jiǎn)單的方法來(lái)確定處理器的 PC 狀態(tài):

   TEQ     PC, PC     ; 對(duì)于 32 位是 EQ;對(duì)于 26 位是 NE

使用它你可以確定:

26 位 PC,可能是 APCS-R 或 APCS-32。32 位 PC,不能 APCS-R。所有 26-bit 代碼(TEQP 等)面臨著失敗!
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 遂昌县| 惠水县| 湘西| 焦作市| 拉萨市| 玉树县| 兴义市| 宝应县| 屯门区| 清远市| 措勤县| 赫章县| 赞皇县| 鄢陵县| 张家界市| 谢通门县| 陵川县| 建湖县| 迁西县| 区。| 湖北省| 铁岭县| 清原| 祁阳县| 台安县| 凤庆县| 康平县| 安国市| 莱西市| 且末县| 东宁县| 远安县| 五指山市| 鲁甸县| 喀什市| 永胜县| 湟中县| 常山县| 岳阳县| 长丰县| 东丰县|