4. ELF File Format
b.c abc() { } gcc -c b.c ls -l b.o -rw-r--r-- 1 root root 728 Nov 14 13:14 b.o
--------------------------------------------------------------------
Here's our first program. Ambitious it isn't. It's got no main(), or anything else except an empty function named abc(). Notice that we don't try to link this program for doing so would fill our screen with a long list of errors. All we do is compile the program. Check out the file size.
This program will be used by our other, slightly more complete program.
1 a.c #include <stdio.h> main() { FILE *fp; fp = fopen("b.o","r"); printf("%x ",fgetc(fp)); printf("%c",fgetc(fp)); printf("%c",fgetc(fp)); printf("%c",fgetc(fp)); printf("\n"); } gcc a.c -o a ../a 7f ELF --------------------------------------------------------------
Right. This program is pretty simple as well. All we're doing here is opening up our file, b.o and printing out the first 4 bytes. Nearly all-binary files have some sort of unique identifier, called a magic number, embedded in them. Java .class files start off with CAFE BABE. DOS .exe files start with an MZ while under Windows, it's PE00. ELF files will always start off with 7fELF. Nothing to it.
2 a.c #include <stdio.h> main() { FILE *fp; fp = fopen("b.o","r"); fseek(fp,4,0); printf("%d ",fgetc(fp)); printf("%d ",fgetc(fp)); printf("%d ",fgetc(fp)); printf("\n"); } Output 1 1 1
-------------------------------------------------------------
Here we're going to byte number 5 and reading three bytes from there. These give us some additional information about the file. They're switches, so 0 means OFF or NO and 1 means ON or YES. The first one tells us whether the file is 32 or 64 bit based. The second tells us about its endianess and the last is the version number.
3 a.c #include <stdio.h> char *class[]={"Invalid Class","32-bit objects", "64-bit objects"}; char *endian[] = {"Invalid", "Little Endian","Big Endian"}; char *version[] = {"Invalid Version","Current Version"}; FILE *fp; main() { int i; fp = fopen("b.o","r"); fseek(fp,4,0); i = fgetc(fp); printf("%s \n",class[i]); i = fgetc(fp); printf("%s \n",endian[i]); i = fgetc(fp); printf("%s \n",version[i]); } Output 32-bit objects Little Endian Current Version
------------------------------------------------------------------
Nothing spectacular here. We've just made the program a little friendlier. We're just printing out text strings instead of numbers.
4 a.c #include <stdio.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elfhdr e; char *class[]={"Invalid Class","32-bit objects", "64-bit objects"}; char *endian[] = {"Invalid", "Little Endian","Big Endian"}; char *version[] = {"Invalid Version","Current Version"}; char *filetype[] = {"None","Rel Obj","Executable","Dynamic","Core","Num"}; char *machine[] = { "None","WE32100","SPARC","80386","MK68000","MK88000","80486","80860","MIPS"}; FILE *fp; main() { fp = fopen("b.o","r"); fread(&e,52,1,fp); printf("%x %c%c%c %s %s %s\n",e.e_ident[0],e.e_ident[1],e.e_ident[2], e.e_ident[3],class[e.e_ident[4]],endian[e.e_ident[5]],version[e.e_ident[6]]); printf("Type :%d %s \n",e.e_type,filetype[e.e_type]); printf("Machine :%d %s \n",e.e_machine,machine[e.e_machine]); printf("Version :%d %s \n",e.e_version,version[e.e_version]); printf("Entry Point :%x\n",e.e_entry); printf("Size of Initial Structure :%d\n",e.e_ehsize); printf("Flags :%d\n",e.e_flags); printf("Program Header Offset :%d\n",e.e_phoff); printf("PHO : Structure Size %d No. of Entries %d\n",e.e_phentsize,e.e_phnum); printf("Section Header Offset :%d\n",e.e_shoff); printf("SHO: Structure Size %d No. of Entries %d\n",e.e_shentsize,e.e_shnum); printf("String Table Section Number :%d\n",e.e_shstrndx); } Output 7f ELF 32-bit objects Little Endian Current Version Type :1 Rel Obj Machine :3 80386 Version :1 Current Version Entry Point :0 Size of Initial Structure :52 Flags :0 Program Header Offset :0 PHO : Structure Size 0 No. of Entries 0 Section Header Offset :200 SHO: Structure Size 40 No. of Entries 9 String Table Section Number :6
Okay, we're getting a little more ambitious here. We create a structure e which looks like the structure tag elfhdr and then read in the first 52 bytes of the ELF file into it. We then systematically print out the members of the structure. So fine, it looks a little messy, but that's the price one has to pay for intelligible output! The code itself is rather obvious and requires no real explanation.
Notice that all the members of e_ident[16] are not fully utilized. They're left blank for future growth in the standard.
A quick run through the various members.
First we have the magic number and then the three bytes we've already covered. We're then told that this is a Relocatable Object (lots more on that later), that the file is for the 80386 processor and that the version number is 1. The entry point is 0 while the size of the initial structure is 52 (but we already knew that). The flags are currently set to 0 while the program header offset is 0 right now. It'll change later when we link the code. The program header structure size is 0 right now and so are the number of entries. The section header starts at 200 and has 9 entries, each 40 bytes large. The string table is the 6th section. More on all of this later.
c.c #include <stdio.h> #include <sys/stat.h> FILE *fp; struct stat st; main() { fp = fopen("b.o","r"); printf("Fileno %d\n",fileno(fp)); fstat(fileno(fp),&st); printf("Size %d\n",st.st_size); } gcc -o c c.c ../c Fileno 3 Size 728 ls -l b.o -rw-r--r-- 1 root root 728 Nov 14 13:14 b.o -------------------------------------------------------------------
Here's a small diversion. We're using fstat() here to extract the file size. fstat() needs the file number and we use fileno() to get that. Then we print out the file size.
5 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; char *class[]={"Invalid Class","32-bit objects", "64-bit objects"}; char *endian[] = {"Invalid", "Little Endian","Big Endian"}; char *version[] = {"Invalid Version","Current Version"}; char *filetype[] = {"None","Rel Obj","Executable","Dynamic","Core","Num"}; char *machine[] = { "None","WE32100","SPARC","80386","MK68000","MK88000","80486","80860","MIPS"}; FILE *fp;struct stat st; struct elfhdr *e; char *p; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; printf("%x %c%c%c %s %s %s\n",e->e_ident[0],e->e_ident[1],e->e_ident[2], e->e_ident[3],class[e->e_ident[4]],endian[e->e_ident[5]],version[e->e_ident[6]]); printf("Type :%s \n",filetype[e->e_type]); printf("Machine :%s \n",machine[e->e_machine]); printf("Version :%s \n",version[e->e_version]); printf("Entry Point :%x\n",e->e_entry); printf("Size of Initial Structure :%d\n",e->e_ehsize); printf("Flags :%d\n",e->e_flags); printf("Program Header Offset :%d\n",e->e_phoff); printf("PHO : Structure Size %d No. of Entries %d\n",e->e_phentsize,e->e_phnum); printf("Section Header Offset :%d\n",e->e_shoff); printf("SHO: Structure Size %d No. of Entries %d\n",e->e_shentsize,e->e_shnum); printf("String Table Section Number :%d\n",e->e_shstrndx); } Output 7f ELF 32-bit objects Little Endian Current Version Type :Rel Obj Machine :80386 Version :Current Version Entry Point :0 Size of Initial Structure :52 Flags :0 Program Header Offset :0 PHO : Structure Size 0 No. of Entries 0 Section Header Offset :200 SHO: Structure Size 40 No. of Entries 9 String Table Section Number :6 --------------------------------------------------------
Things are getting a little more complicated. We're using malloc() to dynamically allocate memory as we need it. It's better programming practice. No other difference.
6 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; FILE *fp;struct stat st; struct elfhdr *e; struct elf_shdr *s; char *p; int i; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; printf("Section Header Offset :%d\n",e->e_shoff); printf("SHO: Structure Size %d No. of Entries %d\n",e->e_shentsize,e->e_shnum); printf("String Table Section Number :%d\n",e->e_shstrndx); s = p + e->e_shoff; printf("\n"); printf("%-3s %-15s %-5s %-5s %-8s %-8s %-5s %-5s %-5s %-5s %-6s\n", "No","Name","Type","Flags","Address","Offset","Size","Link","Info","Align","Esize"); for(i = 0; i < e->e_shnum; i++) { printf("%-3d %-15d %-5d %-5d %-8x %-8d %-5d %-5d %-5d %-5d %-6d\n", i,s->name,s->type,s->flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); s++; } } Output Section Header Offset :200 SHO: Structure Size 40 No. of Entries 9 String Table Section Number :6 No Name Type Flags Address Offset Size Link Info Align Esize 0 0 0 0 0 0 0 0 0 0 0 1 27 1 6 0 52 5 0 0 4 0 2 33 1 3 0 60 0 0 0 4 0 3 39 8 3 0 60 0 0 0 4 0 4 44 7 0 0 60 20 0 0 1 0 5 50 1 0 0 80 61 0 0 1 0 6 17 3 0 0 141 59 0 0 1 0 7 1 2 0 0 560 144 8 8 4 16 8 9 3 0 0 704 24 0 0 1 0 ----------------------------------------------------------
We're going a little deeper this time. What we're doing is going to the section headers and we're printing them out. Nothing too spectacular at this point. Just ogle at the numbers and drool. Take note of the for() loop where we do the actual printing. Similar code does the work of printing out other parts of the file as well.
7 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; FILE *fp;struct stat st; struct elfhdr *e; struct elf_shdr *s,*sh; char *p,*str; int i; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; printf("Section Header Offset :%d\n",e->e_shoff); printf("SHO: Structure Size %d No. of Entries %d\n",e->e_shentsize,e->e_shnum); printf("String Table Section Number :%d\n",e->e_shstrndx); sh = p + e->e_shoff; s= sh; printf("\n"); printf("%-3s %-15s %-5s %-5s %-8s %-8s %-5s %-5s %-5s %-5s %-6s\n", "No","Name","Type","Flags","Address","Offset","Size","Link","Info","Align","Esize"); for(i = 0; i < e->e_shnum; i++) { printf("%-3d %-15d %-5d %-5d %-8x %-8d %-5d %-5d %-5d %-5d %-6d\n", i,s->name,s->type,s->flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); s++; } s = sh + e->e_shstrndx; str = p + s->offset; for(i=0; i < s->size; i++) { if ( i%6 == 0) printf("\n"); if (str[i] == 0) printf("%2d) %3d 0 ",i,str[i]); else printf("%2d) %3d %2c ",i,str[i],str[i]); } printf("\n"); } Output Section Header Offset :200 SHO: Structure Size 40 No. of Entries 9 String Table Section Number :6 No Name Type Flags Address Offset Size Link Info Align Esize 0 0 0 0 0 0 0 0 0 0 0 1 27 1 6 0 52 5 0 0 4 0 2 33 1 3 0 60 0 0 0 4 0 3 39 8 3 0 60 0 0 0 4 0 4 44 7 0 0 60 20 0 0 1 0 5 50 1 0 0 80 61 0 0 1 0 6 17 3 0 0 141 59 0 0 1 0 7 1 2 0 0 560 144 8 8 4 16 8 9 3 0 0 704 24 0 0 1 0 0) 0 0 1) 46 . 2) 115 s 3) 121 y 4) 109 m 5) 116 t 6) 97 a 7) 98 b 8) 0 0 9) 46 . 10) 115 s 11) 116 t 12) 114 r 13) 116 t 14) 97 a 15) 98 b 16) 0 0 17) 46 . 18) 115 s 19) 104 h 20) 115 s 21) 116 t 22) 114 r 23) 116 t 24) 97 a 25) 98 b 26) 0 0 27) 46 . 28) 116 t 29) 101 e 30) 120 x 31) 116 t 32) 0 0 33) 46 . 34) 100 d 35) 97 a 36) 116 t 37) 97 a 38) 0 0 39) 46 . 40) 98 b 41) 115 s 42) 115 s 43) 0 0 44) 46 . 45) 110 n 46) 111 o 47) 116 t 48) 101 e 49) 0 0 50) 46 . 51) 99 c 52) 111 o 53) 109 m 54) 109 m 55) 101 e 56) 110 n 57) 116 t 58) 0 0 ----------------------------------------------------------------------
Deeper and deeper. Here we jump down to the 6th section header, called the string table and print out it's contents. The string table contains, not surprisingly, strings and if you look carefully, you'll see that those strings are the names of the different sections. They all begin with a dot and are null terminated.
8 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; FILE *fp;struct stat st; struct elfhdr *e; struct elf_shdr *s,*sh; char *p,*str; int i; char flags[4]; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; printf("Section Header Offset :%d\n",e->e_shoff); printf("SHO: Structure Size %d No. of Entries %d\n",e->e_shentsize,e->e_shnum); printf("String Table Section Number :%d\n",e->e_shstrndx); sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n"); printf("%-2s %-12s %-10s %-5s %-8s %-8s %-5s %-5s %-5s %-5s %-5s\n", "No","Name","Type","Flg","Address","Offset","Size","Link","Info","Align","Esize"); for(i = 0; i < e->e_shnum; i++) { printf("%-2d %-12s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); s++; } } Output Section Header Offset :200 SHO: Structure Size 40 No. of Entries 9 String Table Section Number :6 No Name Type Flg Address Offset Size Link Info Align Esize 0 NULL 0 0 0 0 0 0 0 1 .text PROGBITS AE 0 52 5 0 0 4 0 2 .data PROGBITS WA 0 60 0 0 0 4 0 3 .bss NOBITS WA 0 60 0 0 0 4 0 4 .note NOTE 0 60 20 0 0 1 0 5 .comment PROGBITS 0 80 61 0 0 1 0 6 .shstrtab STRTAB 0 141 59 0 0 1 0 7 .symtab SYMTAB 0 560 144 8 8 4 16 8 .strtab STRTAB 0 704 24 0 0 1 0 -----------------------------------------------
What a spectacular break with tradition. We're getting positively user-friendly here! We're going to the string table and printing out the names. We're also placing the appropriate letter instead of a number in the flag field
Now about those flags. They're actually a bit field of which only the first 3 bits are currently being used. When the first bit is on, the section is writable (e.g. the data section), when the second one is on, the section is to be allocated space in memory and when the third bit is on, the section is executable (e.g. the ,text section). We bitwise AND them to figure out if they're on.
9 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; FILE *fp;struct stat st; struct elfhdr *e; struct elf_shdr *s,*sh; char *p,*str; int i,j; char flags[4]; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; printf("\nSTRTAB\n"); sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; for(i = 0; i < e->e_shnum; i++) { if(s->type == 3) { printf("\n%2d %-12s \n",i+1,str+s->name); str = p + s->offset; for(j = 0; j < s->size; j++) if(str[j] == 0) printf("\n %-3d ",j+1); else printf("%c",str[j]); printf("\n"); } s++; } } Output STRTAB 7 .shstrtab 1 .symtab 9 .strtab 17 .shstrtab 27 .text 33 .data 39 .bss 44 .note 50 .comment 59 9 .strtab 1 b.c 5 gcc2_compiled. 20 abc 24 -------------------------------------------------------
Here we have e which is a pointer to a structure which looks like elfheader while p is a pointer to a char. We ferret out the location of the start of the section headers and the location of the string table. If there are 9 sections, the for() will loop 9 times.
We use the offset from the start and display every byte found there. All the strings are null terminated, so we can use that to our advantage.
Notice that you can see abc() here.
In this program, we're just going to the string table (type 3) and printing out stuff.
10 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; struct sym { int name; unsigned int value; int size; unsigned char info; unsigned char other; short int shndx; }; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; char *bind[] = {"LOCAL", "GLOBAL","WEAK"}; char *type[] = {"NOTYPE","OBJECT","FUNC","SECTION","FILE"}; FILE *fp;struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; int i,j,k,l; char flags[4]; struct sym *sy1; int no; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; printf("\nSYMTAB\n"); sh=p + e->e_shoff; s= sh; for(i = 0; i < e->e_shnum; i++) { if(s->type == 2) { sy1= p+s->offset; s1 = sh + s->link; str = p + s1->offset; no = s->size/s->esize; printf("No of Symbols %d\n",no); printf("%-3s %-20s %-8s %-5s %-5s %-10s %-5s\n","No","Name","Value","Size", "Other", "Sect Index","Info"); for(j = 0; j < no; j++) { printf("%-3d %-20s %-8x %-5d %-5d %-10d ",j,str+sy1->name,sy1->value,sy1->size, sy1->other,sy1->shndx); k = sy1->info; k = k >> 4; printf("%-6s ",bind[k]); l = sy1->info; l = l & 0xF; printf("%-7s \n",type[l]); sy1++; } } s++; } } Output SYMTAB No of Symbols 9 No Name Value Size Other Sect Index Info 0 0 0 0 0 LOCAL NOTYPE 1 b.c 0 0 0 -15 LOCAL FILE 2 0 0 0 1 LOCAL SECTION 3 0 0 0 2 LOCAL SECTION 4 0 0 0 3 LOCAL SECTION 5 gcc2_compiled. 0 0 0 1 LOCAL NOTYPE 6 0 0 0 4 LOCAL SECTION 7 0 0 0 5 LOCAL SECTION 8 abc 0 5 0 1 GLOBAL FUNC ------------------------------------------
Here we display the symbol table. Anything to do with the program (e.g. function names, variables etc.) is a symbol and they're noted down in this section.
struct sym is the structure of the symbols.
The names are actually offsets into the symbol table and that's where we get the text from.
11 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; struct sym { int name; unsigned int value; int size; unsigned char info; unsigned char other; short int shndx; }; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str,*str1; int i,j,k,l; char flags[4]; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; sh=p + e->e_shoff; s= sh; s1 = s + e->e_shstrndx; str = p + s1->offset; for(i = 0; i < e->e_shnum; i++) { if(s->type == 1) { if(strcmp((str+s->name),".comment")== 0) { str1 = p + s->offset; for(j=0; j < s->size; j++) if(str1[j] !=0) printf("%c",str1[j]); printf("\n"); } } s++; } } Output GCC: (GNU) egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) --------------------------------------------------------------------
The code should start looking a little familiar by now. We're following the same basic steps everywhere. It may look confusing, but a little inspection will clear most of your doubts.
In this program, we go to the .comment section and print it out.
12 b.c int i=4; abc() { } a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; struct sym { int name; unsigned int value; int size; unsigned char info; unsigned char other; short int shndx; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i,j,k,l; char flags[4]; struct sym *sy1; int no; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; char *bind[] = {"LOCAL", "GLOBAL","WEAK"}; char *type[] = {"NOTYPE","OBJECT","FUNC","SECTION","FILE"}; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n"); printf("%-2s %-12s %-10s %-5s %-8s %-8s %-5s %-5s %-5s %-5s %-5s\n", "No","Name","Type","Flg","Address","Offset","Size","Link","Info","Align","Esize"); for(i = 0; i < e->e_shnum; i++) { printf("%-2d %-12s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); s++; } s = sh + e->e_shstrndx; str = p + s->offset; s= sh; for(i = 0; i < e->e_shnum; i++) { if(s->type == 2) { printf("\n\n"); printf("%-2d %-12s \n",i,str+s->name); sy1= p+s->offset; s1 = sh + s->link; str1 = p + s1->offset; no = s->size/s->esize; printf("\nNo of Symbols %d\n",no); printf("%-3s %-20s %-8s %-5s %-5s %-10s %-5s\n","No","Name","Value","Size", "Other", "Sect Index","Info"); for(j = 0; j < no; j++) { printf("%-3d %-20s %-8x %-5d %-5d %-10d ",j,str1+sy1->name,sy1->value,sy1->size, sy1->other,sy1->shndx); k = sy1->info; k = k >> 4; printf("%-6s ",bind[k]); l = sy1->info; l = l & 0xF; printf("%-7s \n",type[l]); sy1++; } } if(s->type == 1) { if(strcmp((str+s->name),".data")== 0) { printf("\n\n"); printf("%-2d %-12s ",i,str+s->name); str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02d ",str1[j]); printf("\n"); } } s++; } } gcc -c b.c gcc a.c -o a ../a No Name Type Flg Address Offset Size Link Info Align Esize 0 NULL 0 0 0 0 0 0 0 1 .text PROGBITS AE 0 52 5 0 0 4 0 2 .data PROGBITS WA 0 60 4 0 0 4 0 3 .bss NOBITS WA 0 64 0 0 0 4 0 4 .note NOTE 0 64 20 0 0 1 0 5 .comment PROGBITS 0 84 61 0 0 1 0 6 .shstrtab STRTAB 0 145 59 0 0 1 0 7 .symtab SYMTAB 0 564 160 8 8 4 16 8 .strtab STRTAB 0 724 26 0 0 1 0 2 .data 04 00 00 00 7 .symtab No of Symbols 10 No Name Value Size Other Sect Index Info 0 0 0 0 0 LOCAL NOTYPE 1 b.c 0 0 0 -15 LOCAL FILE 2 0 0 0 1 LOCAL SECTION 3 0 0 0 2 LOCAL SECTION 4 0 0 0 3 LOCAL SECTION 5 gcc2_compiled. 0 0 0 1 LOCAL NOTYPE 6 0 0 0 4 LOCAL SECTION 7 0 0 0 5 LOCAL SECTION 8 i 0 4 0 2 GLOBAL OBJECT 9 abc 0 5 0 1 GLOBAL FUNC --------------------------------------------------------------
Here b.c has a variable initialized to 4 and a function as well.
Notice the .data section. It has the value of the variable which is set to 4. Intel chips are little endian, so read the four bytes from the left.
Notice that i also shows up in the .symtab.
The size is 4 (for int) and the sect is 2. That means you will find the value in section no. 2 (the .data section).
Notice that in .data, the number of bytes are 4. Just as we'd expect.
Notice also that the size of abc is 5, and that consists of the function prolog and epilog. abc() is found in section number 1; i.e. .text. Thus we can see that the symbol table cross-references all the symbols.
Basically, see if you can avoid this prog by having the guy run the prev prog on the new b.c file.
13 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; struct sym { int name; unsigned int value; int size; unsigned char info; unsigned char other; short int shndx; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i,j,k,l; char flags[4]; struct sym *sy1; int no; char *class[]={"Invalid Class","32-bit objects", "64-bit objects"}; char *endian[] = {"Invalid", "Little Endian","Big Endian"}; char *version[] = {"Invalid Version","Current Version"}; char *filetype[] = {"None","Rel Obj","Executable","Dynamic","Core","Num"}; char *machine[] = { "None","WE32100","SPARC","80386","MK68000","MK88000","80486","80860","MIPS"}; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; char *bind[] = {"LOCAL", "GLOBAL","WEAK"}; char *type[] = {"NOTYPE","OBJECT","FUNC","SECTION","FILE"}; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; printf("%x %c%c%c %s %s %s\n",e->e_ident[0],e->e_ident[1],e->e_ident[2], e->e_ident[3],class[e->e_ident[4]],endian[e->e_ident[5]],version[e->e_ident[6]]); printf("Type :%s \n",filetype[e->e_type]); printf("Machine :%s \n",machine[e->e_machine]); printf("Version :%s \n",version[e->e_version]); printf("Entry Point :%x\n",e->e_entry); printf("Size of Initial Structure :%d\n",e->e_ehsize); printf("Flags :%d\n",e->e_flags); printf("Program Header Offset :%d\n",e->e_phoff); printf("PHO : Structure Size %d No. of Entries %d\n",e->e_phentsize,e->e_phnum); printf("Section Header Offset :%d\n",e->e_shoff); printf("SHO: Structure Size %d No. of Entries %d\n",e->e_shentsize,e->e_shnum); printf("String Table Section Number :%d\n",e->e_shstrndx); sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n"); printf("%-2s %-15s %-10s %-5s %-8s %-8s %-5s %-5s %-5s %-5s %-5s\n", "No","Name","Type","Flg","Address","Offset","Size","Link","Info","Align","Esize"); for(i = 0; i < e->e_shnum; i++) { printf("%-2d %-15s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%-10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); s++; } s = sh + e->e_shstrndx; str = p + s->offset; s= sh; for(i = 0; i < e->e_shnum; i++) { printf("\n\n"); printf("%-2d %-15s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); if(s->type == 3) { str1 = p + s->offset; for(j = 0; j < s->size; j++) if(str1[j] == 0) printf("\n %-3d ",j+1); else printf("%c",str1[j]); printf("\n"); } if(s->type == 2) { sy1= p+s->offset; s1 = sh + s->link; str1 = p + s1->offset; no = s->size/s->esize; printf("\nNo of Symbols %d\n",no); printf("%-3s %-20s %-8s %-5s %-5s %-10s %-5s\n","No","Name","Value","Size", "Other", "Sect Index","Info"); for(j = 0; j < no; j++) { printf("%-3d %-20s %-8x %-5d %-5d %-10d ",j,str1+sy1->name,sy1->value,sy1->size, sy1->other,sy1->shndx); k = sy1->info; k = k >> 4; printf("%-6s ",bind[k]); l = sy1->info; l = l & 0xF; printf("%-7s \n",type[l]); sy1++; } } if(s->type == 7) { str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02x ",str1[j]); printf("\n"); } if(s->type == 1) { if(strcmp((str+s->name),".comment")== 0) { str1 = p + s->offset; for(j=0; j < s->size; j++) if(str1[j] !=0) printf("%c",str1[j]); printf("\n"); } if(strcmp((str+s->name),".text")== 0) { str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02x ",str1[j]); printf("\n"); } if(strcmp((str+s->name),".data")== 0) { str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02d ",str1[j]); printf("\n"); } } s++; } } Output 7f ELF 32-bit objects Little Endian Current Version Type :Rel Obj Machine :80386 Version :Current Version Entry Point :0 Size of Initial Structure :52 Flags :0 Program Header Offset :0 PHO : Structure Size 0 No. of Entries 0 Section Header Offset :204 SHO: Structure Size 40 No. of Entries 9 String Table Section Number :6 No Name Type Flg Address Offset Size Link Info Align Esize 0 NULL 0 0 0 0 0 0 0 1 .text PROGBITS AE 0 52 5 0 0 4 0 2 .data PROGBITS WA 0 60 4 0 0 4 0 3 .bss NOBITS WA 0 64 0 0 0 4 0 4 .note NOTE 0 64 20 0 0 1 0 5 .comment PROGBITS 0 84 61 0 0 1 0 6 .shstrtab STRTAB 0 145 59 0 0 1 0 7 .symtab SYMTAB 0 564 160 8 8 4 16 8 .strtab STRTAB 0 724 26 0 0 1 0 0 NULL 0 0 0 0 0 0 0 1 .text PROGBITS AE 0 52 5 0 0 4 0 55 89 e5 c9 c3 2 .data PROGBITS WA 0 60 4 0 0 4 0 04 00 00 00 3 .bss NOBITS WA 0 64 0 0 0 4 0 4 .note NOTE 0 64 20 0 0 1 0 08 00 00 00 00 00 00 00 01 00 00 00 30 31 2e 30 31 00 00 00 5 .comment PROGBITS 0 84 61 0 0 1 0 GCC: (GNU) egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) 6 .shstrtab STRTAB 0 145 59 0 0 1 0 1 .symtab 9 .strtab 17 .shstrtab 27 .text 33 .data 39 .bss 44 .note 50 .comment 59 7 .symtab SYMTAB 0 564 160 8 8 4 16 No of Symbols 10 No Name Value Size Other Sect Index Info 0 0 0 0 0 LOCAL NOTYPE 1 b.c 0 0 0 -15 LOCAL FILE 2 0 0 0 1 LOCAL SECTION 3 0 0 0 2 LOCAL SECTION 4 0 0 0 3 LOCAL SECTION 5 gcc2_compiled. 0 0 0 1 LOCAL NOTYPE 6 0 0 0 4 LOCAL SECTION 7 0 0 0 5 LOCAL SECTION 8 i 0 4 0 2 GLOBAL OBJECT 9 abc 0 5 0 1 GLOBAL FUNC 8 .strtab STRTAB 0 724 26 0 0 1 0 1 b.c 5 gcc2_compiled. 20 i 22 abc 26
------------------------------------------------
This is an all in one program to wrap things up. It's just an amalgamation of all we've done so far. Here we're displaying the five bytes of the .text section as well.
Remove or shift the prog around
14 b.c abc() { int j; } a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; struct sym { int name; unsigned int value; int size; unsigned char info; unsigned char other; short int shndx; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i,j,k,l; char flags[4]; struct sym *sy1; int no; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n"); printf("%-2s %-12s %-10s %-5s %-8s %-8s %-5s %-5s %-5s %-5s %-5s\n", "No","Name","Type","Flg","Address","Offset","Size","Link","Info","Align","Esize"); for(i = 0; i < e->e_shnum; i++) { printf("%-2d %-12s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); s++; } s = sh + e->e_shstrndx; str = p + s->offset; s= sh; for(i = 0; i < e->e_shnum; i++) { if(s->type == 1) { if(strcmp((str+s->name),".text")== 0) { printf("\n\n%-2d %-12s \n",i,str+s->name); str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02x ",str1[j]); printf("\n"); } } s++; } } Output No Name Type Flg Address Offset Size Link Info Align Esize 0 NULL 0 0 0 0 0 0 0 1 .text PROGBITS AE 0 52 8 0 0 4 0 2 .data PROGBITS WA 0 60 0 0 0 4 0 3 .bss NOBITS WA 0 60 0 0 0 4 0 4 .note NOTE 0 60 20 0 0 1 0 5 .comment PROGBITS 0 80 61 0 0 1 0 6 .shstrtab STRTAB 0 141 59 0 0 1 0 7 .symtab SYMTAB 0 560 144 8 8 4 16 8 .strtab STRTAB 0 704 24 0 0 1 0 1 .text 55 89 e5 83 ec 04 c9 c3 -----------------------------------------------
In b.c, we've added a variable to the function.
We're displaying the .text section so that we can see the actual machine language opcodes. The size of the section is 8 bytes. c3 = ret. TODO:Decypher the opcodes
15 b.c abc() { pqr(); } a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; struct sym { int name; unsigned int value; int size; unsigned char info; unsigned char other; short int shndx; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i,j,k,l; char flags[4]; struct sym *sy1; int no; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; char *bind[] = {"LOCAL", "GLOBAL","WEAK"}; char *type[] = {"NOTYPE","OBJECT","FUNC","SECTION","FILE"}; main() { fp = fopen("b.o","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n"); printf("%-2s %-12s %-10s %-5s %-8s %-8s %-5s %-5s %-5s %-5s %-5s\n", "No","Name","Type","Flg","Address","Offset","Size","Link","Info","Align","Esize"); for(i = 0; i < e->e_shnum; i++) { printf("%-2d %-12s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); s++; } s = sh + e->e_shstrndx; str = p + s->offset; s= sh; for(i = 0; i < e->e_shnum; i++) { if(s->type == 3) { if(strcmp(str+s->name,".strtab")==0) { printf("\n%-2d %-12s \n",i,str+s->name); str = p + s->offset; for(j = 0; j < s->size; j++) if(str[j] == 0) printf("\n %-3d ",j+1); else printf("%c",str[j]); printf("\n"); } } if(s->type == 2) { printf("\n%-2d %-12s\n ",i,str+s->name); sy1= p+s->offset; s1 = sh + s->link; str1 = p + s1->offset; no = s->size/s->esize; printf("\nNo of Symbols %d\n",no); printf("%-3s %-20s %-8s %-5s %-5s %-10s %-5s\n","No","Name","Value","Size", "Other", "Sect Index","Info"); for(j = 0; j < no; j++) { printf("%-3d %-20s %-8x %-5d %-5d %-10d ",j,str1+sy1->name,sy1->value,sy1->size, sy1->other,sy1->shndx); k = sy1->info; k = k >> 4; printf("%-6s ",bind[k]); l = sy1->info; l = l & 0xF; printf("%-7s \n",type[l]); sy1++; } } if(s->type == 1) { if(strcmp((str+s->name),".text")== 0) { printf("\n%-2d %-12s \n",i,str+s->name); str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02x ",str1[j]); printf("\n"); } } s++; } } Output No Name Type Flg Address Offset Size Link Info Align Esize 0 NULL 0 0 0 0 0 0 0 1 .text PROGBITS AE 0 52 10 0 0 4 0 2 .rel.text REL 0 804 8 8 1 4 8 3 .data PROGBITS WA 0 64 0 0 0 4 0 4 .bss NOBITS WA 0 64 0 0 0 4 0 5 .note NOTE 0 64 20 0 0 1 0 6 .comment PROGBITS 0 84 61 0 0 1 0 7 .shstrtab STRTAB 0 145 69 0 0 1 0 8 .symtab SYMTAB 0 616 160 9 8 4 16 9 .strtab STRTAB 0 776 28 0 0 1 0 1 .text 55 89 e5 e8 fc ff ff ff c9 c3 8 .symtab No of Symbols 10 No Name Value Size Other Sect Index Info 0 0 0 0 0 LOCAL NOTYPE 1 b.c 0 0 0 -15 LOCAL FILE 2 0 0 0 1 LOCAL SECTION 3 0 0 0 3 LOCAL SECTION 4 0 0 0 4 LOCAL SECTION 5 gcc2_compiled. 0 0 0 1 LOCAL NOTYPE 6 0 0 0 5 LOCAL SECTION 7 0 0 0 6 LOCAL SECTION 8 abc 0 10 0 1 GLOBAL FUNC 9 pqr 0 0 0 0 GLOBAL NOTYPE 9 .strtab 1 b.c 5 gcc2_compiled. 20 abc 24 pqr 28 ---------------------------------------------------------------
We now have a function being called by abc() in b.c.
pqr() is present in the string and symbol table, but the section is 0. So it doesn't belong to any section as of yet. Notice the code in the ..text section. it now has a call. e8 =call. The function however has not yet been specified. This will happen when relocation takes place when the program is loaded into memory... but more on that later.
16 b.c abc() { printf("hello\n"); } gcc -c b.c ld -o b b.o -dynamic-linker /lib/ld-linux.so.2 -lc -e abc a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; struct sym { int name; unsigned int value; int size; unsigned char info; unsigned char other; short int shndx; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i,j,k,l; char flags[4]; struct sym *sy1; int no; char *class[]={"Invalid Class","32-bit objects", "64-bit objects"}; char *endian[] = {"Invalid", "Little Endian","Big Endian"}; char *version[] = {"Invalid Version","Current Version"}; char *filetype[] = {"None","Rel Obj","Executable","Dynamic","Core","Num"}; char *machine[] = { "None","WE32100","SPARC","80386","MK68000","MK88000","80486","80860","MIPS"}; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; char *bind[] = {"LOCAL", "GLOBAL","WEAK"}; char *type[] = {"NOTYPE","OBJECT","FUNC","SECTION","FILE"}; main() { fp = fopen("b","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; printf("%x %c%c%c %s %s %s\n",e->e_ident[0],e->e_ident[1],e->e_ident[2], e->e_ident[3],class[e->e_ident[4]],endian[e->e_ident[5]],version[e->e_ident[6]]); printf("Type :%s \n",filetype[e->e_type]); printf("Machine :%s \n",machine[e->e_machine]); printf("Version :%s \n",version[e->e_version]); printf("Entry Point :%x\n",e->e_entry); printf("Size of Initial Structure :%d\n",e->e_ehsize); printf("Flags :%d\n",e->e_flags); printf("Program Header Offset :%d\n",e->e_phoff); printf("PHO : Structure Size %d No. of Entries %d\n",e->e_phentsize,e->e_phnum); printf("Section Header Offset :%d\n",e->e_shoff); printf("SHO: Structure Size %d No. of Entries %d\n",e->e_shentsize,e->e_shnum); printf("String Table Section Number :%d\n",e->e_shstrndx); sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n"); printf("%-2s %-15s %-10s %-5s %-8s %-8s %-5s %-5s %-5s %-5s %-5s\n", "No","Name","Type","Flg","Address","Offset","Size","Link","Info","Align","Esize"); for(i = 0; i < e->e_shnum; i++) { printf("%-2d %-15s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%-10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); s++; } s = sh + e->e_shstrndx; str = p + s->offset; s= sh; for(i = 0; i < e->e_shnum; i++) { if(s->type == 3) { printf("\n%-2d %-15s \n",i,str+s->name); str1 = p + s->offset; for(j = 0; j < s->size; j++) if(str1[j] == 0) printf("\n %-3d ",j+1); else printf("%c",str1[j]); printf("\n"); } if(s->type == 2) { printf("\n%-2d %-15s \n",i,str+s->name); sy1= p+s->offset; s1 = sh + s->link; str1 = p + s1->offset; no = s->size/s->esize; printf("\nNo of Symbols %d\n",no); printf("%-3s %-20s %-8s %-5s %-5s %-10s %-5s\n","No","Name","Value","Size", "Other", "Sect Index","Info"); for(j = 0; j < no; j++) { printf("%-3d %-20s %-8x %-5d %-5d %-10d ",j,str1+sy1->name,sy1->value,sy1->size, sy1->other,sy1->shndx); k = sy1->info; k = k >> 4; printf("%-6s ",bind[k]); l = sy1->info; l = l & 0xF; printf("%-7s \n",type[l]); sy1++; } } if(s->type == 7) { printf("\n%-2d %-15s \n",i,str+s->name); str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02x ",str1[j]); printf("\n"); } if(s->type == 1) { if(strcmp((str+s->name),".comment")== 0) { printf("\n%-2d %-15s \n",i,str+s->name); str1 = p + s->offset; for(j=0; j < s->size; j++) if(str1[j] !=0) printf("%c",str1[j]); printf("\n"); } if(strcmp((str+s->name),".text")== 0) { printf("\n%-2d %-15s \n",i,str+s->name); str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02x ",str1[j]); printf("\n"); } if(strcmp((str+s->name),".data")== 0) { printf("\n%-2d %-15s \n",i,str+s->name); str1 = p + s->offset; for(j=0; j < s->size; j++) printf(" %02d ",str1[j]); printf("\n"); } } s++; } } Output 7f ELF 32-bit objects Little Endian Current Version Type :Executable Machine :80386 Version :Current Version Entry Point :8048184 Size of Initial Structure :52 Flags :0 Program Header Offset :52 PHO : Structure Size 32 No. of Entries 5 Section Header Offset :788 SHO: Structure Size 40 No. of Entries 20 String Table Section Number :17 No Name Type Flg Address Offset Size Link Info Align Esize 0 NULL 0 0 0 0 0 0 0 1 .interp PROGBITS A 80480d4 212 19 0 0 1 0 2 .hash HASH A 80480e8 232 20 3 0 4 4 3 .dynsym DYNSYM A 80480fc 252 32 4 1 4 16 4 .dynstr STRTAB A 804811c 284 28 0 0 1 0 5 .gnu.version 6fffffff A 8048138 312 4 3 0 2 2 6 .gnu.version_r 6ffffffe A 804813c 316 32 4 1 4 0 7 .rel.plt REL A 804815c 348 8 3 8 4 8 8 .plt PROGBITS AE 8048164 356 32 0 0 4 4 9 .text PROGBITS AE 8048184 388 18 0 0 4 0 10 .rodata PROGBITS A 8048196 406 7 0 0 1 0 11 .data PROGBITS WA 80491a0 416 0 0 0 4 0 12 .got PROGBITS WA 80491a0 416 16 0 0 4 4 13 .dynamic DYNAMIC WA 80491b0 432 120 4 0 4 8 14 .bss NOBITS WA 8049228 552 0 0 0 4 0 15 .comment PROGBITS 0 552 61 0 0 1 0 16 .note NOTE 3d 613 20 0 0 1 0 17 .shstrtab STRTAB 0 633 153 0 0 1 0 18 .symtab SYMTAB 0 1588 480 19 22 4 16 19 .strtab STRTAB 0 2068 104 0 0 1 0 4 .dynstr 1 libc.so.6 11 printf 18 GLIBC_2.0 28 9 .text 55 89 e5 68 96 81 04 08 e8 e3 ff ff ff 83 c4 04 c9 c3 11 .data 15 .comment GCC: (GNU) egcs-2.91.66 19990314/Linux (egcs-1.1.2 release) 16 .note 08 00 00 00 00 00 00 00 01 00 00 00 30 31 2e 30 31 00 00 00 17 .shstrtab 1 .symtab 9 .strtab 17 .shstrtab 27 .interp 35 .hash 41 .dynsym 49 .dynstr 57 .gnu.version 70 .gnu.version_r 85 .rel.plt 94 .plt 99 .text 105 .rodata 113 .data 119 .got 124 .dynamic 133 .bss 138 .comment 147 .note 153 18 .symtab No of Symbols 30 No Name Value Size Other Sect Index Info 0 0 0 0 0 LOCAL NOTYPE 1 80480d4 0 0 1 LOCAL SECTION 2 80480e8 0 0 2 LOCAL SECTION 3 80480fc 0 0 3 LOCAL SECTION 4 804811c 0 0 4 LOCAL SECTION 5 8048138 0 0 5 LOCAL SECTION 6 804813c 0 0 6 LOCAL SECTION 7 804815c 0 0 7 LOCAL SECTION 8 8048164 0 0 8 LOCAL SECTION 9 8048184 0 0 9 LOCAL SECTION 10 8048196 0 0 10 LOCAL SECTION 11 80491a0 0 0 11 LOCAL SECTION 12 80491a0 0 0 12 LOCAL SECTION 13 80491b0 0 0 13 LOCAL SECTION 14 8049228 0 0 14 LOCAL SECTION 15 0 0 0 15 LOCAL SECTION 16 3d 0 0 16 LOCAL SECTION 17 0 0 0 17 LOCAL SECTION 18 0 0 0 18 LOCAL SECTION 19 0 0 0 19 LOCAL SECTION 20 b.c 0 0 0 -15 LOCAL FILE 21 gcc2_compiled. 8048184 0 0 9 LOCAL NOTYPE 22 _DYNAMIC 80491b0 0 0 13 GLOBAL OBJECT 23 _etext 8048196 0 0 -15 GLOBAL OBJECT 24 abc 8048184 18 0 9 GLOBAL FUNC 25 __bss_start 8049228 0 0 -15 GLOBAL OBJECT 26 printf@@GLIBC_2.0 8048174 41 0 0 GLOBAL FUNC 27 _edata 8049228 0 0 -15 GLOBAL OBJECT 28 _GLOBAL_OFFSET_TABLE_ 80491a0 0 0 12 GLOBAL OBJECT 29 _end 8049228 0 0 -15 GLOBAL OBJECT 19 .strtab 1 b.c 5 gcc2_compiled. 20 _DYNAMIC 29 _etext 36 abc 40 __bss_start 52 printf@@GLIBC_2.0 70 _edata 77 _GLOBAL_OFFSET_TABLE_ 99 _end 104 ------------------------------- gcc -c b.c ld -o b b.o -dynamic-linker /lib/ld-linux.so.2 -lc -e abc
b.c now has a call to printf(); just one however. In the call to the linker, we specify the entry point as abc() because if you've noticed, we have no main(). main() calls in a lot of initialization code which is necessary if you want a working program. However, that would add complexity and make the output even larger than it already is. Besides, we don't intend to run these programs anyway.
In the main program, we open up b, the actual executable rather than the ..o file. We're through with those.
Most of the code used to generate the output is the same and the output itself is remarkably similar to what weave been doing so far. The file type is now Executable however.
Notice the Entry Point is now 0x8048184. The file is loaded in at 0x8048000. So the real code starts 0x184 bytes into the file. Notice that in the section header output, .text has an offset of 388 (0x184) bytes into the program. So the Entry Point points to the first byte of ..text.
If there are no flags shown next to a section, then it doesn't have to be loaded into memory. 'Progbits' is a message for the linker or loader and it's not supposed parse such sections. Only .text and ..plt are AE which means they're to be loaded into memory (A) and that they're executable (E) as well.
The compiler is now no longer important. All the information in the file is for the benefit of the loader.
So we have 52 bytes of the init header.
Then the PHO, (32*5) = 160 bytes of program headers
Then we have data of the program headers.
From offset 788, we get the section headers.
Then we have (40*20) = 800 bytes of section headers.
Take look at .interp, which starts at offset 212 (52+160). It is the first program header. Simple huh? The memory location it's supposed to be loaded at is 0x80480d4 and d4=212. So it's a straight load into memory.
..interp itself contains nothing more than the name of the loader.
The column Align means align on the byte boundary. 1 means align on a byte boundary, 4 implies that it's aligned on a 4 bytes basis etc.
..hash contains a hash of the symbols. LINK means "applied to which". It is 3 here, so the .hash applies to the 3rd section, the .dynsym. Notice that everything matches up and is connected to everything else.
..dynsym has two structures of 16 bytes each adding up to a total of 32 bytes. It is the dynamic sym table.
..gunu.stuff is gnu's stuff. :-) We're not supposed to touch it.
..rodata is read only data and its size is 07. hello\n\0 is 7 bytes.
..shstrtab is the section table for strings.
b.c abc() { printf("hello\n"); } gcc -c b.c ld -o b b.o --dynamic-linker /lib/ld-linux.so.2 -lc -e abc ----------------------------------------------------------- 1 a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i; char flags[4]; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; main() { fp = fopen("b","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n\n"); for(i = 0; i < e->e_shnum; i++) { if(strcmp(str+s->name,".interp") == 0) { printf("%-2d %-15s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%-10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); str1 = p + s->offset; printf("%s\n",str1); } s++; } } gcc a.c -o a ../a 1 .interp PROGBITS A 80480d4 212 19 0 0 1 0 /lib/ld-linux.so.2 ---------------------------------------------------------------------------
Since we're done with the linker, all information we now process is for the loader and the OS. It's time to tackle the actual executable. Here we're printing .interp which contains the path and name of the loader this program will use. It's the same string we used as a parameter to 'ld'. From byte 212, the next 19 bytes contain the information that we're printing out.
The linker doesn't place the addresses of the function calls in the code. That's done dynamically at loading by the loader and it's called relocation. The loader has to come in later and link up the incomplete call with the actual library code when the program is loaded in.
2 hash #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i; char flags[4]; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; int flg=0; int j,no; long *hs; main() { fp = fopen("b","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n\n"); for(i = 0; i < e->e_shnum; i++) { if(strcmp(str+s->name,".hash") == 0) { printf("%-2d %-15s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%-10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); hs = (long *) (p + s->offset); no = s->size/s->esize; printf("\n"); for(j = 1; j <= no; j++ ) { printf("%ld\n",*hs); hs++; } } s++; } } Output 2 .hash HASH A 80480e8 232 20 3 0 4 4 1 2 1 0 0 -------------------------------------------------------------------------------
Here we're printing out the section named .hash which contains a hash table with information we'll need a little later. The size of each entry is 4 and the total size is 20, so each entry must be 5 bytes large, starting at 232.
We'll do the hash in detail a little later. It's used to store data in a format from where it can be retrieved real fast.
3. #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; struct sym { int name; unsigned int value; int size; unsigned char info; unsigned char other; short int shndx; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i; char flags[4]; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; char *bind[] = {"LOCAL", "GLOBAL","WEAK"}; char *type[] = {"NOTYPE","OBJECT","FUNC","SECTION","FILE"}; int flg=0; int j,no,k,l; struct sym *sy1; main() { fp = fopen("b","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n\n"); for(i = 0; i < e->e_shnum; i++) { if(strcmp(str+s->name,".dynsym") == 0) { printf("%-2d %-15s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%-10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); sy1= p+s->offset; s1 = sh + s->link; str = p + s1->offset; no = s->size/s->esize; printf("\nNo of Symbols %d\n\n",no); printf("%-3s %-20s %-8s %-5s %-5s %-10s %-5s\n","No","Name","Value","Size", "Other", "Sect Index","Info"); for(j = 0; j < no; j++) { printf("%-3d %-20s %-8x %-5d %-5d %-10d ",j,str+sy1->name,sy1->value,sy1->size, sy1->other,sy1->shndx); k = sy1->info; k = k >> 4; printf("%-6s ",bind[k]); l = sy1->info; l = l & 0xF; printf("%-7s \n",type[l]); sy1++; } printf("\n"); } s++; } } Output 3 .dynsym DYNSYM A 80480fc 252 32 4 1 4 16 No of Symbols 2 No Name Value Size Other Sect Index Info 0 0 0 0 0 LOCAL NOTYPE 1 printf 8048174 41 0 0 GLOBAL FUNC -----------------------------------------------------------------------
4. a.c #include <stdio.h> #include <sys/stat.h> struct elfhdr { char e_ident[16]; short int e_type; short int e_machine; int e_version; char *e_entry; int e_phoff; int e_shoff; int e_flags; short int e_ehsize; short int e_phentsize; short int e_phnum; short int e_shentsize; short int e_shnum; short int e_shstrndx; }; struct elf_shdr { int name; int type; int flags; int addr; int offset; int size; int link; int info; int align; int esize; }; FILE *fp; struct stat st; struct elfhdr *e; struct elf_shdr *s,*s1,*sh; char *p,*str; unsigned char *str1; int i; char flags[4]; char *stype[] ={"NULL","PROGBITS","SYMTAB","STRTAB","RELA","HASH","DYNAMIC", "NOTE","NOBITS","REL","SHLIB","DYNSYM","NUM"}; int flg=0; int j,no,k,l; main() { fp = fopen("b","r"); fstat(fileno(fp),&st); p = (char *) malloc(st.st_size); fread(p,st.st_size,1,fp); e = (struct elfhdr *)p; sh = p + e->e_shoff; s = sh + e->e_shstrndx; str = p + s->offset; s= sh; printf("\n\n"); for(i = 0; i < e->e_shnum; i++) { if(strcmp(str+s->name,".dynstr") == 0) { printf("%-2d %-15s ",i,str+s->name); if(s->type <= 12) printf("%-10s",stype[s->type]); else printf("%-10x",s->type); strcpy(flags,""); if((s->flags & 0x01) == 1) strcat(flags,"W"); if((s->flags & 0x02) == 2) strcat(flags,"A"); if((s->flags & 0x04) == 4) strcat(flags,"E"); printf(" %-5s %-8x %-8d %-5d %-5d %-5d %-5d %-5d\n", flags,s->addr,s->offset,s->size,s->link,s->info,s->align,s->esize); str1 = p + s->offset; for(j=0;j < s->size; j++ ) { if(str1[j]== 0) printf("\n"); else printf("%c",str1[j]); } } s++; } } Output 4 .dynstr STRTAB A 804811c 284 28 0 0 1 0 libc.so.6 printf GLIBC_2.0 ------------------------------------------------------------------
Here we've put everything we've done uptil now into one big program. Notice that our test program now contains a main().
In the code, we're just doing strncmp() again and again to find sections and when we do, we just printing them out in a neat manner.
The first 52 bytes are the header. Upto String Section 26.
Now we have the 6 program headers, each 32 bytes large. Section and program section headers may point to the same data.
PHDR starts from 52 and the size is 192 (6*32). This gives us the limit of the program headers themselves. .interp is 244 (52+192), so it's right up after the program headers. V(irtual) and P(physical)addresses are usually the same. This is so the file can be loaded quickly into memory. This is why the file and memory size is also usually the same.
Align is set for byte alignment.
..interp's data is at 244 and it contains the name of the loader.
The two loads are the program.
We can only understand all the program headers if we look at the section headers. Check out .interp in the section headers. The size is F4(244). So this too points to the same place as the program header.
..note.ABI-tag is a note from the linker which we just print out. It's for others to look at. It's optional, so if we remove it the prog will still work.
..hash is the hash table.
DYNSYM contains the list of dynamic symbols which have been added by the linker. Notice that printf() is present here.
..strtab is present as well.
..re.got and .rel.plt are for relocation, so we'll tackle them later.
..init is the initilization code called even before the startup code of main(). The startup code is the bit which sets up argc and argv and handles other little details.
..text is the code.
In the DYNAMIC section, the smaller numbers are sizes and the larger numbers are actual memory addresses, not offsets.
..stab is also code
And all of this is followed by a boatload of strings.