view packages/services/objloader/current/src/objelf.c @ 2890:f8dd12428f1d

* cdl/objloader.cdl, src/objelf.c, src/relocate_ppc.c, src/relocate_arm.c: Eliminate dependency on CYGPKG_IO_FILEIO when the filesystem loader is not required. * src/relocate_arm.c : Added relocation software for the ARM . * src/relocate_i386.c : Added a structure containing the relocation names for every architecture. Only used (and compiled in) for pretty printing. * src/relocate_ppc.c : * include/relocate_arm.h : New header file for src/relocate_arm.c * cdl/objloader.cdl: Added the option to relocate for the ARM. * src/objloader.c : * src/objelf.c : Erased some redundant debug printout and improved the readability of the rest. * src/loader_memory.c : Added the code to allow the objloader package to load libraries from ROM memory, instead from a file system. The various sections of the library are the copied from ROM to RAM and relocated. * include/loader_memory.h : New header file for src/loader_memory.c * src/objloader.c : Added a line to include the loader_memory header file and code to flush the cache when calling cyg_ldr_find_symbol(). * cdl/objloader.cdl: Added one check boxe for each of the supported ways of loading libraries (two for now) so that the user can compile out the loading methods not used.
author jld
date Fri, 03 Jul 2009 14:49:44 +0000
parents 74dbf4c3f2e1
children 7cbaadc92c67
line wrap: on
line source

/* =================================================================
 *
 *      objelf.c
 *
 *      Relocation routine for eCos loader.
 *
 * ================================================================= 
 * ####ECOSGPLCOPYRIGHTBEGIN####                                     
 * -------------------------------------------                       
 * This file is part of eCos, the Embedded Configurable Operating System.
 * Copyright (C) 2005, 2008, 2009 Free Software Foundation, Inc.                 
 *
 * 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.,
 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 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 v2.                                        
 *
 * 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):    Anthony Tonizzo (atonizzo@gmail.com)
 *  Contributors: nickg@ecoscentric.com
 *  Date:         2005-05-13
 *  Purpose:      
 *  Description:  
 *               
 * ####DESCRIPTIONEND####
 * 
 * =================================================================
 */

#include <cyg/infra/diag.h>     // For diagnostic printing.
#include <cyg/hal/hal_tables.h>
#include <stdio.h>

#include <pkgconf/objloader.h>
#include <cyg/objloader/elf.h>
#include <cyg/objloader/objelf.h>

CYG_HAL_TABLE_BEGIN(cyg_ldr_table, ldr_table);
CYG_HAL_TABLE_END(cyg_ldr_table_end, ldr_table);

__externC cyg_ldr_table_entry cyg_ldr_table[];
__externC cyg_ldr_table_entry cyg_ldr_table_end[];

#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 0
void 
cyg_ldr_print_section_data(PELF_OBJECT p)
{
    int    i;
    char   strname[32];
    char  *p_strtab = (char*)p->sections[p->p_elfhdr->e_shstrndx];
    
    diag_printf("Section Headers:\n"); 
    diag_printf("----------------------------------------------------------\n"); 
    diag_printf("[Nr]  Name                  Addr    Offset"
                                                         "    Size     Info\n");
    for (i = 0; i < p->p_elfhdr->e_shnum; i++)
    {
        sprintf(strname, "%s", p_strtab + p->p_sechdr[i].sh_name);
        while (strlen(strname) < 20)
            strcat(strname, " ");
        diag_printf("[%2d] %s %08X %08X %08X %08X\n",  
                     i, 
                     strname,
                     p->p_sechdr[i].sh_addr,
                     p->p_sechdr[i].sh_offset,
                     p->p_sechdr[i].sh_size,
                     p->p_sechdr[i].sh_info);
    }                 
    diag_printf("\n"); 
}

void 
cyg_ldr_print_symbol_names(PELF_OBJECT p)
{
    int        i;
    Elf32_Sym *p_symtab = (Elf32_Sym*)p->sections[p->hdrndx_symtab];
    char      *p_strtab = (char*)p->sections[p->hdrndx_strtab];
//    char       strname[32];

    // Total number of entries in the symbol table.
    int symtab_entries = p->p_sechdr[p->hdrndx_symtab].sh_size / 
                                p->p_sechdr[p->hdrndx_symtab].sh_entsize;
    diag_printf("Symbol Table Entries\n"); 
    diag_printf("----------------------------------------\n"); 
    diag_printf("[Nr]   Value   Size  Ndx     Name\n"); 
    for (i = 1; i < symtab_entries; i++)
        diag_printf("[%3d] %08X %04d %5d %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);
    diag_printf("\n");
}

void 
cyg_ldr_print_rel_names(PELF_OBJECT p)
{
    int        i, j, r_entries, sym_index;
    Elf32_Sym *p_symtab = (Elf32_Sym*)p->sections[p->hdrndx_symtab];
    char      *p_strtab = (char*)p->sections[p->hdrndx_strtab];
    char      *p_shstrtab = (char*)p->sections[p->p_elfhdr->e_shstrndx];
#if ELF_ARCH_RELTYPE == Elf_Rela        
    Elf32_Rela*   p_rela;
#else
    Elf32_Rel*    p_rel;
#endif
    char       strname[32];

    for (i = 1; i < p->p_elfhdr->e_shnum; i++)
    {
        if ((p->p_sechdr[i].sh_type == SHT_REL) ||
                                  (p->p_sechdr[i].sh_type == SHT_RELA))
        {                                  
            // Calculate the total number of entries in the .rela section.
            r_entries = p->p_sechdr[i].sh_size / p->p_sechdr[i].sh_entsize;

            diag_printf("\n\nSymbols at: %s\n\n", 
                         p_shstrtab + p->p_sechdr[i].sh_name);
#if ELF_ARCH_RELTYPE == Elf_Rela        
            p_rela = (Elf32_Rela*)cyg_ldr_load_elf_section(p, i);
            printf("Offset    Info      Name [+ Addend]\n");
#else
            p_rel = (Elf32_Rel*)cyg_ldr_load_elf_section(p, i);
            printf("Offset    Info     Name\n");
#endif

            for (j = 0; j < r_entries; j++)
            {
                sprintf(strname, 
                         "%08X  %08X  ", 
#if ELF_ARCH_RELTYPE == Elf_Rela        
                        p_rela[j].r_offset,
                        p_rela[j].r_info 
#else
                        p_rel[j].r_offset,
                        p_rel[j].r_info 
#endif
                        );

                diag_printf(strname);         

#if ELF_ARCH_RELTYPE == Elf_Rela        
                cyg_uint8 sym_type = ELF32_R_SYM(p_rela[j].r_info);
#else
                cyg_uint8 sym_type = ELF32_R_SYM(p_rel[j].r_info);
#endif
                if (strlen (p_strtab + p_symtab[sym_type].st_name) > 0)
                    diag_printf(p_strtab + p_symtab[sym_type].st_name);         
                else 
                {   
                    // If the symbol name is not available, then print
                    //  the name of the section.
                    sym_index = p_symtab[sym_type].st_shndx;                    
                    diag_printf(p_shstrtab + p->p_sechdr[sym_index].sh_name);         
                }    
#if ELF_ARCH_RELTYPE == Elf_Rela        
                if (p_rela[j].r_addend != 0)
                    diag_printf(" + %08X", p_rela[j].r_addend);
#endif
                diag_printf("\n");         
            }            
            // After all the printing is done, the relocation table can 
            //  be dumped.
            cyg_ldr_delete_elf_section(p, i);
        } 
    }    
}
#endif // DEBUG_PRINT

static void
*cyg_ldr_local_address(PELF_OBJECT p, cyg_uint32 sym_index)
{
    cyg_uint32 data_sec, addr;
    Elf32_Sym *p_symtab;

    p_symtab = (Elf32_Sym*)cyg_ldr_section_address(p, p->hdrndx_symtab);
    
    // Find out the section number in which the data for this symbol is 
    //  located.
    data_sec = p_symtab[sym_index].st_shndx;    

    // From the section number we get the start of the memory area in 
    //  memory.
    addr = (cyg_uint32)cyg_ldr_section_address(p, data_sec);

    // And now return the address of the data.
    return (void*)(addr + p_symtab[sym_index].st_value);
}    

void
*cyg_ldr_external_address(PELF_OBJECT p, cyg_uint32 sym_index)
{
    cyg_uint8*    tmp2;
    Elf32_Sym *p_symtab;
    cyg_uint8 *p_strtab;
    cyg_ldr_table_entry *entry = cyg_ldr_table;
  
  
    p_symtab = (Elf32_Sym*)cyg_ldr_section_address(p, p->hdrndx_symtab);
    p_strtab = (cyg_uint8*)cyg_ldr_section_address(p, p->hdrndx_strtab);
  
    // This is the name of the external reference to search.
    tmp2 = p_strtab + p_symtab[sym_index].st_name;
    while (entry != cyg_ldr_table_end)
    {
        if (!strcmp((const char*)tmp2, entry->symbol_name ))
            return entry->handler;
        entry++;
    }

    // Symbol not found.
    return 0;
}

// input:
// p          : Pointer to the elf file object
// sym_index  : Index of the symbol to be searched (in the SYMTAB)
//
// out:
// 0          : Symbol not found
// Other      : Address of the symbol in absolute memory.
void 
*cyg_ldr_symbol_address(PELF_OBJECT p, cyg_uint32 sym_index)
{
    cyg_uint32 addr;
    Elf32_Sym *p_symtab = (Elf32_Sym*)cyg_ldr_section_address(p, 
                                                              p->hdrndx_symtab);
    cyg_uint8 sym_info = p_symtab[sym_index].st_info;
    switch (ELF32_ST_TYPE(sym_info))
    {
    case STT_NOTYPE:
    case STT_FUNC:
    case STT_OBJECT:
        switch (ELF32_ST_BIND(sym_info))
        {
        case STB_LOCAL:
        case STB_GLOBAL:
            if (p_symtab[sym_index].st_shndx == SHN_UNDEF) 
                return cyg_ldr_external_address(p, sym_index);
            else
                return cyg_ldr_local_address(p, sym_index);
        case STB_WEAK:
            addr = (cyg_uint32)cyg_ldr_external_address(p, sym_index);
            if (addr != 0)
                return (void*)addr;
            else    
                return cyg_ldr_local_address(p, sym_index);
        default:
            return 0;
        }
        break;
    case STT_SECTION:
        // Return the starting address of a section, given its index.
        return (void*)cyg_ldr_section_address(p, p_symtab[sym_index].st_shndx);
    default:
        return 0;
    }
}

// Loads the relocation information, relocates, and dumps the relocation
//  information once the process is complete.
cyg_int32 
cyg_ldr_relocate_section(PELF_OBJECT p, cyg_uint32 r_shndx)
{
    int         i, rc;
#if ELF_ARCH_RELTYPE == Elf_Rela        
    Elf32_Rela* p_rela = (Elf32_Rela*)cyg_ldr_load_elf_section(p, r_shndx);
#else
    Elf32_Rel*  p_rel = (Elf32_Rel*)cyg_ldr_load_elf_section(p, r_shndx);
#endif

#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 0
    Elf32_Sym *p_symtab = (Elf32_Sym*)cyg_ldr_section_address(p, 
                                                           p->hdrndx_symtab);
    char      *p_strtab = (char*)cyg_ldr_section_address(p, p->hdrndx_strtab);
    char      *p_shstrtab = (char*)cyg_ldr_section_address(p, 
                                                     p->p_elfhdr->e_shstrndx);
#endif

    // Now we can get the address of the contents of the section to modify.
    cyg_uint32 r_target_shndx = p->p_sechdr[r_shndx].sh_info;
    cyg_uint32 r_target_addr  = (cyg_uint32)cyg_ldr_section_address(p, 
                                                                r_target_shndx);

#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 0
    diag_printf("Relocating section \"%s\"\n",
            p_shstrtab + p->p_sechdr[r_target_shndx].sh_name);
    diag_printf("----------------------------------------\n"); 
#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 1
    diag_printf(" Ndx  Type             Offset   Name\"\n");
#endif
#endif

    // Perform relocatation for each of the members of this table.
    cyg_uint32 r_entries = p->p_sechdr[r_shndx].sh_size / 
                                             p->p_sechdr[r_shndx].sh_entsize;
    for (i = 0; i < r_entries; i++)
    {
#if ELF_ARCH_RELTYPE == Elf_Rela        
        Elf32_Addr  r_offset = p_rela[i].r_offset; 
        Elf32_Word  r_type   = ELF32_R_TYPE(p_rela[i].r_info); 
        cyg_uint32  sym_index = ELF32_R_SYM(p_rela[i].r_info);
        Elf32_Sword r_addend  = p_rela[i].r_addend; 
#else
        Elf32_Addr  r_offset  = p_rel[i].r_offset; 
        Elf32_Word  r_type    = ELF32_R_TYPE(p_rel[i].r_info); 
        cyg_uint32  sym_index = ELF32_R_SYM(p_rel[i].r_info);
        Elf32_Sword r_addend  = 0; 
#endif

        cyg_uint32 sym_value = (cyg_uint32)cyg_ldr_symbol_address(p, sym_index);
        
        // This is architecture dependent, and deals with whether we have
        //  '.rel' or '.rela' sections.
#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 1
        diag_printf("%5d %s %08X  ",
                     sym_index,
                     relocation_name[r_type],
                     r_offset);
        if (strlen(p_strtab + p_symtab[sym_index].st_name) > 0)
            diag_printf(p_strtab + p_symtab[sym_index].st_name);         
        else 
        {   
            // If the symbol name is not available, then print
            //  the name of the section.
            cyg_uint32 sec_ndx = p_symtab[sym_index].st_shndx;                    
            diag_printf(p_shstrtab + p->p_sechdr[sec_ndx].sh_name);         
        }    
        diag_printf("\n");         
#endif
        rc = cyg_ldr_relocate(r_type,
                              r_target_addr + r_offset, 
                              sym_value + r_addend);
        if (rc != 0)
        {
#if CYGPKG_SERVICES_OBJLOADER_DEBUG_LEVEL > 1
            diag_printf("Error while relocating symbol: %s\n",
                        p_strtab + p_symtab[sym_index].st_name);
            return -1;
#endif
        }    
    }

    // After the relocation is done, the relocation table can be dumped.
    cyg_ldr_delete_elf_section(p, r_shndx);
    return 0;
}