| /* ARC target-dependent stuff. Extension structure access functions | |
| Copyright 1995, 1997, 2000, 2001, 2004, 2005, 2007, 2009 | |
| Free Software Foundation, Inc. | |
| This file is part of libopcodes. | |
| This library 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 3, or (at your option) | |
| any later version. | |
| It 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 this program; if not, write to the Free Software | |
| Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, | |
| MA 02110-1301, USA. */ | |
| #include "sysdep.h" | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include "bfd.h" | |
| #include "arc-ext.h" | |
| #include "libiberty.h" | |
| /* Extension structure */ | |
| static struct arcExtMap arc_extension_map; | |
| /* Get the name of an extension instruction. */ | |
| const char * | |
| arcExtMap_instName(int opcode, int minor, int *flags) | |
| { | |
| if (opcode == 3) | |
| { | |
| /* FIXME: ??? need to also check 0/1/2 in bit0 for (3f) brk/sleep/swi */ | |
| if (minor < 0x09 || minor == 0x3f) | |
| return 0; | |
| else | |
| opcode = 0x1f - 0x10 + minor - 0x09 + 1; | |
| } | |
| else | |
| if (opcode < 0x10) | |
| return 0; | |
| else | |
| opcode -= 0x10; | |
| if (!arc_extension_map.instructions[opcode]) | |
| return 0; | |
| *flags = arc_extension_map.instructions[opcode]->flags; | |
| return arc_extension_map.instructions[opcode]->name; | |
| } | |
| /* Get the name of an extension core register. */ | |
| const char * | |
| arcExtMap_coreRegName(int value) | |
| { | |
| if (value < 32) | |
| return 0; | |
| return arc_extension_map.coreRegisters[value-32]; | |
| } | |
| /* Get the name of an extension condition code. */ | |
| const char * | |
| arcExtMap_condCodeName(int value) | |
| { | |
| if (value < 16) | |
| return 0; | |
| return arc_extension_map.condCodes[value-16]; | |
| } | |
| /* Get the name of an extension aux register. */ | |
| const char * | |
| arcExtMap_auxRegName(long address) | |
| { | |
| /* walk the list of aux reg names and find the name */ | |
| struct ExtAuxRegister *r; | |
| for (r = arc_extension_map.auxRegisters; r; r = r->next) { | |
| if (r->address == address) | |
| return (const char *) r->name; | |
| } | |
| return 0; | |
| } | |
| /* Recursively free auxilliary register strcture pointers until | |
| the list is empty. */ | |
| static void | |
| clean_aux_registers(struct ExtAuxRegister *r) | |
| { | |
| if (r -> next) | |
| { | |
| clean_aux_registers( r->next); | |
| free(r -> name); | |
| free(r -> next); | |
| r ->next = NULL; | |
| } | |
| else | |
| free(r -> name); | |
| } | |
| /* Free memory that has been allocated for the extensions. */ | |
| static void | |
| cleanup_ext_map(void) | |
| { | |
| struct ExtAuxRegister *r; | |
| struct ExtInstruction *insn; | |
| int i; | |
| /* clean aux reg structure */ | |
| r = arc_extension_map.auxRegisters; | |
| if (r) | |
| { | |
| (clean_aux_registers(r)); | |
| free(r); | |
| } | |
| /* clean instructions */ | |
| for (i = 0; i < NUM_EXT_INST; i++) | |
| { | |
| insn = arc_extension_map.instructions[i]; | |
| if (insn) | |
| free(insn->name); | |
| } | |
| /* clean core reg struct */ | |
| for (i = 0; i < NUM_EXT_CORE; i++) | |
| { | |
| if (arc_extension_map.coreRegisters[i]) | |
| free(arc_extension_map.coreRegisters[i]); | |
| } | |
| for (i = 0; i < NUM_EXT_COND; i++) { | |
| if (arc_extension_map.condCodes[i]) | |
| free(arc_extension_map.condCodes[i]); | |
| } | |
| memset(&arc_extension_map, 0, sizeof(struct arcExtMap)); | |
| } | |
| int | |
| arcExtMap_add(void *base, unsigned long length) | |
| { | |
| unsigned char *block = (unsigned char *) base; | |
| unsigned char *p = (unsigned char *) block; | |
| /* Clean up and reset everything if needed. */ | |
| cleanup_ext_map(); | |
| while (p && p < (block + length)) | |
| { | |
| /* p[0] == length of record | |
| p[1] == type of record | |
| For instructions: | |
| p[2] = opcode | |
| p[3] = minor opcode (if opcode == 3) | |
| p[4] = flags | |
| p[5]+ = name | |
| For core regs and condition codes: | |
| p[2] = value | |
| p[3]+ = name | |
| For aux regs: | |
| p[2..5] = value | |
| p[6]+ = name | |
| (value is p[2]<<24|p[3]<<16|p[4]<<8|p[5]) */ | |
| if (p[0] == 0) | |
| return -1; | |
| switch (p[1]) | |
| { | |
| case EXT_INSTRUCTION: | |
| { | |
| char opcode = p[2]; | |
| char minor = p[3]; | |
| char * insn_name = (char *) xmalloc(( (int)*p-5) * sizeof(char)); | |
| struct ExtInstruction * insn = | |
| (struct ExtInstruction *) xmalloc(sizeof(struct ExtInstruction)); | |
| if (opcode==3) | |
| opcode = 0x1f - 0x10 + minor - 0x09 + 1; | |
| else | |
| opcode -= 0x10; | |
| insn -> flags = (char) *(p+4); | |
| strcpy (insn_name, (char *) (p+5)); | |
| insn -> name = insn_name; | |
| arc_extension_map.instructions[(int) opcode] = insn; | |
| } | |
| break; | |
| case EXT_CORE_REGISTER: | |
| { | |
| char * core_name = (char *) xmalloc(((int)*p-3) * sizeof(char)); | |
| strcpy(core_name, (char *) (p+3)); | |
| arc_extension_map.coreRegisters[p[2]-32] = core_name; | |
| } | |
| break; | |
| case EXT_COND_CODE: | |
| { | |
| char * cc_name = (char *) xmalloc( ((int)*p-3) * sizeof(char)); | |
| strcpy(cc_name, (char *) (p+3)); | |
| arc_extension_map.condCodes[p[2]-16] = cc_name; | |
| } | |
| break; | |
| case EXT_AUX_REGISTER: | |
| { | |
| /* trickier -- need to store linked list to these */ | |
| struct ExtAuxRegister *newAuxRegister = | |
| (struct ExtAuxRegister *)malloc(sizeof(struct ExtAuxRegister)); | |
| char * aux_name = (char *) xmalloc ( ((int)*p-6) * sizeof(char)); | |
| strcpy (aux_name, (char *) (p+6)); | |
| newAuxRegister->name = aux_name; | |
| newAuxRegister->address = p[2]<<24 | p[3]<<16 | p[4]<<8 | p[5]; | |
| newAuxRegister->next = arc_extension_map.auxRegisters; | |
| arc_extension_map.auxRegisters = newAuxRegister; | |
| } | |
| break; | |
| default: | |
| return -1; | |
| } | |
| p += p[0]; /* move to next record */ | |
| } | |
| return 0; | |
| } | |
| /* Load hw extension descibed in .extArcMap ELF section. */ | |
| void | |
| build_ARC_extmap (text_bfd) | |
| bfd *text_bfd; | |
| { | |
| char *arcExtMap; | |
| bfd_size_type count; | |
| asection *p; | |
| for (p = text_bfd->sections; p != NULL; p = p->next) | |
| if (!strcmp (p->name, ".arcextmap")) | |
| { | |
| count = bfd_get_section_size (p); | |
| arcExtMap = (char *) xmalloc (count); | |
| if (bfd_get_section_contents (text_bfd, p, (PTR) arcExtMap, 0, count)) | |
| { | |
| arcExtMap_add ((PTR) arcExtMap, count); | |
| break; | |
| } | |
| free ((PTR) arcExtMap); | |
| } | |
| } |