免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1820 | 回复: 0

Understanding PIC GOT (ZZ) [复制链接]

论坛徽章:
0
发表于 2010-01-25 21:16 |显示全部楼层






















.artifactcomment, #commentform {
margin: .5em 0 0 0;
padding: .3em 0 .3em .3em;
border-top: 3px solid #c3d9ff;
}
#commentform { padding-top: 1em; }
.delcom { background: #e8e8e8 }
.commentcontent { margin: 1em 0;}





















Understanding PIC GOT
  




This article describers the PIC (Position Independant Code) and GOT (Global Offset Table) used in CellOS.
Introduction
Sometimes,
we have to write PIC (Position Independant Code). In the PIC code, we
sometimes need to refer to some absolute symbols. But PIC code can not
itself contain absolute virtual addresses. So GOT is used to solve this
issue.
CellOS uses the same
mechanism to solve the PIC code refering to absolute addresses issues.
The following article is to undertand the GOT details with a "reverse
engineering" way.
DetailsThe theory for PIC and GOT
The following theory section is copied from the :
When
the system creates a process image, the executable file portion of the
process has fixed addresses and the system chooses shared object
library virtual addresses to avoid conflicts with other segments in the
process. To maximize text sharing, shared objects conventionally use
position-independent code, in which instructions contain no absolute
addresses. Shared object text segments can be loaded at various virtual
addresses without having to change the segment images. Thus multiple
processes can share a single shared object text segment, even if the
segment reside at a different virtual address in each process.
Position-independent code relies on two techniques:
  • Control
    transfer instructions hold addresses relative to the Effective Address
    (EA) or use registers that hold the transfer address. An EA-relative
    branch computes its destination address in terms of the current EA, not
    relative to any absolute address.
  • When
    the program requires an absolute address, it computes the desired
    value. Instead of embedding absolute addresses in instructions (in the
    text segment), the compiler generates code to calculate an absolute
    address (in a register or in the stack or data segment) during
    execution.

Because
the PowerPC Architecture provides EA-relative branch instructions and
also branch instructions using registers that hold the transfer
address, compilers can satisfy the first condition easily.
A
"Global Offset Table," or GOT, provides information for address
calculation. Position independent object files (executable and shared
object files) have a table in their data segment that holds addresses.
When the system creates the memory image for an object file, the table
entries are relocated to reflect the absolute virtual address as
assigned for an individual process. Because data segments are private
for each process, the table entries can change—unlike text segments,
which multiple processes share.
Position-independent
code cannot, in general, contain absolute virtual addresses. Global
offset tables hold absolute addresses in private data, thus making the
addresses available without compromising the position-independence and
sharability of a program’s text. A program references its global offset
table using position-independent addressing and extracts absolute
values, thus redirecting position-independent references to absolute
locations.
When the
dynamic linker creates memory segments for a loadable object file, it
processes the relocation entries, some of which will be of type
R_PPC_GLOB_DAT, referring to the global offset table. The dynamic
linker determines the associated symbol values, calculates their
absolute addresses, and sets the global offset table entries to the
proper values. Although the absolute addresses are unknown when the
link editor builds an object file, the dynamic linker knows the
addresses of all memory segments and can thus calculate the absolute
addresses of the symbols contained therein.
A
global offset table entry provides direct access to the absolute
address of a symbol without compromising position-independence and
sharability. Because the executable file and shared objects have
separate global offset tables, a symbol may appear in several tables.
The dynamic linker processes all the global offset table relocations
before giving control to any code in the process image, thus ensuring
the absolute addresses are available during execution.
The
dynamic linker may choose different memory segment addresses for the
same shared object in different programs; it may even choose different
library addresses for different executions of the same program.
Nonetheless, memory segments do not change addresses once the process
image is established. As long as a process exists, its memory segments
reside at fixed virtual addresses.
A global offset table’s format and interpretation are processor specific. For PowerPC, the symbol GLOBAL_OFFSET_TABLE
may be used to access the table. The symbol may reside in the middle of
the .got section, allowing both positive and negative "subscripts" into
the array of addresses. Four words in the global offset table are
reserved:
  • The word at GLOBAL_OFFSET_TABLE-1 shall contain a blrl instruction (see the

text relating to Figure 3-33, "Prologue and Epilogue Sample Code").
  • The word at GLOBAL_OFFSET_TABLE0 is set by the link editor to hold the address of
the dynamic structure, referenced with the symbol DYNAMIC.
This
allows a program, such as the dynamic linker, to find its own dynamic
structure without having yet processed its relocation entries. This is
especially important for the dynamic linker, because it must initialize
itself without relying on other programs to relocate its memory image.
  • The word at GLOBAL_OFFSET_TABLE1 is reserved for future use.
  • The word at GLOBAL_OFFSET_TABLE2 is reserved for future use.
The global offset table resides in the ELF .got section. The implementation for GOT in CellOS1. The various macros used to define GOT
The following code defines the GOT entries;  
/***************************************************************************
*
* These definitions simplify the ugly declarations necessary for GOT
* definitions.
*
* Stolen from prepboot/bootldr.h, (C) 1998 Gabriel Paubert, paubert@iram.es
*
* Uses r14 to access the GOT
*/
#define START_GOT                               \
        .section        ".got2","aw";           \
.LCTOC1 = .+32768
#define END_GOT                                 \
        .text
#define GET_GOT                                 \
        bl      1f                      ;       \
        .text   2                       ;       \
0:      .long   .LCTOC1-1f              ;       \#offset from center of GOT to CIA#
        .text                           ;       \
1:      mflr    r14                     ;       \#Get CIA#
        lwz             r0,0b-1b(r14)   ;       \#Get the offset#
        add             r14,r0,r14              ;
#define GOT_ENTRY(NAME)         .L_ ## NAME = . - .LCTOC1 ; .long NAME
#define GOT(NAME)               .L_ ## NAME (r14)
/*
* Set up GOT: Global Offset Table
*
* Use r14 to access the GOT
*/
START_GOT
GOT_ENTRY(_GOT2_TABLE_)
GOT_ENTRY(_FIXUP_TABLE_)
GOT_ENTRY(_start)
GOT_ENTRY(_start_of_vectors)
GOT_ENTRY(_end_of_vectors)
GOT_ENTRY(transfer_to_handler)
GOT_ENTRY(__init_end)
GOT_ENTRY(_end)
GOT_ENTRY(__bss_start)
END_GOT
2. How GOT is actually setup in runtime code?
In the cellEntry code, before it transfers to the normal C code, it calls GET_GOT.
        GET_GOT                         /* initialize GOT access        */
        /* NEVER RETURNS! */
        bl      cellMain
The disasm of the above code section is :
    0x000021b8 cellosEntry+184>:   bl      0x21bc cellosEntry+188> #bl 1f
    ####Here is a compiler trick, it is described below!!###
    0x000021bc cellosEntry+188>:   mflr    r14            #Get CIA,r14=0x000021bc
    0x000021c0 cellosEntry+192>:   lwz     r0,936(r14)    #load the offset into r0
    0x000021c4 cellosEntry+196>:   add     r14,r0,r14     #add r0=0xf468
    0x000021c8 cellosEntry+200>:   bl      0x4cb0
Even it is not strictly related, we describe the compiler trick that is shown above:
We have seen that the GET_GOT is written like this:
#define GET_GOT                                 \
        bl      1f                      ;       \
        .text   2                       ;       \
0:      .long   .LCTOC1-1f              ;       \#offset from center of GOT to CIA#
        .text                           ;       \
1:      mflr    r14                     ;       \#Get CIA#
        lwz             r0,0b-1b(r14)   ;       \#Get the offset#
        add             r14,r0,r14      
If
there is no ".text 2" in the above macro, then we can imagine there is
an immediate value defined just between the "bl 1f" and "1: mflr
r14
"
instrcution; However, the ".text 2" seems to trigger the compiler to
move the immediate value to somewhere else, not between the two
instructions. Should there is no ".text 2", the immediate value were to
be put in between the two intructions, thus the (0f - 1f) were to be
-4;
However, with the ".text 2", the (0f - 1f) becomes 936 (in
this compilation, and can vary with different compilations if you have
other code added or changed), because the immediate value has been
"moved" to a position higer than the CIA.
The (0f - 1f), in
this case, the value 936, is an intermediate offset, at that offset (to
the CIA), stores the real offset value from the centor of the GOT to
CIA; In our case, the CIA for the "1: mflr
r14
" is 0x000021bc; 0x000021bc + 936 = 0x2564; check the disasm and debug the code, you  will find the following:
(gdb) x/xw 0x2564 #to display the memory at 0x2564
0x2564 in32+8>:        0x0000f468
(gdb)
So, the "lwz
r0
,936(
r14
)" say "lwz  r0,0b-1b(r14)" is to load the
r0
with a value 0x0000f468; This 0x0000f468 is the real offset from CIA to the centor of GOT; Thus the "add
r14
,
r0
,
r14
" is actually to add the CIA with the offset to the centor of GOT, which in effect is to set
r14
to "sit" in the centor of GOT;
Note that I said several times of "centor of GOT", becasue I interpret that the
r14
can be added with a 16bit "signed" offset to access the contents around
r14
; I call
r14
the GOT anchor. This is a little bit like the SDA (Small Data Area) concept.
So, now the
r14
is set to sit in the centor of a GOT area, where it can easily used to locate the "values" around it (+/- 32KB);
r14
is not changed across the execution from now on. Let's now remember the value of
r14
, which is 0x0000f468 + 0x000021bc = 0x11624; We need to use this 0x11624 to calculate the address of the entries in the GOT.
3. How GOT is used to access the absolute symbols ?
Let's find a case where the GOT is used in CellOS.
When the system is running and an interrupt happens, interrupt handling is run, where GOT is used:
/*
* Exception vectors.
*
* The data words for `hdlr' and `int_return' are initialized with
* OFFSET values only; they must be relocated first before they can
* be used!
*/
#define STD_EXCEPTION(n, label, hdlr)                           \
        . = n;                                                  \
label:                                                          \
        EXCEPTION_PROLOG(SRR0, SRR1);                           \
        lwz     r3,GOT(transfer_to_handler);                    \#GOT#
        mtlr    r3;                                             \
        addi    r3,r1,STACK_FRAME_OVERHEAD;                     \
        li              r20,MSR_KERNEL;                         \
        rlwimi  r20,r23,0,25,25;                                \
        blrl;                                                   \
.L_ ## label :                                                  \
        .long   hdlr - _start + _START_OFFSET;                  \
        .long   int_return - _start + _START_OFFSET
The disasm code for the a bove code section looks like this:
00003568 PIT>:
    3568:       7e 90 43 a6     mtsprg  0,r20
    356c:       7e b1 43 a6     mtsprg  1,r21
    3570:       7e 80 00 26     mfcr    r20
    3574:       3a a1 ff 00     addi    r21,r1,-256
    3578:       92 95 00 a8     stw     r20,168(r21)
    357c:       92 d5 00 68     stw     r22,104(r21)
    3580:       92 f5 00 6c     stw     r23,108(r21)
    3584:       7e 90 42 a6     mfsprg  r20,0
    3588:       92 95 00 60     stw     r20,96(r21)
    358c:       7e d1 42 a6     mfsprg  r22,1
    3590:       92 d5 00 64     stw     r22,100(r21)
    3594:       7e 88 02 a6     mflr    r20
    3598:       92 95 00 a0     stw     r20,160(r21)
    359c:       7e c9 02 a6     mfctr   r22
    35a0:       92 d5 00 9c     stw     r22,156(r21)
    35a4:       7e 81 02 a6     mfxer   r20
    35a8:       92 95 00 a4     stw     r20,164(r21)
    35ac:       7e 95 f2 a6     mfdear  r20
    35b0:       92 95 00 b4     stw     r20,180(r21)
    35b4:       7e da 02 a6     mfsrr0  r22
    35b8:       7e fb 02 a6     mfsrr1  r23
    35bc:       90 15 00 10     stw     r0,16(r21)
    35c0:       90 35 00 14     stw     r1,20(r21)
    35c4:       90 55 00 18     stw     r2,24(r21)
    35c8:       90 35 00 00     stw     r1,0(r21)
    35cc:       7e a1 ab 78     mr      r1,r21
    35d0:       90 75 00 1c     stw     r3,28(r21)
    35d4:       90 95 00 20     stw     r4,32(r21)
    35d8:       90 b5 00 24     stw     r5,36(r21)
    35dc:       90 d5 00 28     stw     r6,40(r21)
    35e0:       80 6e 80 14     lwz     r3,-32748(r14) #GOT, r14 = 0x11624
    35e4:       7c 68 03 a6     mtlr    r3
    35e8:       38 61 00 10     addi    r3,r1,16
    35ec:       3a 80 10 00     li      r20,4096
    35f0:       52 f4 06 72     rlwimi  r20,r23,0,25,25
    35f4:       4e 80 00 21     blrl
    35f8:       00 00 5a cc     .long 0x5acc
    35fc:       00 00 23 38     .long 0x2338
So, we saw there is a simple usage of
r14
, that is "lwz  
r3
,-32748(
r14
)", or "lwz       
r3
,GOT(transfer_to_handler)". Remember that
r14
= 0x11624, this instrcution is to load from EA = 0x11624 - 32748 = 0x9638.
So what is the value in 0x9638?
(gdb) x/xw 0x9638
0x9638 _GOT2_TABLE_+56>:       0x0000229c
(gdb)
OK, we see something called "GOT2_TABLE", seems familar? Yes, GOT! Looking back in this  article, there is (yes, I copied it twice, becasue the space is for free :-))
/*
* Set up GOT: Global Offset Table
*
* Use r14 to access the GOT
*/
START_GOT
GOT_ENTRY(_GOT2_TABLE_)
GOT_ENTRY(_FIXUP_TABLE_)
GOT_ENTRY(_start)
GOT_ENTRY(_start_of_vectors)
GOT_ENTRY(_end_of_vectors)
GOT_ENTRY(transfer_to_handler)
GOT_ENTRY(__init_end)
GOT_ENTRY(_end)
GOT_ENTRY(__bss_start)
END_GOT
The GOT_ENTRY is to define an entry "around" the centor of GOT (the GOT anchor).
#define GOT_ENTRY(NAME)         .L_ ## NAME = . - .LCTOC1 ; .long NAME
The actual GOT table looks like this:
00009600 _GOT2_TABLE_>:
    9600:       00 00 96 00     .long 0x9600
    9604:       00 00 96 48     .long 0x9648
    9608:       00 00 46 68     .long 0x4668
    960c:       00 00 26 68     .long 0x2668
    9610:       00 00 46 00     .long 0x4600
    9614:       00 00 48 04     .long 0x4804
    9618:       00 00 9c 00     .long 0x9c00
    961c:       00 60 d0 00     .long 0x60d000
    9620:       00 00 9c 00     .long 0x9c00
    9624:       00 00 96 00     .long 0x9600
    9628:       00 00 96 48     .long 0x9648
    962c:       00 00 21 00     .long 0x2100
    9630:       00 00 01 00     .long 0x100
    9634:       00 00 20 98     .long 0x2098
    9638:       00 00 22 9c     .long 0x229c
    963c:       00 00 9c 00     .long 0x9c00
    9640:       00 60 d0 00     .long 0x60d000
    9644:       00 00 9c 00     .long 0x9c00
Disassembly of section .data:
So, at "9638:        00 00 22 9c         .long 0x229c", there stores a value 0x229c;
(gdb) x/xw 0x229c
0x229c :   0x92d50090
(gdb)
Right, that is what we want! The address of the function transfer_to_handler is stored in the GOT entry.
So
now we have a very clear understanding to the GOT. It is actually a
"jumping table", through which an indirect addressing to the absolute
addresses are performed. This technic is is mostly used for operating
systems to load exe images using shared objects. For CellOS, derived
from u-boot, it simply severs as a way to locate some absolute symbols.
4. Note for the ones curious with the question : where GOT2_TABLE comes from?
In the linker script,there is a section:
  .reloc   :
  {
    *(.got)
    _GOT2_TABLE_ = .;
    *(.got2)
    _FIXUP_TABLE_ = .;
    *(.fixup)
  }
  __got2_entries = (_FIXUP_TABLE_ - _GOT2_TABLE_) >>2;
  __fixup_entries = (. <span class%3

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/39383/showart_2160346.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

SACC2022中国系统架构师大会

2022年1月13日~15日,由IT168旗下ITPUB企业社区平台主办的第十四届中国系统架构师大会(SACC2022)将在云端进行网络直播。承续上届“数字转型 架构重塑”核心主题,按照技术主线分为架构线(传统架构-分布式架构-微服务架构-低代码架构)、运维篇(DevOps、AIOps、高效运维、数据安全等)、金融行业篇、前沿技术篇、大数据篇等,云集国内CTO、研发总监、高级系统架构师、开发工程师和IT经理等技术人群,力争为各路豪杰奉献一场技术的饕餮盛宴。

大会官网
  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP