Mercurial > ecos-v3_0-branch
diff packages/services/objloader/current/src/objloader.c @ 2011:662e1b34b811
Added OBJLOADER package.
| author | nickg |
|---|---|
| date | Thu, 07 Jul 2005 10:55:12 +0000 |
| parents | |
| children | 8bf90d16b837 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/services/objloader/current/src/objloader.c @@ -0,0 +1,462 @@ +/* ================================================================= + * + * objelf.c + * + * An object loader for eCos + * + * ================================================================= + * ####ECOSGPLCOPYRIGHTBEGIN#### + * ------------------------------------------- + * This file is part of eCos, the Embedded Configurable Operating + * System. + * Copyright (C) 2005 eCosCentric Ltd. + * + * eCos is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 or (at your option) + * any later version. + * + * eCos is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with eCos; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * As a special exception, if other files instantiate templates or + * use macros or inline functions from this file, or you compile this + * file and link it with other works to produce a work based on this + * file, this file does not by itself cause the resulting work to be + * covered by the GNU General Public License. However the source code + * for this file must still be made available in accordance with + * section (3) of the GNU General Public License. + * + * This exception does not invalidate any other reasons why a work + * based on this file might be covered by the GNU General Public + * License. + * + * ------------------------------------------- + * ####ECOSGPLCOPYRIGHTEND#### + * ================================================================= + * #####DESCRIPTIONBEGIN#### + * + * Author(s): atonizzo@lycos.com + * Contributors: nickg@ecoscentric.com + * Date: 2005-05-13 + * Purpose: + * Description: + * + * ####DESCRIPTIONEND#### + * + * ================================================================= + */ + +#include <cyg/infra/diag.h> // For diagnostic printing. +#include <cyg/infra/cyg_ass.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <pkgconf/objloader.h> +#include <cyg/objloader/elf.h> +#include <cyg/objloader/objelf.h> +#include <cyg/objloader/loader_fs.h> + +cyg_uint8 *cyg_ldr_last_error; + +void *cyg_ldr_malloc( size_t ) CYGBLD_ATTRIB_WEAK; +void +*cyg_ldr_malloc( size_t s ) +{ + return malloc( s ); +} + +void cyg_ldr_free( void * ) CYGBLD_ATTRIB_WEAK; +void +cyg_ldr_free( void *s ) +{ + free( s ); +} + +void +cyg_ldr_delete_elf_section( PELF_OBJECT p, cyg_uint32 idx ) +{ + cyg_ldr_free( p->sections[idx] ); + p->sections[idx] = 0; +} + +// Frees all the memory allocated for a particular ELF object. Also calls +// the close() function to close files or sockets, and finally frees up +// the ELF object altogether. +static void +cyg_ldr_free_elf_object( PELF_OBJECT p ) +{ + cyg_int32 i; + + for ( i = 0; i < p->p_elfhdr->e_shnum + 1; i++ ) + if ( p->sections[i] ) + cyg_ldr_delete_elf_section( p, i ); + + if ( p->sections != 0 ) + cyg_ldr_free( p->sections ); + + if ( p->p_sechdr != 0 ) + cyg_ldr_free( p->p_sechdr ); + + if ( p->p_elfhdr != 0 ) + cyg_ldr_free( p->p_elfhdr ); + + p->close( p ); + cyg_ldr_free( p ); +} + +static cyg_uint32 +cyg_ldr_find_common_size( PELF_OBJECT p ) +{ + cyg_int32 i, symtab_entries, common_size = 0; + Elf32_Sym *p_symtab = (Elf32_Sym*)p->sections[p->hdrndx_symtab]; + + // Total number of entries in the symbol table. + symtab_entries = p->p_sechdr[p->hdrndx_symtab].sh_size / + p->p_sechdr[p->hdrndx_symtab].sh_entsize; + for ( i = 1; i < symtab_entries; i++ ) + if ( p_symtab[i].st_shndx == SHN_COMMON ) + { + // In the case of an SHN_COMMON symbol the st_value field holds + // alignment constraints. + cyg_uint32 boundary = p_symtab[i].st_value - 1; + + // Calculate the next byte boundary. + common_size = ( common_size + boundary ) & ~boundary; + common_size += p_symtab[i].st_size; + } + +#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 0 + diag_printf( "common_size = %d\n", common_size ); +#endif + return common_size; +} + +// Allocates memory and loads the contents of a specific ELF section. +// Returns the address of the newly allocated memory, of 0 for any error. +cyg_int32 +*cyg_ldr_load_elf_section( PELF_OBJECT p, cyg_uint32 idx ) +{ + cyg_uint32 *addr; + + addr = cyg_ldr_malloc( p->p_sechdr[idx].sh_size ); + CYG_ASSERT( addr != 0, "Cannot malloc() section" ); + if ( addr == 0 ) + { + cyg_ldr_last_error = "ERROR IN MALLOC"; + return (void*)0; + } + p->seek( p, p->p_sechdr[idx].sh_offset ); + p->read( p, sizeof( char ), p->p_sechdr[idx].sh_size, addr ); + return addr; +} + +// Returns the starting address of a section. If the section is not already +// loaded in memory, area for it will be allocated and the section will be +// loaded. +cyg_int32 +*cyg_ldr_section_address( PELF_OBJECT p, cyg_uint32 idx ) +{ + if ( p->sections[idx] == 0 ) + p->sections[idx] = cyg_ldr_load_elf_section( p, idx ); + + return p->sections[idx]; +} + +void +*cyg_ldr_find_symbol( void* handle, cyg_uint8* sym_name ) +{ + PELF_OBJECT p = (PELF_OBJECT)handle; + cyg_uint8* tmp2; + cyg_int32 i, symtab_entries; + cyg_uint8 *p_strtab = (cyg_uint8*)p->sections[p->hdrndx_strtab]; + Elf32_Sym *p_symtab = (Elf32_Sym*)p->sections[p->hdrndx_symtab]; + + symtab_entries = p->p_sechdr[p->hdrndx_symtab].sh_size / + p->p_sechdr[p->hdrndx_symtab].sh_entsize; + + for ( i = 0; i < symtab_entries; i++ ) + { + tmp2 = p_strtab + p_symtab[i].st_name; + if ( !strcmp( tmp2, sym_name ) ) + return cyg_ldr_symbol_address( p, i ); + } + + // Symbol not found. + cyg_ldr_last_error = "SYMBOL NOT FOUND"; + return 0; +} + +static cyg_uint8 +*cyg_ldr_sanity_check( PELF_OBJECT p ) +{ + if ( ( p->p_elfhdr->e_ident[EI_MAG0] != ELFMAG0 ) || + ( p->p_elfhdr->e_ident[EI_MAG1] != ELFMAG1 ) || + ( p->p_elfhdr->e_ident[EI_MAG2] != ELFMAG2 ) || + ( p->p_elfhdr->e_ident[EI_MAG3] != ELFMAG3 ) || + ( p->p_elfhdr->e_ident[EI_CLASS] != ELFCLASS32 ) ) + return "INVALID ELF HEADER"; + + // We only work with relocatable files. No dynamic linking. + if ( p->p_elfhdr->e_type != ET_REL ) + return "NOT RELOCATABLE"; + + // These #defines are sitting in the hal. + if ( p->p_elfhdr->e_machine != ELF_ARCH_MACHINE_TYPE ) + return "INVALID ARCHITECTURE"; + + if ( p->p_elfhdr->e_ident[EI_DATA] != ELF_ARCH_ENDIANNESS ) + return "INVALID ENDIAN"; + return 0; } + +// Load only the ELF header and the sections header. These are the only +// sections loaded during library initialization. All the other sections +// will be loaded on demand when needed during the relocation process and, +// when possible, dumped after use. +static cyg_int32 +cyg_ldr_load_sections( PELF_OBJECT p ) +{ + cyg_uint8 *error_string; + cyg_int32 idx; + + // Load the ELF header. + p->p_elfhdr = (Elf32_Ehdr*)cyg_ldr_malloc( sizeof( Elf32_Ehdr ) ); + CYG_ASSERT( p->p_elfhdr != 0, "Cannot malloc() p->p_elfhdr" ); + if ( p->p_elfhdr == 0 ) + return -1; + p->seek( p, 0 ); + p->read( p, sizeof( char ), sizeof( Elf32_Ehdr ), p->p_elfhdr ); + error_string = cyg_ldr_sanity_check( p ); + if ( error_string != 0 ) + { + cyg_ldr_last_error = "ERROR IN ELF HEADER"; + return -1; + } + + // Allocate an array that can hold an address to all the section of this + // library. This is not strictly optimal, since some sections do not + // need to be loaded all the time. Allocate an extra pointer for the + // COMMON area. + p->sections = cyg_ldr_malloc( ( p->p_elfhdr->e_shnum + 1 ) * + sizeof( cyg_uint32 ) ); + CYG_ASSERT( p->sections != 0, "Cannot malloc() p->sections" ); + if ( p->sections == 0 ) + { + cyg_ldr_last_error = "ERROR IN MALLOC"; + return -1; + } + memset( p->sections, 0, ( p->p_elfhdr->e_shnum + 1 ) * + sizeof( cyg_uint32 ) ); + + // Now that the header is loaded, load the sections header. + p->p_sechdr = (Elf32_Shdr*)cyg_ldr_malloc( + p->p_elfhdr->e_shnum * p->p_elfhdr->e_shentsize ); + CYG_ASSERT( p->p_sechdr != 0, "Cannot malloc() p->p_sechdr" ); + if ( p->p_sechdr == 0 ) + { + cyg_ldr_last_error = "ERROR IN MALLOC"; + return -1; + } + p->seek( p, p->p_elfhdr->e_shoff ); + p->read( p, p->p_elfhdr->e_shentsize, p->p_elfhdr->e_shnum, p->p_sechdr ); + + // Load the section header string table. This is a byte oriented table, + // so alignment is not an issue. + idx = p->p_elfhdr->e_shstrndx; + p->sections[idx] = cyg_ldr_load_elf_section( p, idx ); + return 0; +} + +PELF_OBJECT +cyg_ldr_open_library( CYG_ADDRWORD ptr, cyg_int32 mode ) +{ + Elf32_Sym *p_symtab; + cyg_uint8 *p_shstrtab; + PELF_OBJECT e_obj; + int (*fn)(void); + cyg_int32 i, rc, symtab_entries; + cyg_uint32 common_size; + + // In the future there might be a switch() (against 'mode') that calls an + // open function other than cyg_ldr_open_library_fs(). These function + // fetch and open a library using ftp, http or libraries that are already + // in ROM. + e_obj = cyg_ldr_open_library_fs( (cyg_uint8*)ptr ); + if ( e_obj == 0 ) + return 0; + rc = cyg_ldr_load_sections( e_obj ); + if ( rc != 0 ) + { + cyg_ldr_free_elf_object( e_obj ); + return 0; + } + + // Find the section index for the .shstrtab section. The names of the + // sections are held here, and are the only way to identify them. + p_shstrtab = (cyg_uint8*)cyg_ldr_section_address( e_obj, + e_obj->p_elfhdr->e_shstrndx ); + if ( p_shstrtab == 0 ) + { + cyg_ldr_free_elf_object( e_obj ); + return 0; + } + + // .symtab section and .strtab. We have to go through the section names + // to find where they are. + for ( i = 1; i < e_obj->p_elfhdr->e_shnum; i++ ) + { + // Now look for the index of .symtab. These are the symbols needed for + // the symbol retrieval as well as relocation. + if ( !strcmp( p_shstrtab + e_obj->p_sechdr[i].sh_name, + ELF_STRING_symtab ) ) + { + e_obj->hdrndx_symtab = i; + e_obj->sections[i] = cyg_ldr_load_elf_section( e_obj, i ); + if ( e_obj->sections[i] == 0 ) + { + cyg_ldr_free_elf_object( e_obj ); + return 0; + } + } + + // Load the table with the names of all the symbols. We need this + // to compare the name of external references symbols against the + // names in the in the CYG_HAL_TABLE provided by the user. + if ( !strcmp( p_shstrtab + e_obj->p_sechdr[i].sh_name, + ELF_STRING_strtab ) ) + { + e_obj->hdrndx_strtab = i; + e_obj->sections[i] = cyg_ldr_load_elf_section( e_obj, i ); + if ( e_obj->sections[i] == 0 ) + { + cyg_ldr_free_elf_object( e_obj ); + return 0; + } + } + } + + CYG_ASSERT( e_obj->hdrndx_symtab != 0, "No symtab index found" ); + CYG_ASSERT( e_obj->hdrndx_strtab != 0, "No strtab index found" ); + + // Now look for symbols in the COMMON area. The COMMON symbols are a + // special case, because the area they reside in must be sized up + // and allocated separately from the other sections, which appear in + // the sections header and can be read out of the library itself. + // Extra room in the 'sections' array has already been allocated to hold + // the pointer to the commons area. + common_size = cyg_ldr_find_common_size( e_obj ); + if ( common_size != 0 ) + { + cyg_uint32 com_shndx = e_obj->p_elfhdr->e_shnum; + cyg_int32 com_offset = 0; + + e_obj->sections[com_shndx] = (cyg_uint32*)cyg_ldr_malloc( common_size ); + CYG_ASSERT( e_obj->sections[com_shndx] != 0, + "Cannot malloc() the COMMONS" ); + + if ( e_obj->sections[com_shndx] == 0 ) + { + cyg_ldr_free_elf_object( e_obj ); + return 0; + } + + // Now find all the symbols in the SHN_COMMON area and make + // them point to the newly allocated COM area. + symtab_entries = e_obj->p_sechdr[e_obj->hdrndx_symtab].sh_size / + e_obj->p_sechdr[e_obj->hdrndx_symtab].sh_entsize; + p_symtab = (Elf32_Sym*)e_obj->sections[e_obj->hdrndx_symtab]; + +#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 1 + diag_printf( "Num Value Size Ndx Name\n" ); +#endif + for ( i = 1; i < symtab_entries; i++ ) + { +#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 1 + cyg_uint8 *p_strtab = (cyg_uint8*)cyg_ldr_section_address( e_obj, + e_obj->hdrndx_strtab ); +#endif + if ( p_symtab[i].st_shndx == SHN_COMMON ) + { + cyg_uint32 boundary = p_symtab[i].st_value - 1; + // Calculate the next byte boundary. + com_offset = ( com_offset + boundary ) & ~boundary; + p_symtab[i].st_shndx = com_shndx; + p_symtab[i].st_value = com_offset; + com_offset += p_symtab[i].st_size; + } + +#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 1 + diag_printf( "%03d %08X %04X %03d %s\n", + i, + p_symtab[i].st_value, + p_symtab[i].st_size, + p_symtab[i].st_shndx, + p_strtab + p_symtab[i].st_name ); +#endif + } + } + +#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 0 + cyg_ldr_print_section_data( e_obj ); +#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 1 + cyg_ldr_print_symbol_names( e_obj ); +#endif +#endif + + for ( i = 1; i < e_obj->p_elfhdr->e_shnum; i++ ) + { + // Find all the '.rel' or '.rela' sections and relocate them. + if ( ( e_obj->p_sechdr[i].sh_type == SHT_REL ) || + ( e_obj->p_sechdr[i].sh_type == SHT_RELA ) ) + { + // Load and relocate the section. + rc = cyg_ldr_relocate_section( e_obj, i ); + if ( rc < 0 ) + { +#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 0 + ELFDEBUG( "Relocation unsuccessful\n" ); +#endif + cyg_ldr_free_elf_object( e_obj ); + return 0; + } + } + } + + // Synch up the caches before calling any function in the library. + cyg_ldr_flush_cache(); + + // Run the library initialization code. + fn = cyg_ldr_find_symbol( e_obj, "library_open" ); + if ( fn != 0 ) + fn(); + return ((void*)e_obj); +} + +cyg_uint8 +*cyg_ldr_error( void ) +{ + cyg_uint8* p = cyg_ldr_last_error; + cyg_ldr_last_error = NULL; + return p; +} + +void cyg_ldr_close_library( void* handle ) +{ + void (*fn)(void); + + PELF_OBJECT p = (PELF_OBJECT)handle; + fn = cyg_ldr_find_symbol( p, "library_close" ); + if ( fn != 0 ) + fn(); + + cyg_ldr_free_elf_object( p ); + p = 0; +} +
