events.c 9.4 KB
/*==============================================================================
    Module: events.c
    $Revision: 1.1.1.2 $
    $Date: 2002/10/29 08:07:15 $
    $Author: blythe $
    $Source: /root/leakn64/depot/rf/sw/bbplayer/tools/gcord/cord/events.c,v $
==============================================================================*/
#include <sys/types.h>
#include <elf.h>
#include <cmplrs/events.h>

#include "global.h"
#include "update.h"

/*------------------------------------------------------------------------------
    find events or contents section (type SHT_MIPS_EVENTS or SHT_MIPS_CONTENT)
    for each event, perform process on that event.
------------------------------------------------------------------------------*/
void foreach_event(Elf *elf, void (*process)(Elf *, Full_Events *))
{
    Elf_Scn     *section;
    Full_Events full_event;
    char        *event, *event_end;
    int         i;

    /* find events and contents sections */
    for (i = 0; (section = elf_getscn(elf, i)) != NULL; i++)
    {
        Elf64_Shdr *shdr64;
        Elf32_Shdr *shdr32;
	int	events_or_content = 0;

	if (IS_64BIT())
	{
	    shdr64 = (Elf64_Shdr *)elf64_getshdr(section);
	    events_or_content = (shdr64->sh_type == SHT_MIPS_EVENTS ||
		shdr64->sh_type == SHT_MIPS_CONTENT);
	}
	else
	{
	    shdr32 = (Elf32_Shdr *)elf32_getshdr(section);
	    events_or_content = (shdr32->sh_type == SHT_MIPS_EVENTS ||
		shdr32->sh_type == SHT_MIPS_CONTENT);
	}

        if (events_or_content)
	{
            Elf_Data        *data = elf_getdata(section, 0);

            event = (char *) data->d_buf;
            event_end = (char *) data->d_buf +data->d_size;
            full_event.fevnt_index = (IS_64BIT())? 
			shdr64->sh_link : shdr32->sh_link;

            full_event.fevnt_offset = 0;

            while (event < event_end)
	    {
                event = event_get_next_rec(event,
                                full_event.fevnt_offset, &full_event);
                process(elf, &full_event);
            }
        }
    }

}

/*------------------------------------------------------------------------------
    process and update a single event entry
------------------------------------------------------------------------------*/
void process_update_one_event(Elf *elf, Full_Events *event)
{
    Elf_Scn	*section;
    Addr 	old_address;
    Addr 	new_address;
    Uint32	*p32;
    Uint64	*p64;
    MIPS_Inst	*new_inst;

    Uint32	entrysize = 4;		/* default entry size */
    int		i;

    section = elf_getscn(elf, event->fevnt_index);

    if (IS_32BIT())
    {
        Elf32_Shdr	*shdr = elf32_getshdr(section);
        old_address = event->fevnt_offset + shdr->sh_addr;
    }
    else
    {
        Elf64_Shdr	*shdr = elf64_getshdr(section);
        old_address = event->fevnt_offset + shdr->sh_addr;
    }

    switch (event->fevnt_type) 
    {
	case CK_SADDR_64:
		/*------------------
		   This is a simple 64-bit address data.
		------------------*/
	    ASSERT(IS_64BIT());
	    entrysize = 8;
	    for (i = 0; i < event->fevnt_arg1/entrysize; i++) 
	    {
		p64 = (Uint64 *) get_textdata_by_addr(bin.new_elf, 
			    old_address);
		if (IN_TEXT(*p64))
		{
		    new_address =  NEW_ADDR(*p64);
		    *p64 = new_address;
		    dump("address data at %#llx changed to %#llx",
			old_address, new_address);
		}
		old_address += entrysize;
	    }
	    break;

	case CK_SADDR_32:
		/*------------------
		   This is a simple 32-bit address data.
		------------------*/
	    ASSERT(IS_32BIT());
	    for (i = 0; i < event->fevnt_arg1/entrysize; i++) 
	    {
		p32 = (Uint32 *) get_textdata_by_addr(bin.new_elf, 
			    old_address);
		if (IN_TEXT(*p32))
		{
		    new_address =  NEW_ADDR(*p32);
		    *p32 = new_address;
		}
		old_address += entrysize;
	    }
	    break;

	case CK_GADDR_64:
		/*------------------
		   This is a gp relative 64-bit address data.
		------------------*/
	    entrysize += 4;
	case CK_GADDR_32:
		/*------------------
		   This is a gp relative 32-bit address data.
		------------------*/
	    error("CK_GADDR ot handled yet");
	    for (i = 0; i < event->fevnt_arg1; i++) 
	    {
		old_address += entrysize;
	    }
	    break;
	case CK_INSTR:
		warning("Don't do CK_INSTR");
	    break;
	case CK_DATA:
		warning("Don't do CK_DATA");
	    break;
	case CK_CADDR_32:
		warning("Don't do CK_CADDR_32");
	    break;
	case CK_CADDR_64:
		warning("Don't do CK_CADDR_64");
	    break;
	case CK_ALIGN:
		warning("Don't do CK_ALIGN");
	    break;
	case CK_NO_XFORM:
		warning("Don't do CK_NO_XFORM");
	    break;
	case CK_NO_REORDER:
		warning("Don't do CK_NO_REORDER");
	    break;
		/*------------------
		   This points to the lui of the gp prolog. The argument
		   refers to the 16 bit immediate contained in the second
		   half of the prolog gp value generation.
		------------------*/
	case EK_GP_PROLOG_HI:
	    if(IN_TEXT(old_address))
	    {
		Sint32 old_entry, new_entry;

		new_address = NEW_ADDR(old_address);
		new_inst = (MIPS_Inst *) 
			&(GET_NEWTEXT_BY_ADDR(new_address));
		old_entry = (event->fevnt_arg1);
		old_entry += new_inst->u_format.uimmediate << 16;
		old_entry = bin.gp_value - old_entry;
		new_entry = NEW_ADDR(old_entry);
		new_inst->u_format.uimmediate = SHI(bin.gp_value-new_entry);
	    }
	    else
	    {
		warning("incorrect EK_GP_PROLOG_HI");
	    }
	    break;
		/*------------------
		   This points to the addiu of the gp prolog. The argument
		   refers to the 16 bit immediate contained in the first
		   half of the prolog gp value generation.
		------------------*/
	case EK_GP_PROLOG_LO:
	    if(IN_TEXT(old_address))
	    {
		Addr old_entry, new_entry;

		new_address = NEW_ADDR(old_address);
		new_inst = (MIPS_Inst *) 
			&(GET_NEWTEXT_BY_ADDR(new_address));

		old_entry = bin.gp_value - ((event->fevnt_arg1 << 16) +
                      new_inst->i_format.simmediate);
		new_entry = NEW_ADDR(old_entry);
		new_inst->i_format.simmediate = SLO(bin.gp_value-new_entry);
	    }
	    else
	    {
		warning("incorrect EK_GP_PROLOG_LO");
	    }
	    break;
	case EK_64_HIGHEST:
	    if(IN_TEXT(old_address))
	    {
		Addr new_entry;

		new_address = NEW_ADDR(old_address);
		new_inst = (MIPS_Inst *) 
			&(GET_NEWTEXT_BY_ADDR(new_address));

		if (IN_TEXT(event->fevnt_arg1))
		{
		    new_entry = NEW_ADDR(event->fevnt_arg1);
		    new_inst->i_format.simmediate = S64_HIGHEST(new_entry);
		}
	    }
	    else
	    {
		warning("incorrect EK_64_HIGHEST");
	    }
	    break;
	case EK_64_HIGHER:
	    if(IN_TEXT(old_address))
	    {
		Addr new_entry;

		new_address = NEW_ADDR(old_address);
		new_inst = (MIPS_Inst *) 
			&(GET_NEWTEXT_BY_ADDR(new_address));

		if (IN_TEXT(event->fevnt_arg1))
		{
		    new_entry = NEW_ADDR(event->fevnt_arg1);
		    new_inst->i_format.simmediate = S64_HIGHER(new_entry);
		}
	    }
	    else
	    {
		warning("incorrect EK_64_HIGHER");
	    }
	    break;
	case EK_64_HIGH:
	    if(IN_TEXT(old_address))
	    {
		MIPS_Inst *new_inst;
		Addr new_entry;

		new_address = NEW_ADDR(old_address);
		new_inst = (MIPS_Inst *) 
			&(GET_NEWTEXT_BY_ADDR(new_address));

		if (IN_TEXT(event->fevnt_arg1))
		{
		    new_entry = NEW_ADDR(event->fevnt_arg1);
		    new_inst->i_format.simmediate = S64_HIGH(new_entry);
		}
	    }
	    else
	    {
		warning("incorrect EK_64_HIGH");
	    }
	    break;
	case EK_64_LOW:
	    if(IN_TEXT(old_address))
	    {
		Addr new_entry;

		new_address = NEW_ADDR(old_address);

		if (IN_TEXT(event->fevnt_arg1))
		{
		    new_entry = NEW_ADDR(event->fevnt_arg1);
		    GET_NEWTEXT_BY_ADDR(new_address).i_format.simmediate = 
			S64_LO(new_entry);
		}
	    }
	    else
	    {
		warning("incorrect EK_64_LOW");
	    }
	    break;

	case EK_HI:
	case EK_LO:
	    error("EK_HI EK_LO not handled");

	case EK_GOT_PAGE:
		/*------------------
		  lw t9 <offset_into_got>(gp)
		------------------*/
	    {
	    	int    offset;	/* offset off the got entry */
	    	Addr	target; /* address of the local target */

	    	ASSERT(IN_TEXT(old_address));
	    	new_address = NEW_ADDR(old_address);
	    	new_inst = (MIPS_Inst *) &(GET_NEWTEXT_BY_ADDR(new_address));
	    	offset = event->fevnt_arg1;
	    	target = got_content_by_gpoffset((int) 
				new_inst->i_format.simmediate) + offset;
	    	update_got_page(new_address, offset, target);
	    	break;
	    }

	case EK_GOT_OFST:
		/*------------------
		  addiu t9,t9,<offset>
		------------------*/
	    {
	    	int    offset;	/* offset off the got entry */
	    	Addr	target; /* address of the local target */

	    	ASSERT(IN_TEXT(old_address));
	    	new_address = NEW_ADDR(old_address);
	    	offset = GET_NEWTEXT_BY_ADDR(new_address).i_format.simmediate;
	    	target = got_content_by_gpoffset((int) event->fevnt_arg1) + 
			offset;
	    	update_got_offset(new_address, offset, target);
	    	break;
	    }

	case EK_FCALL_LOCAL:
	case EK_FCALL_EXTERN:
	case EK_FCALL_EXTERN_BIG:
	case EK_FCALL_MULT:
	case EK_FCALL_MULT_PARTIAL:
	case EK_NULL:
	case EK_ENTRY:
	case EK_EXIT:
	case EK_INCR_LOC:
	case EK_INCR_LOC_EXT:
	case EK_INCR_LOC_UNALIGNED:
	case EK_ADDR_RESET:
	case EK_PEND:
	    break;

	case EK_IF_ENTRY:
	case EK_SWITCH_32:
	case EK_SWITCH_64:
	case EK_GPREL:
	case EK_DEF:
	case EK_BB_START:
	case CK_DEFAULT:
	default:
		warning("Don't do event/content kind %d", event->fevnt_type);
	    break;

    }	/* switch */
}

/*------------------------------------------------------------------------------
    process and update .events and .contents sections.
------------------------------------------------------------------------------*/
void process_update_events(void)
{
    foreach_event(bin.new_elf, process_update_one_event);
}