Http://vm-kernel.org/cnblog/2010/04/mips-pic%E6%A6%82%E8%BF%B0/
Mips pic Overview
PIC is a very important concept. It is short for position independent code. it indicates that the code can be loaded to any place for execution. the advantage of this is that for shared libraries, we can keep only one copy in the memory, and all other programs can only call the code in this shared library. therefore, the code of the shared library does not depend on its address in the memory.
To achieve this goal, we need something that becomes a GOT (global offset table. in MIPS, the address of this table is saved in the gp register. in other architectures, there are other implementation methods. for more information, see the ABI manual. this table stores the global data and the address of the external function (such as printf) to be used. then, when calling these functions, the table items in the GOT table will be taken out first, and then jump. the Assembly statement is as follows:
40065c: 8f998038 lw t9,-32712 (gp)-> retrieve the GOT table item. The value is the address of the function to be called.
400660: 00000000 nop
400664: 0320f809 jalr t9-> jump to this function
400668: 00000000 nop
When we call the printf function, because the printf function is in libc, we do not know where the prinf function is located when the function is linked. in this case, you need to enter a stub value in the printf section of the GOT table. when will this stub value be changed to the real address of the printf function? There are two methods.
(1) method 1. when the program is loaded and executed, run the parse GOT table to find the table items similar to printf that do not know the specific address of the function. then go to libc or other lib to find the memory address of these functions. of course, if these lib has not been loaded into the memory, you must first load the Library to the memory. in this way, because all the necessary databases are in the memory, their addresses are determined. therefore, you can modify the GOT table of the program and write the addresses of these functions in the memory to the GOT table entry. in this case, the functions printf can be called through the GOT table.
It sounds good in this regard. But what is a fatal problem? During program loading, the address of all external functions of parse is required. if there are many of these functions, it will take a long time, during which it may go through numerous cache misses and so on. some functions are called in the program, but these functions are not actually run (for example, a function called after a judgment statement, the result of this judgment statement is not met during running ). therefore, this is a huge loss for performance. maybe after you call a program, you need to pour a cup of water to get back, and the program has not been loaded yet (of course, the example of Taiji terminal ). therefore, the actual situation is not to use this method, but to use method 2.
(2) method 2. this method is called fully dynamic linking. that is to say, the time for refilling the GOT table item is postponed from the loading time to the call time. that is to say, this table item will be refilled only when this external function is called, otherwise it will not be done. this method is similar to pushing a task to a non-essential task. Therefore, it is also called lazy linking. to implement lazy linking, apart from the GOT table, MIPS is called. MIPS. section of stubs. the table entry of the external function in the GOT table is pointed. MIPS. stubs. when the program runs, if it is the first time to call external functions such as printf, it will run to this section. the section then calls the dynamic connector to obtain the real address of printf and update the GOT table item. when you call this function again, you can directly call these external functions. the process is as follows:
The first time printf is called
The second call to printf
Another problem is that for PIC code, the location of its GOT table in the memory is not fixed. How can we get the location of the GOT table? The solution is that although the location of the GOT table is not fixed, the offset between the GOT table and the current function is fixed, and the address of the current function is placed in register t9. t9 + (the offset between the GOT and the current function) will not get the address of the GOT table. remember, this is a prerequisite. the premise is that, in a function of PIC code, t9 must be equal to the address of the current function in memory. this is guaranteed by the caller (caller) with the function. in addition, in the callee part of the called function, you need to save the gp value and restore the gp value when the function returns.
Therefore, you can compile the following code to obtain the current GOT table:
400640: 3c1c0002 lui gp, 0x2
400644: 279c82f0 addiu gp, gp,-32016-> gp = got-current function address
400648: 0399e021 Addu GP, GP, T9-> GP + T9 = got
In addition, for dynamic library functions, it must be pic, and for general applications, it does not need to be pic. in MIPS, the default application is not pic. to make the generated application pic, you must call the-mshared compilation parameter during compilation.
The following uses an example to illustrate the process of MIPS dynamic connection. the running environment in this example is Debian Lenny. the CPU is loongson 2f. the GCC version is GCC version 4.3.2 (Debian 4.3.2-1.1 ). the version of GDB is gnu gdb 6.8-Debian.
Source file:
Main-pic.c test-pic.c
Disassembly file:
Main-pic.o.asm test-pic.o.asm test-pic.asm
Compile command:
Gcc-C-mshared-G main-pic.c
Gcc-C-mshared-G test-pic.c
Gcc-O test-pic main-pic.o test-pic.o
Objdump-D main-pic.o> main-pic.o.asm
Objdump-D test-pic.o> test-pic.o.asm
Objdump-d test-PIC> test-pic.asm
Yajin @ kill-bill :~ /Dev $ gdb test GNU gdb 6.8-debian Copyright (C) 2008 Free Software Foundation, Inc. License GPLv3 +: gnu gpl version 3 or later This is free software: you are free to change and redistribute it. There is no warranty, to the extent permitted by law. Type "show copying" And "show warranty" for details. This GDB was configured as "mipsel-linux-gnu "... (Gdb) B main Breakpoint 1 at 0x400660: file main-pic.c, line 7. (Gdb) r Starting program:/home/yajin/dev/test-pic Breakpoint 1, main () at main-pic.c: 7 7 test (); (GDB) P/x $ PC $1 = 0x400660 (GDB) P/x $ GP $2 = 0x418930-> Gp = Location of got 0x418930 (GDB) P/x $ T9 $3 = 0x400640-> T9 = current function address (GDB) Si 0x00400664 7 test (); (GDB) P/x $ T9-> T9 = address of the test function to be called $4 = 0x400690 (GDB) Si 0x00400668 7 test (); (GDB) Si Test () at test. C: 4 4 { (GDB) P/x $ PC $5 = 0x400690 (GDB) Si 0x00400694 4 { (Gdb) si 0x00400698 4 { (Gdb) si 0x0040069c 4 { (Gdb) p/x $ gp $6 = 0x418930-> NO? Gp = 0x418930, which is the location of GOT (Gdb) p/x $ pc $7 = 0x40069c (Gdb) si 0x004006a0 4 { (Gdb) 0x004006a4 4 { (Gdb) 0x004006a8 4 { (Gdb) 0x004006ac 4 { (Gdb) 5 printf ("hello world first/n "); (Gdb) 0x004006b4 5 printf ("Hello World First/N "); (GDB) 0x004006b8 5 printf ("Hello World First/N "); (GDB) P/x $ GP $8 = 0x418930 (GDB) Si 0x004006bc 5 printf ("Hello World First/N "); (GDB) P/x $ GP $9 = 0x418930 (GDB) P/x $ PC $10 = 0x4006bc (GDB) Si 0x004006c0 5 printf ("Hello World First/N "); (GDB) P/x $ T9 $11 = 0x400840-> here is the position of. MIPS. stubs to be jumped. (GDB) Si 0x004006c4 5 printf ("Hello World First/N "); (Gdb) si-> jump to. MIPS. stubs 0x00400840 in puts () Current language: auto; currently asm (Gdb) si 0x00400844 in puts () (Gdb) p/x $ pc $12 = 0x400844 (Gdb) p/x $ t9 $13 = 0x2aabf4d0 (Gdb) si 0x00400848 in puts () (Gdb) si-> jump to dynamic Connector 0x2aabf4d0 in _ dl_runtime_resolve () from/lib/ld. so.1 (Gdb) B * 0x4006cc Breakpoint 2 at 0x4006cc: file test. c, line 5. (Gdb) c Continuing. Hello world first Breakpoint 2, 0x004006cc in test () at test-pic.c: 5 5 printf ("hello world first/n "); Current language: auto; currently c (Gdb) p/x $ gp $14 = 0x2ac55950 (Gdb) p/x $ pc $15 = 0x4006cc (Gdb) si 0x004006d0 5 printf ("hello world first/n "); (Gdb) p/x $ gp $16 = 0x418930-> the first printf ends, and the gp is restored. (Gdb) si 6 printf ("hello world second/n "); (Gdb) p/x $ pc $17 = 0x4006d4 (Gdb) si 0x004006d8 6 printf ("hello world second/n "); (Gdb) 0x004006dc 6 printf ("hello world second/n "); (Gdb) 0x004006e0 6 printf ("hello world second/n "); (Gdb) si 0x004006e4 6 printf ("hello world second/n "); (Gdb) p/x $ t9-> directly to printf for the second time, not to. MIPS. Stubs $18 = 0x2ab444d0 (Gdb) p/x $ pc $19 = 0x4006e4 (Gdb) si 0x004006e8 6 printf ("hello world second/n "); (Gdb) p/x $ pc $20 = 0x4006e8 (Gdb) si |
The example in this article is written in c. for related compilation, refer to the following connection. there are also some macros, such as _ gp_disp ,__ gnu_local_gp and cpload $25. It is not difficult to understand this article.
Refer:
[1] Some new tricks for better performance in MIPS-Linux
[2] Position-Independent Coding in Assembly Language
[3] PIC CODE
Tags: loongson, MIPS, PIC