Rev 1051 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
////////////////////////////////////////////////////////////////////////
// elf.c
//
// DynaLink for S.H.A.R.K
// Dynamic ELF object linker.
//
// Original code written by Luca Abeni.
// Adapted by Lex Nahumury 19-7-2006.
//
// This is free software; see GPL.txt
////////////////////////////////////////////////////////////////////////
#include "kernel/kern.h"
#include <kernel/func.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ll/i386/hw-data.h>
#include <ll/i386/mem.h>
#include <ll/i386/hw-instr.h>
#include <ll/i386/cons.h>
#include <ll/i386/error.h>
#include <ll/i386/mem.h>
#include <ll/ctype.h>
#include <ll/i386/x-bios.h>
#include "format.h"
#include "elf.h"
//#define __ELF_DEBUG__
char elf_signature
[] = "\177ELF";
DWORD ELF_read_headers
( struct file_ops
*kf
,
struct table_info
*tables
)
{
//printk("ELF_read_headers\n");
int res
;
DWORD entry
;
struct elf_header header
;
struct elf_section_header h
;
kf
->file_seek
(kf
->file_offset
+ 0, kf
->seek_set
);
kf
->file_read
(&header
, sizeof(struct elf_header
));
if (memcmp(header.
e_ident, elf_signature
, 4)) {
printk
("Not an ELF file\n");
printk
("Wrong signature: 0x%lx)!!!\n",
*(DWORD
*)header.
e_ident);
return 0;
}
//printk("ELF signature OK\n");
tables
->flags
= 0;
if (header.
e_ident[4] != ELFCLASS32
) {
printk
("Wrong ELF class\n");
printk
("Class: 0x%x!!!\n", header.
e_ident[4]);
return 0;
}
//printk("ELF Class OK\n");
if (header.
e_ident[5] != ELFDATA2LSB
) {
printk
("Wrong data ordering (not LSB)\n");
printk
("Ordering: 0x%x!!!\n", header.
e_ident[5]);
return 0;
}
//printk("ELF data ordering OK\n");
#ifdef __ELF_DEBUG__
if(header.
e_machine != EM_386
)
{
printk
("Warning: machine = 0x%x!!!\n", header.
e_machine);
}
#endif
//printk("ELF Type: 0x%x\n", header.e_type);
if(header.
e_shoff != 0)
{
#ifdef __ELF_DEBUG__
printk
("Section headers @ %ld\n", header.
e_shoff);
printk
("Number of sections: %d\n", header.
e_shnum);
printk
("Section header size: %d (0x%x)\n",
header.
e_shentsize, header.
e_shentsize);
#endif
tables
->section_header
= header.
e_shoff;
tables
->section_header_size
= header.
e_shentsize;
tables
->num_sections
= header.
e_shnum;
}
#ifdef __ELF_DEBUG__
if (header.
e_phoff != 0) {
printk
("Program header table @ %ld\n", header.
e_phoff);
printk
("Number of segments: %d\n", header.
e_phnum);
printk
("Segment header size: %d (0x%x)\n",
header.
e_phentsize, header.
e_phentsize);
}
#ifdef __ELF_DEBUG__
/* Flags... */
/* RELOCATION */
if (header.
f_flags & F_RELFLG
) {
printk
("No relocation info!\n");
}
if (header.
f_flags & F_EXEC
) {
printk
("Executable file (no unresolved symbols)\n");
}
if (header.
f_flags & F_LNNO
) {
printk
("No line numbers!\n");
}
if (header.
f_flags & F_LSYMS
) {
printk
("No local symbols!\n");
}
if (header.
f_flags & F_AR32WR
) {
printk
("32-bit little endian!\n");
} else {
printk
("File type?\n");
}
#endif
printk
("Section Name String Table is section number %d\n",
header.
e_shstrndx);
#endif
if (header.
e_shstrndx > tables
->num_sections
)
{
printk
("Error: SNST number > section number...\n");
return 0;
}
res
= kf
->file_seek
( kf
->file_offset
+ tables
->section_header
+
header.
e_shstrndx * tables
->section_header_size
,
kf
->seek_set
);
if (res
< 0) {
printk
("Cannot seek");
return 0;
}
//printk("ELF file seek OK\n");
res
= kf
->file_read
( &h
, sizeof(struct elf_section_header
));
if (res
< 0) {
printk
("Cannot read");
return 0;
}
//printk("ELF file read OK\n");
tables
->section_names
= 0;
if(h.
sh_size != 0)
{
//printk("ELF Loading Section Names...\n");
tables
->section_names
= (void *)malloc(h.
sh_size);
if (tables
->section_names
== NULL
)
{
printk
("Failed to allocate space for section names...\n");
return 0;
}
res
= kf
->file_seek
( kf
->file_offset
+ h.
sh_offset, kf
->seek_set
);
if (res
< 0) {
printk
("Cannot seek");
return 0;
}
res
= kf
->file_read
( tables
->section_names
, h.
sh_size);
if (res
< 0) {
printk
("Cannot read");
return 0;
}
tables
->section_names_size
= h.
sh_size;
}
else
{
printk
("0 size?\n");
tables
->section_names_size
= 0;
}
entry
= header.
e_entry;
if (entry
== 0) {
tables
->flags
|= NO_ENTRY
;
tables
->flags
|= NEED_LOAD_RELOCATABLE
;
tables
->flags
|= NEED_SECTION_RELOCATION
;
}
//printk("ELF Read headers Done!\n");
return entry
;
};
int ELF_read_section_headers
(struct file_ops
*kf
,
struct table_info
*tables
,
struct section_info
*scndata
)
{
int bss
= -1;
int i
, j
;
struct elf_section_header h
;
int header_size
;
int stringtable
= -1;
struct elf_rel_info r
;
header_size
= tables
->section_header_size
;
if(header_size
> sizeof(struct elf_section_header
))
{
printk
("Section header size (%d) > sizeof(struct section_header) (%d)\n",
header_size
, (int)(sizeof(struct elf_section_header
)) );
header_size
= sizeof(struct elf_section_header
);
}
for (i
= 0; i
< tables
->num_sections
; i
++)
{
kf
->file_seek
( kf
->file_offset
+ tables
->section_header
+
i
* tables
->section_header_size
, kf
->seek_set
);
kf
->file_read
( &h
, sizeof(struct elf_section_header
));
#ifdef __ELF_DEBUG__
printk
("Section %d: ", i
);
printk
("Flags 0x%x: \n", h.
sh_flags);
#endif
/*
Set this stuff to 0...
If size == 0 the section must not be loaded
*/
scndata
[i
].
num_reloc = 0;
scndata
[i
].
base = 0;
scndata
[i
].
size = 0;
scndata
[i
].
fileptr = 0;
scndata
[i
].
filesize = 0;
/* If this is a NULL section, skip it!!! */
if (h.
sh_type != SHT_NULL
) {
if (tables
->section_names
!= 0) {
#ifdef __ELF_DEBUG__
printk
("[%s]", tables
->section_names
+ h.
sh_name);
#endif
if (strcmp(tables
->section_names
+ h.
sh_name, ".bss") == 0) {
bss
= i
;
}
}
#ifdef __ELF_DEBUG__
printk
(" <0x%lx:0x%lx> (0x%lx)\n",
h.
sh_addr,
h.
sh_addr + h.
sh_size,
h.
sh_offset);
#endif
if (h.
sh_type == SHT_REL
) {
#ifdef __ELF_DEBUG__
printk
("\t\tSection %d: relocation info!!!\n", i
);
printk
("\t\tSymbol table: section number %lu\n", h.
sh_link);
printk
("\t\tSection to modify: %lu\n", h.
sh_info);
printk
("\t\tNumber of relocation entries: %lu\n",
h.
sh_size / h.
sh_entsize);
#endif
if (scndata
[h.
sh_info].
num_reloc != 0) {
printk
("Double relocation for section\n");
printk
("%lu?\n", h.
sh_info);
return 0;
}
/* So, ...let's load it!!! */
scndata
[h.
sh_info].
num_reloc = h.
sh_size / h.
sh_entsize;
//scndata[h.sh_info].reloc = (void *)kf->mem_alloc((h.sh_size / h.sh_entsize) * sizeof(struct reloc_info));
scndata
[h.
sh_info].
reloc = (void *)malloc((h.
sh_size / h.
sh_entsize) * sizeof(struct reloc_info
));
if (scndata
[h.
sh_info].
reloc == NULL
) {
printk
("Failed to allocate space for relocation info...\n");
return 0;
}
//printk("ELF Allocate space for relocation info OK!\n");
for (j
= 0; j
< h.
sh_size / h.
sh_entsize; j
++) {
kf
->file_seek
( kf
->file_offset
+ h.
sh_offset + j
* h.
sh_entsize,
kf
->seek_set
);
kf
->file_read
( &r
, sizeof(struct elf_rel_info
));
scndata
[h.
sh_info].
reloc[j
].
offset = r.
r_offset;
scndata
[h.
sh_info].
reloc[j
].
symbol = r.
r_info >> 8;
/* HACKME!!! Unify the relocation types... */
scndata
[h.
sh_info].
reloc[j
].
type = (BYTE
)r.
r_info;
if ((BYTE
)r.
r_info == R_386_32
){
scndata
[h.
sh_info].
reloc[j
].
type = REL_TYPE_ELF_ABSOLUTE
;
} else if ((BYTE
)r.
r_info == R_386_PC32
) {
scndata
[h.
sh_info].
reloc[j
].
type = REL_TYPE_RELATIVE
;
}
}
} else if (h.
sh_type == SHT_RELA
) {
printk
("Error: unsupported relocation section!!!\n");
return 0;
} else if ((h.
sh_type == SHT_SYMTAB
) || (h.
sh_type == SHT_DYNSYM
)) {
#ifdef __ELF_DEBUG__
printk
("\t\tSection %d: symbol table!!!\n", i
);
printk
("\t\tString table: section number %lu\n", h.
sh_link);
printk
("\t\tLast local Symbol + 1: %lu\n", h.
sh_info);
#endif
tables
->symbol
= h.
sh_offset;
tables
->num_symbols
= h.
sh_size / h.
sh_entsize;
tables
->symbol_size
= h.
sh_size;
if (stringtable
!= -1) {
printk
("Error: double string table!!!\n");
return 0;
}
stringtable
= h.
sh_link;
if (stringtable
< i
) {
printk
("Strange... ");
printk
("String table (%d) < Symbol Table\n", stringtable
);
return 0;
}
} else if (i
== stringtable
) {
#ifdef __ELF_DEBUG__
printk
("\t\t Section %d: string table!!!\n", i
);
#endif
tables
->string
= h.
sh_offset;
tables
->string_size
= h.
sh_size;
stringtable
= -1;
} else {
scndata
[i
].
base = h.
sh_addr;
scndata
[i
].
size = h.
sh_size;
scndata
[i
].
fileptr = h.
sh_offset;
if (h.
sh_type != SHT_NOBITS
) {
#ifdef __ELF_DEBUG__
printk
("BSS?\n");
#endif
scndata
[i
].
filesize = h.
sh_size;
}
}
} else {
#ifdef __ELF_DEBUG__
printk
("NULL Section\n");
#endif
}
#if 0
if (h.
s_flags & SECT_TEXT
) {
printk
("Executable section\n");
}
if (h.
s_flags & SECT_INIT_DATA
) {
printk
("Data section\n");
}
if (h.
s_flags & SECT_UNINIT_DATA
) {
printk
("BSS section\n");
scndata
[i
].
filesize = 0;
}
#endif
}
tables
->image_base
= scndata
[0].
base;
return bss
;
};
int ELF_read_symbols
(struct file_ops
*kf
,
struct table_info
*tables
,
struct symbol_info
*syms
)
{
//printk("ELF read_symbols\n");
int i
;
int entsize
;
struct elf_symbol_info symbol
;
char *s
;
s
= (void *)malloc(tables
->string_size
);
if(s
== NULL
)
{
printk
("Failed to allocate space for string table...\n");
return 0;
}
tables
->string_buffer
= (DWORD
)s
;
kf
->file_seek
( kf
->file_offset
+ tables
->string
, kf
->seek_set
);
kf
->file_read
( s
, tables
->string_size
);
entsize
= tables
->symbol_size
/ tables
->num_symbols
;
for (i
= 0; i
< tables
->num_symbols
; i
++)
{
kf
->file_seek
( kf
->file_offset
+ tables
->symbol
+ i
* entsize
,
kf
->seek_set
);
kf
->file_read
( &symbol
, sizeof(struct elf_symbol_info
));
syms
[i
].
name = s
+ symbol.
st_name;
syms
[i
].
section = symbol.
st_shndx;
syms
[i
].
offset = symbol.
st_value;
if (syms
[i
].
section == SHN_UNDEF
) {
/* extern symbol */
if (symbol.
st_name != 0)
syms
[i
].
section = EXTERN_SYMBOL
;
else /* Mark the empty entry, external symbol with no name is not used :-) */
syms
[i
].
section = NULL_SYMBOL
;
}
if (syms
[i
].
section == SHN_COMMON
) {
/* extern symbol */
syms
[i
].
section = COMMON_SYMBOL
;
syms
[i
].
offset = symbol.
st_size;
/* calculate the local_bss_size */
tables
->local_bss_size
+= syms
[i
].
offset;
}
}
return 1;
};
int Elf_check
(struct file_ops
*kf
)
{
char signature
[4];
kf
->file_offset
= kf
->file_seek
( 0, kf
->seek_cur
);
kf
->file_read
(signature
, 4);
kf
->file_seek
( kf
->file_offset
+ 0, kf
->seek_set
);
if(memcmp(signature
, elf_signature
, 4))
{
return 0;
}
return 1;
};
int ELF_relocate_section
(struct file_ops
*kf
,
DWORD base
,
struct table_info
*tables
,
int n
,
struct section_info
*s
,
int sect
,
struct symbol_info
*syms
,
struct symbol
*import
)
{
int i
, idx
;
DWORD address
, destination
;
int j
, done
;
DWORD local_bss
= tables
->local_bss
;
struct reloc_info
*rel
= s
[sect
].
reloc;
/* Setup the common space-uninitialized symbols at the first section relocation
* Pre-calculate the local BSS size (in read_symbols)
* then allocate for each symbol (in load_relocatable)
*/
if(sect
== 0)
{
for(i
=0; i
<tables
->num_symbols
; i
++)
{
if(syms
[i
].
section == COMMON_SYMBOL
)
{
j
= syms
[i
].
offset;
syms
[i
].
offset = local_bss
;
local_bss
+= j
;
}
else if(syms
[i
].
section == EXTERN_SYMBOL
)
{
#ifdef __ELF_DEBUG__
printk
("Searching for symbol %s\n", syms
[i
].
name);
#endif
/* Pre-set the external symbol at the same time */
for(j
=0, done
=0; import
[j
].
name != 0; j
++)
{
if(strcmp(import
[j
].
name, syms
[i
].
name) == 0)
{
syms
[i
].
offset = import
[j
].
address;
done
= 1;
break;
}
}
if(done
== 0)
{
printk
("Symbol %s not found\n", syms
[i
].
name);
return -1;
}
}
}
}
//printk("[COMMON] s[sect].num_reloc = %d\n", s[sect].num_reloc);
for(i
=0; i
< s
[sect
].
num_reloc; i
++)
{
#ifdef __COFF_DEBUG__
printk
("Relocate 0x%lx (index 0x%x): mode %d ",
rel
[i
].
offset, rel
[i
].
symbol, rel
[i
].
type);
#endif
idx
= rel
[i
].
symbol;
#ifdef __COFF_DEBUG__
printk
("%s --> 0x%lx (section %d)\n", syms
[idx
].
name,
syms
[idx
].
offset, syms
[idx
].
section);
#endif
switch (rel
[i
].
type)
{
case REL_TYPE_ELF_ABSOLUTE
:
destination
= s
[sect
].
base + rel
[i
].
offset + base
;
// Initial address
address
= *((DWORD
*)destination
);
break;
case REL_TYPE_RELATIVE
:
destination
= s
[sect
].
base + rel
[i
].
offset + base
;
address
= 0;
break;
default:
// (Non-)external symbols: only REL32 is supported
printk
("Unsupported relocation!\n");
printk
("Relocation Type: %d\n", rel
[i
].
type);
return -1;
}
if(syms
[idx
].
section == COMMON_SYMBOL
|| syms
[idx
].
section == EXTERN_SYMBOL
)
{
if(rel
[i
].
type == REL_TYPE_ELF_ABSOLUTE
)
address
+= syms
[idx
].
offset;
else if(rel
[i
].
type == REL_TYPE_RELATIVE
)
address
= syms
[idx
].
offset - destination
- 4;
}
else if(syms
[idx
].
section >= n
)
{
// Check if the section exists ...
printk
("Unsupported relocation section\n");
printk
("Section %d > %d\n", syms
[idx
].
section, n
);
printk
("Value 0x%lx\n", syms
[idx
].
offset);
return -1;
}
else
{
if(rel
[i
].
type == REL_TYPE_ELF_ABSOLUTE
)
{
address
+= base
+ s
[syms
[idx
].
section].
base + syms
[idx
].
offset;
}
else if(rel
[i
].
type == REL_TYPE_RELATIVE
)
{
address
= (s
[syms
[idx
].
section].
base + syms
[idx
].
offset) - (s
[sect
].
base + rel
[i
].
offset) - 4;
#ifdef __COFF_DEBUG__
printk
("Reloc: 0x%lx + 0x%lx - 0x%lx = 0x%lx ",
syms
[idx
].
offset,
s
[syms
[idx
].
section].
base,
rel
[i
].
offset , address
);
#endif
}
}
#ifdef __COFF_DEBUG__
printk
("0x%lx <--- 0x%lx\n", destination
, address
);
#endif
*((DWORD
*)destination
) = address
;
}
return 1;
};
/* Import symbol with suffix `name'
* NOTE: Any symbol with prefix `_' won't be found and be regarded as internal and hidden
*/
DWORD ELF_import_symbol
(struct file_ops
*kf
,
int n
,
struct symbol_info
*syms
,
char *name
,
int *sect
)
{
//printk("[COMMON] import_symbol...\n");
int i
;
int len
= strlen(name
);
for(i
= 0; i
< n
; i
++)
{
#ifdef __COFF_DEBUG__
printk
("Checking symbol %d [%d] --- Sect %d\n", i
, n
, syms
[i
].
section);
#endif
if ((syms
[i
].
section != EXTERN_SYMBOL
) && (syms
[i
].
section != COMMON_SYMBOL
) && (syms
[i
].
section != NULL_SYMBOL
))
{
#ifdef __COFF_DEBUG__
printk
("Compare %s, %s\n", syms
[i
].
name, name
);
#endif
if(syms
[i
].
name[0] != '_')
{
int sym_len
= strlen(syms
[i
].
name);
if(sym_len
>= len
&& strcmp(syms
[i
].
name+sym_len
-len
, name
) == 0)
{
#ifdef __COFF_DEBUG__
printk
("Found: %s --- 0x%x : 0x%lx\n",
syms
[i
].
name, syms
[i
].
section, syms
[i
].
offset);
#endif
break;
}
#ifdef __COFF_DEBUG__
else
{
printk
("Cmp failed --- Going to %d\n", i
);
}
#endif
}
}
#ifdef __COFF_DEBUG__
else
{
printk
("Skipped symbol --- Going to %d\n", i
);
}
#endif
}
if(i
< n
)
{ /* Symbol found */
*sect
= syms
[i
].
section;
return syms
[i
].
offset;
}
else
{
*sect
= -1;
printk
("Symbol not found!!!\n");
return 0;
}
};
DWORD ELF_load_relocatable
( struct file_ops
*kf
,
struct table_info
*tables
,
int n
,
struct section_info
*s
,
DWORD
*size
)
{
int i
;
DWORD needed_mem
= 0;
DWORD local_offset
= 0;
BYTE
*mem_space
, *where_to_place
;
// Allocate for the local bss at the mean time
for(i
=0; i
<n
; i
++)
{
needed_mem
+= s
[i
].
size;
}
needed_mem
+= tables
->local_bss_size
;
mem_space
= (BYTE
*)malloc(needed_mem
);
if(mem_space
== NULL
)
{
printk
("Unable to allocate memory for the program image\n");
return 0;
}
memset(mem_space
, 0, needed_mem
);
#ifdef __ELF_DEBUG__
printk
("Loading relocatable @%p; size 0x%lx\n", mem_space
, needed_mem
);
#endif
if(tables
->local_bss_size
!= 0)
tables
->local_bss
= (DWORD
)mem_space
+ needed_mem
- tables
->local_bss_size
;
else
tables
->local_bss
= 0;
for(i
=0; i
<n
; i
++)
{
#ifdef __ELF_DEBUG__
printk
("Section %d\t", i
);
#endif
if(s
[i
].
size != 0)
{
#ifdef __ELF_DEBUG__
printk
("Loading @ 0x%lx (0x%lx + 0x%lx)...\n",
(DWORD
)mem_space
+ (DWORD
)local_offset
,
(DWORD
)mem_space
, local_offset
);
#endif
where_to_place
= mem_space
+ local_offset
;
s
[i
].
base = local_offset
;
local_offset
+= s
[i
].
size;
kf
->file_seek
( kf
->file_offset
+ s
[i
].
fileptr, kf
->seek_set
);
if(s
[i
].
filesize > 0)
{
kf
->file_read
( where_to_place
, s
[i
].
filesize);
}
}
else
{
#ifdef __ELF_DEBUG__
printk
("Not to be loaded\n");
#endif
}
}
*size
= needed_mem
;
return (DWORD
)mem_space
;
};
void ELF_free_tables
(struct file_ops
*kf
,
struct table_info
*tables
,
struct symbol_info
*syms
,
struct section_info
*scndata
)
{
int i
;
for(i
= 0; i
< tables
->num_sections
; i
++)
if (scndata
[i
].
num_reloc != 0)
free( scndata
[i
].
reloc);
free(scndata
);
if (syms
!= NULL
) free(syms
);
if (tables
->string_size
!= 0) free((DWORD
*)tables
->string_buffer
);
if (tables
->section_names_size
!= 0) free(tables
->section_names
);
};