GCC 链接控制。用于分配内存

来源:百度文库 编辑:神马文学网 时间:2024/04/28 13:36:57
找到了下面内容,但是还是有些不理解什么叫可分配段,继续找。
.section

       语法:

              .section NAME [, "FLAGS"[, @TYPE[, @ENTSIZE]]]

              FLAGS 常用的有以下几种:

              `a'           可分配段

              `w'          可写段

              `x'           可执行段

              data section 具有的 FLAGS 通常是’wa’;

              text section 具有的 FLAGS 通常是’ax’。

       用法:

              .section .text1, “wa”      // 定义一个.text1段,且该段为可写的以及可分配的请参考 《GNU linker ld》   文分析基于linux2.4.19 source,pxa 270 cpu.
  ARM linux内核启动时,通过start_kernel()->trap_init()的调用关系,初始化内核的中断异常向量表.
  
/* arch/arm/kernel/traps.c */
void __init trap_init(void)
{
    extern void __trap_init(unsigned long);
    unsigned long base = vectors_base();
    __trap_init(base);
    if (base != 0)
       oopsprintk(KERN_DEBUG "Relocating machine vectors to 0x%08lx\n", base);
#ifdef CONFIG_CPU_32
   modify_domain(DOMAIN_USER, DOMAIN_CLIENT);
#endif
}
     vectors_base是一个宏,它的作用是获取ARM异常向量的地址,该宏在include/arch/asm-arm/proc-armv/system.h中定义:
extern unsigned long cr_no_alignment; /* defined in entry-armv.S */
extern unsigned long cr_alignment; /* defined in entry-armv.S */
#if __LINUX_ARM_ARCH__ >= 4
#define vectors_base() ((cr_alignment & CR_V) ? 0xffff0000 : 0)
#else
#define vectors_base() (0)
#endif
  对于ARMv4以下的版本,这个地址固定为0;ARMv4及其以上的版本,ARM异常向量表的地址受协处理器CP15的c1寄存器
(control
register)中V位(bit[13])的控制,如果V=1,则异常向量表的地址为0x00000000~0x0000001C;如果V=0,则
为:0xffff0000~0xffff001C。(详情请参考ARM Architecture Reference Manual)
下面分析一下cr_alginment的值是在哪确定的,我们在arch/arm/kernel/entry-armv.S找到cr_alignment的定义:
                 .globl  SYMBOL_NAME(cr_alignment)
                 .globl  SYMBOL_NAME(cr_no_alignment)
SYMBOL_NAME(cr_alignment):
                 .space  4
SYMBOL_NAME(cr_no_alignment):
                 .space  4
分析过head-armv.S文件的朋友都会知道,head-armv.S是非压缩内核的入口:
               
1                .section ".text.init",#alloc,#execinstr
2                .type    stext, #function
3ENTRY(stext)     
4                mov      r12, r0
5                 
6                mov      r0, #F_BIT | I_BIT | MODE_SVC    @ make sure svc mode
7                msr      cpsr_c, r0                       @ and all irqs disabled
8                bl       __lookup_processor_type         
9                teq      r10, #0                          @ invalid processor?
10                moveq    r0, #'p'                         @ yes, error 'p'
11                beq      __error
12                bl       __lookup_architecture_type
13                teq      r7, #0                           @ invalid architecture?
14                moveq    r0, #'a'                         @ yes, error 'a'
15                beq      __error
16                bl       __create_page_tables            
17                adr      lr, __ret                        @ return address
18                add      pc, r10, #12                     @ initialise processor
19                                                        @ (return control reg)
20
21                .type    __switch_data, %object
22__switch_data:  .long    __mmap_switched
23                 .long    SYMBOL_NAME(__bss_start)
24                 .long    SYMBOL_NAME(_end)
25                 .long    SYMBOL_NAME(processor_id)
26                 .long    SYMBOL_NAME(__machine_arch_type)
27                 .long    SYMBOL_NAME(cr_alignment)
28                 .long    SYMBOL_NAME(init_task_union)+8192
29
30                 .type    __ret, %function
31__ret:           ldr      lr, __switch_data
32                 mcr      p15, 0, r0, c1, c0
33                 mrc      p15, 0, r0, c1, c0, 0            @ read it back.
34                 mov      r0, r0
35                 mov      r0, r0
36                 mov      pc, lr
  这里我们关心的是从17行开始,17行code处将lr放置为__ret标号处的相对地址,以便将来某处返回时跳转到31行继续运行;
18行,对于我所分析的pxa270平台,它将是跳转到arch/arm/mm/proc-xscale.S中执行__xscale_setup函数,
在__xscale_setup中会读取CP15的control
register(c1)的值到r1寄存器,并在r1寄存器中设置相应的标志位(其中包括设置V位=1),但在__xscale_setup中,r1寄存
器并不立即写回到Cp15的control
register中,而是在返回后的某个地方,接下来会慢慢分析到。__xscale_setup调用move pc, lr指令返回跳转到31行。
31行,在lr寄存器中放置__switch_data中的数据__mmap_switched,在36行程序会跳转到__mmap_switched处。
32,33行,把r0寄存器中的值写回到cp15的control register(c1)中,再读出来放在r0中。
接下来再来看一下跳转到__mmap_switched处的代码:
40 _mmap_switched:
41                  adr      r3, __switch_data + 4
42                  ldmia    r3, {r4, r5, r6, r7, r8, sp}@ r2 = compat
43                                                         @ sp = stack pointer
44
45                  mov      fp, #0                           @ Clear BSS (and zero fp)
46 1:               cmp      r4, r5
47                  strcc    fp, [r4],#4
48                  bcc      1b
49
50                  str      r9, [r6]                         @ Save processor ID
51                  str      r1, [r7]                         @ Save machine type
52                  bic      r2, r0, #2                       @ Clear 'A' bit
53                  stmia    r8, {r0, r2}                     @ Save control register values
54                  b        SYMBOL_NAME(start_kernel)
  41~42行的结果是:r4=__bss_start,r5=__end,...,r8=cr_alignment,..,这里r8保存的是cr_alignment变量的地址.
到了53行,由于之前r0保存的是cp15的control register(c1)的值,这里把r0的值写入r8指向的地址,即cr_alignment=r0.到此为止,我们就看清楚了cr_alignment的赋值过程。
  让我们回到trap_init()函数,经过上面的分析,我们知道vectors_base返回0xffff0000。函数__trap_init由汇编代码编写,在arch/arm/kernel/entry-arm.S:
       .align 5
__stubs_start:
vector_IRQ:
...
vector_data:
    ....
vector_prefetch:
...                                                                                                                        
vector_undefinstr:
...
vector_FIQ: disable_fiq
subs pc, lr, #4
vector_addrexcptn:
b vector_addrexcptn         
       ...
__stubs_end:
   .equ __real_stubs_start, .LCvectors + 0x200
.LCvectors: swi SYS_ERROR0
   b __real_stubs_start + (vector_undefinstr - __stubs_start)
   ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
   b __real_stubs_start + (vector_prefetch - __stubs_start)
   b __real_stubs_start + (vector_data - __stubs_start)
   b __real_stubs_start + (vector_addrexcptn - __stubs_start)
   b __real_stubs_start + (vector_IRQ - __stubs_start)
   b __real_stubs_start + (vector_FIQ - __stubs_start)
ENTRY(__trap_init)
       stmfd sp!, {r4 - r6, lr}   /* 压栈,保存数据*/
       /* 复制异常向量表(.LCvectors起始的8个地址)到r0指向的地址(异常向量地址),r0就是__trap_init(base)函数调用时传递的参数,不明白的请参考ATPCS*/
       adr r1, .LCvectors    @ set up the vectors
       ldmia r1, {r1, r2, r3, r4, r5, r6, ip, lr}
   stmia r0, {r1, r2, r3, r4, r5, r6, ip, lr}
  
   /* 在异常向量地址后的0x200偏移处,放置散转代码,即__stubs_start~__stubs_end之间的各个异常处理代码*/
   add r2, r0, #0x200
   adr r0, __stubs_start   @ copy stubs to 0x200
   adr r1, __stubs_end
1:                ldr r3, [r0], #4
str r3, [r2], #4
cmp r0, r1
                   blt 1b
                   LOADREGS(fd, sp!, {r4 - r6, pc}) /*出栈,恢复数据,函数__trap_init返回*/
     __trap_init函数填充后的向量表如下:
     虚拟地址       异常               处理代码
     0xffff0000       reset               swi SYS_ERROR0
     0xffff0004       undefined        b __real_stubs_start + (vector_undefinstr - __stubs_start)
     0xffff0008       软件中断       ldr pc, __real_stubs_start + (.LCvswi - __stubs_start)
     0xffff000c       取指令异常   b __real_stubs_start + (vector_prefetch - __stubs_start)
     0xffff0010       数据异常       b __real_stubs_start + (vector_data - __stubs_start)
     0xffff0014       reserved          b __real_stubs_start + (vector_addrexcptn - __stubs_start)
     0xffff0018       irq                   b __real_stubs_start + (vector_IRQ - __stubs_start)
     0xffff001c       fiq                    b __real_stubs_start + (vector_FIQ - __stubs_start)
   
当有异常发生时,处理器会跳转到对应的0xffff0000起始的向量处取指令,然后,通过b指令散转到异常处理代码.因为ARM中b指令是相对跳转,而
且只有+/-32MB的寻址范围,所以把__stubs_start~__stubs_end之间的异常处理代码复制到了0xffff0200起始处.这
里可直接用b指令跳转过去,这样比使用绝对跳转(ldr)效率高。

-------------------------参考资料--------------------
1, 刘淼,嵌入式系统接口设计与Linux驱动程序开发,北京航天航空大学出版社,2006.
2, ARM Architecture Reference Manual, ARM limited,2000.