I see nobody has answered this question, so I think I'll try.
I'll try to show how this works by means of a practical example. Here is some C code, which has an artificial but purposeful mix of external global functions and data -- the bread and butter of relocations.
/* hello.c */
char* hello = "hello";
/* say.c */
#include "stdio.h"
extern char* hello;
void say(void){
printf(hello);
}
/* main.c */
extern void say(void);
void please_say(void){
say();
}
int main(void){
please_say();
return 0;
}
Now the usual way to get a shared object/library would be to compile each C file with -fPIC, and link the lot with -shared. After we do that, we can use readelf to examine the relocations. Something like this:
gcc -fPIC -c *.c
gcc -shared -o libtemp.so *.o
readelf -r libtemp.so
The relocation data is as follows:
Relocation section '.rel.dyn' at offset 0x34c contains 6 entries:
Offset Info Type Sym.Value Sym. Name
000016dc 00000008 R_386_RELATIVE
000016e0 00000008 R_386_RELATIVE
000016ac 00000106 R_386_GLOB_DAT 00000000 __gmon_start__
000016b0 00000206 R_386_GLOB_DAT 00000000 _Jv_RegisterClasses
000016b4 00000d06 R_386_GLOB_DAT 000016e0 hello
000016b8 00000406 R_386_GLOB_DAT 00000000 __cxa_finalize
Relocation section '.rel.plt' at offset 0x37c contains 5 entries:
Offset Info Type Sym.Value Sym. Name
000016c8 00000107 R_386_JUMP_SLOT 00000000 __gmon_start__
000016cc 00000507 R_386_JUMP_SLOT 000004fc please_say
000016d0 00000807 R_386_JUMP_SLOT 00000540 say
000016d4 00000307 R_386_JUMP_SLOT 00000000 printf
000016d8 00000407 R_386_JUMP_SLOT 00000000 __cxa_finalize
The R_386_GLOB_DAT item for hello is a GOT entry. Similarly, the R_386_JUMP_SLOT items for say, please_say and printf are PLT entries. They come from the use of position independent code, not the fact that we have made a shared object.
After doing the same build process without -fPIC, we get different relocations. So
gcc -c *.c
gcc -shared -o libtemp.so *.o
readelf -r libtemp.so
gives us
Relocation section '.rel.dyn' at offset 0x34c contains 9 entries:
Offset Info Type Sym.Value Sym. Name
00001674 00000008 R_386_RELATIVE
00001678 00000008 R_386_RELATIVE
000004d3 00000802 R_386_PC32 000004f0 say
000004e0 00000502 R_386_PC32 000004cc please_say
000004f7 00000d01 R_386_32 00001678 hello
000004ff 00000302 R_386_PC32 00000000 printf
00001654 00000106 R_386_GLOB_DAT 00000000 __gmon_start__
00001658 00000206 R_386_GLOB_DAT 00000000 _Jv_RegisterClasses
0000165c 00000406 R_386_GLOB_DAT 00000000 __cxa_finalize
Relocation section '.rel.plt' at offset 0x394 contains 2 entries:
Offset Info Type Sym.Value Sym. Name
0000166c 00000107 R_386_JUMP_SLOT 00000000 __gmon_start__
00001670 00000407 R_386_JUMP_SLOT 00000000 __cxa_finalize
Now the shared object has familiar relocations for all the definitions. There is an absolute relocation of hello, and PC relative relocations for the functions.
What does this mean? Well the are GOT and PLT tables are still there. There are two important things to note. The first is that there are no GOT or PLT entries for the compiled code. The second is that the GOT and PLT tables are still needed. They are being used for initialisation and cleanup (possibly for the standard library). Since you are using a custom ELF loader, it would probably be advisable to implement some basic support for GOT and PLT entries, even if your main application does standard relocations instead.
Your application will then pay the price of relocation, but not of position independence.