/*************************************************************************
*
*               Copyright Mentor Graphics Corporation 2013
*                         All Rights Reserved.
*
* THIS WORK CONTAINS TRADE SECRET AND PROPRIETARY INFORMATION WHICH IS
* THE PROPERTY OF MENTOR GRAPHICS CORPORATION OR ITS LICENSORS AND IS
* SUBJECT TO LICENSE TERMS.
*
*************************************************************************/
/*************************************************************************
*
*   FILE NAME
*
*       arm_proc_reloc.c
*
*   DESCRIPTION
*
*       Support for ARM specific relocations.
*
*   DATA STRUCTURES
*
*       None
*
*   FUNCTIONS
*
*       PROC_ELF_Process_Reloc_Entry
*
*************************************************************************/

#include "nucleus.h"
#include "kernel/nu_kernel.h"
#include "os/kernel/process/core/proc_core.h"
#include "arm_proc_elf.h"
#include "os/kernel/process/linkload/proc_linkload.h"
#include "process/arch_proc_extern.h"

#ifdef  CFG_NU_OS_KERN_PROCESS_SHELL_ENABLE
#include "services/nu_services.h"
extern  BOOLEAN     PROC_Shell_Tryload;
extern  STATUS      PROC_Shell_Tryload_Status;
extern  NU_SHELL *  PROC_Shell_Tryload_Session;
#endif

/*************************************************************************
*
*   FUNCTION
*
*       PROC_ELF_Process_Reloc_Entry
*
*   DESCRIPTION
*
*       Processes the specified relocation entry. It handles the relocation
*       and linking both cases.
*
*   INPUTS
*
*       process - The process that is being relocated.
*
*       elf_decode_info - ELF decode information.
*
*       load_addr - Load address of the ELF object.
*
*       rel_entry - Relocation entry to be processed..
*
*   OUTPUTS
*
*       NU_SUCCESS - Indicates successful operation.
*
*       <other> - Indicates (other) internal error occurred.
*
*************************************************************************/
STATUS PROC_ELF_Process_Reloc_Entry(PROC_CB *process, PROC_ELF_DECODE_INFO *elf_decode_info, VOID *load_addr, Elf32_Rel *rel_entry)
{
    unsigned char   rel_type = ELF32_R_TYPE(rel_entry->r_info);
    STATUS          status = NU_SUCCESS;

    switch (rel_type)
    {
        case R_ARM_ABS32:                   /* 0x02 */
        {
            Elf32_Addr sym_addr = PROC_ELF_Get_Dynamic_Symbol_Addr(elf_decode_info, ELF32_R_SYM(rel_entry->r_info));

            if (sym_addr)
            {
                *((UINT32 *) (load_addr + rel_entry->r_offset)) = (UINT32)sym_addr + (UINT32)load_addr;
                break;
            }
            
            /* If we don't find the symbol, we fall through the following cases 
               to look for the symbol in the system-wide exported symbols. */
        }

        case R_ARM_GLOB_DAT:                /* 0x15 */
        case R_ARM_JUMP_SLOT:               /* 0x16 */
        {
            char *symbol_name = PROC_ELF_Get_Dynamic_Symbol_Name(elf_decode_info, ELF32_R_SYM(rel_entry->r_info));

            if (symbol_name)
            {
                VOID *sym_addr = NU_NULL;

                status = PROC_Get_Exported_Symbol_Address(process, symbol_name, &sym_addr);
                
                if (status == NU_SUCCESS)
                {
                    *((UINT32 *) (load_addr + rel_entry->r_offset)) = (UINT32)sym_addr;
                }
#ifdef  CFG_NU_OS_KERN_PROCESS_SHELL_ENABLE
                else if (PROC_Shell_Tryload == NU_TRUE)
                {
                    /* Check to see if this is the first error */
                    if (PROC_Shell_Tryload_Status == NU_SUCCESS)
                    {
                        /* Print string stating symbols are missing */
                        NU_Shell_Puts(PROC_Shell_Tryload_Session,"Symbol(s) not found:\n\r");
                    }

                    /* Print missing symbol name to the appropriate shell session */
                    NU_Shell_Puts(PROC_Shell_Tryload_Session, "    ");
                    NU_Shell_Puts(PROC_Shell_Tryload_Session, symbol_name);
                    NU_Shell_Puts(PROC_Shell_Tryload_Session, "\n\r");

                    /* Set another status to show that load failed */
                    PROC_Shell_Tryload_Status = status;

                    /* Set status to success so that other missing symbols can be found */
                    status = NU_SUCCESS;
                }
#endif
            }
        }
            break;

        case R_ARM_RELATIVE:                /* 0x17 */
        {
            UINT32    temp32;

            temp32 = *((UINT32 *) (load_addr + rel_entry->r_offset));
            *((UINT32 *) (load_addr + rel_entry->r_offset)) = temp32 + (UINT32)load_addr;
        }
            break;

        default:
            break;
    }
    
    return status;
}
