Chinaunix首页 | 论坛 | 博客
  • 博客访问: 173481
  • 博文数量: 108
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 1065
  • 用 户 组: 普通用户
  • 注册时间: 2011-03-29 08:56
文章分类

全部博文(108)

文章存档

2011年(11)

2010年(46)

2009年(29)

2008年(22)

我的朋友

分类: LINUX

2010-01-25 21:16:39





Understanding PIC GOT  

This article describers the PIC (Position Independant Code) and GOT (Global Offset Table) used in CellOS.

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.

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.

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.

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.

" 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 " 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 ,936()" say "lwz r0,0b-1b(r14)" is to load the with a value 0x0000f468; This 0x0000f468 is the real offset from CIA to the centor of GOT; Thus the "add ,," is actually to add the CIA with the offset to the centor of GOT, which in effect is to set to "sit" in the centor of GOT;

Note that I said several times of "centor of GOT", becasue I interpret that the can be added with a 16bit "signed" offset to access the contents around ; I call the GOT anchor. This is a little bit like the SDA (Small Data Area) concept.

So, now the is set to sit in the centor of a GOT area, where it can easily used to locate the "values" around it (+/- 32KB); is not changed across the execution from now on. Let's now remember the value of , which is 0x0000f468 + 0x000021bc = 0x11624; We need to use this 0x11624 to calculate the address of the entries in the GOT.

, that is "lwz ,-32748()", or "lwz ,GOT(transfer_to_handler)". Remember that = 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
= (.
阅读(396) | 评论(0) | 转发(0) |
0

上一篇:SGMII 接口 标准

下一篇:man 格式转化

给主人留下些什么吧!~~