From 64c5424c733cb06ecfe34708ee5c554c106f8c45 Mon Sep 17 00:00:00 2001 From: Nelson Chu Date: Wed, 22 Aug 2018 10:45:12 +0800 Subject: [PATCH 03/13] NDS32: Support mapping symbol for debugging usage. For some cases (the manually assembly code or jump table generated by compiler), rodata will be placed into .text section rather than .roadat section. It is hard to debug since the disassmebler may recognize these rodata as instructions but in fact, they are not. To solve the problem, we add some special symbols for these rodata, and then the disassmebler can recognize them through these symbols. gas/ * config/tc-nds32.c (add_mapping_symbol_for_align, make_mapping_symbol, add_mapping_symbol): New functions. * config/tc-nds32.h (enum mstate): New. (nds32_segment_info_type): Likewise. include/ * dis-asm.h (disassemble_init_nds32): Declared. opcodes/ * disassemble.c (disassemble_init_for_target): Add disassemble_init_nds32. * nds32-dis.c (eum map_type): New. (nds32_private_data): Likewise. (get_mapping_symbol_type, is_mapping_symbol, nds32_symbol_is_valid, nds32_add_opcode_hash_table, disassemble_init_nds32): New functions. (print_insn_nds32): Updated. --- gas/config/tc-nds32.c | 79 ++++++++- gas/config/tc-nds32.h | 15 ++ include/dis-asm.h | 1 + opcodes/disassemble.c | 5 + opcodes/nds32-dis.c | 368 ++++++++++++++++++++++++++++++++++++++---- 5 files changed, 440 insertions(+), 28 deletions(-) diff --git a/gas/config/tc-nds32.c b/gas/config/tc-nds32.c index 39a4ca4bc1..226857e69d 100644 --- a/gas/config/tc-nds32.c +++ b/gas/config/tc-nds32.c @@ -1931,6 +1931,9 @@ static int nds32_parse_arch (const char *str); static int nds32_parse_baseline (const char *str); static int nds32_parse_freg (const char *str); static int nds32_parse_abi (const char *str); +static void add_mapping_symbol (enum mstate state, + unsigned int padding_byte, + unsigned int align); static struct nds32_parse_option_table parse_opts [] = { @@ -3414,6 +3417,21 @@ nds32_seg (int i) /* Set if label adjustment is needed. I should not adjust .xbyte in dwarf. */ static symbolS *nds32_last_label; /* Last label for alignment. */ +static void +add_mapping_symbol_for_align (int shift, valueT addr, int is_data_align) +{ + if ((shift > 1) && (addr & 1)) + { + int n = (1 << shift) - 1; + if (!is_data_align) + add_mapping_symbol (MAP_CODE, 1, 0); + else if ((int) (addr & n) != n) + add_mapping_symbol (MAP_CODE, 1, 0); + } + else if ((shift > 1) && ((int) (addr & 1) == 0)) + add_mapping_symbol (MAP_CODE, 0, 0); +} + /* This code is referred from D30V for adjust label to be with pending alignment. For example, LBYTE: .byte 0x12 @@ -3447,7 +3465,10 @@ nds32_adjust_label (int n) if (frag_now_fix () & ((1 << n) -1 )) { if (subseg_text_p (now_seg)) - frag_align_code (n, 0); + { + add_mapping_symbol_for_align (n, frag_now_fix (), 1); + frag_align_code (n, 0); + } else frag_align (n, 0, 0); @@ -3512,10 +3533,63 @@ nds32_cons_align (int size ATTRIBUTE_UNUSED) I think we should just adjust label in `nds32_aligned_X_cons' instead of here. */ } +static void +make_mapping_symbol (enum mstate state, valueT value, fragS * frag, unsigned int align) +{ + symbolS *symbol_p = NULL; + const char *symbol_name = NULL; + switch (state) + { + case MAP_DATA: + if (align == 0) + symbol_name = "$d0"; + else if (align == 1) + symbol_name = "$d1"; + else if (align == 2) + symbol_name = "$d2"; + else if (align == 3) + symbol_name = "$d3"; + else if (align == 4) + symbol_name = "$d4"; + break; + case MAP_CODE: + symbol_name = "$c"; + break; + default: + abort (); + } + + symbol_p = symbol_new (symbol_name, now_seg, value, frag); + /* local scope attribute */ + symbol_get_bfdsym (symbol_p)->flags |= BSF_NO_FLAGS | BSF_LOCAL; +} + +static void +add_mapping_symbol (enum mstate state, unsigned int padding_byte, + unsigned int align) +{ + enum mstate current_mapping_state = + seg_info (now_seg)->tc_segment_info_data.mapstate; + + if (state == MAP_CODE + && current_mapping_state == state) + return; + + if (!SEG_NORMAL (now_seg) + || !subseg_text_p (now_seg)) + return; + + /* start adding mapping symbol */ + seg_info (now_seg)->tc_segment_info_data.mapstate = state; + make_mapping_symbol (state, (valueT) frag_now_fix () + padding_byte, + frag_now, align); +} + static void nds32_aligned_cons (int idx) { nds32_adjust_label (idx); + add_mapping_symbol (MAP_DATA, 0, idx); /* Call default handler. */ cons (1 << idx); if (now_seg->flags & SEC_CODE @@ -3927,6 +4001,7 @@ nds32_pre_do_align (int n, char *fill, int len, int max) { dwarf2_emit_insn (0); fragP = frag_now; + add_mapping_symbol_for_align (n, frag_now_fix (), 0); frag_align_code (n, max); /* Tag this alignment when there is a label before it. */ @@ -5226,11 +5301,13 @@ md_assemble (char *str) /* Make sure the beginning of text being 2-byte align. */ nds32_adjust_label (1); + add_mapping_symbol (MAP_CODE, 0, 0); fld = insn.field; /* Try to allocate the max size to guarantee relaxable same branch instructions in the same fragment. */ frag_grow (NDS32_MAXCHAR); fragP = frag_now; + if (fld && (insn.attr & NASM_ATTR_BRANCH) && (pseudo_opcode || (insn.opcode->value != INSN_JAL && insn.opcode->value != INSN_J)) diff --git a/gas/config/tc-nds32.h b/gas/config/tc-nds32.h index 178ca4ec33..edcbd4a3f1 100644 --- a/gas/config/tc-nds32.h +++ b/gas/config/tc-nds32.h @@ -24,6 +24,21 @@ #include "bfd_stdint.h" +/* Enum mapping symbol. */ +enum mstate +{ + MAP_UNDEFINED = 0, /* Must be zero, for seginfo in new sections. */ + MAP_DATA, + MAP_CODE, +}; +#define TC_SEGMENT_INFO_TYPE struct nds32_segment_info_type + +/* For mapping symbol. */ +struct nds32_segment_info_type +{ + enum mstate mapstate; +}; + #define LISTING_HEADER \ (target_big_endian ? "NDS32 GAS" : "NDS32 GAS Little Endian") diff --git a/include/dis-asm.h b/include/dis-asm.h index 949ccde77b..c46380b42c 100644 --- a/include/dis-asm.h +++ b/include/dis-asm.h @@ -304,6 +304,7 @@ extern bfd_boolean csky_symbol_is_valid (asymbol *, struct disassemble_info *); extern void disassemble_init_powerpc (struct disassemble_info *); extern void disassemble_init_s390 (struct disassemble_info *); extern void disassemble_init_wasm32 (struct disassemble_info *); +extern void disassemble_init_nds32 (struct disassemble_info *); extern const disasm_options_and_args_t *disassembler_options_arm (void); extern const disasm_options_and_args_t *disassembler_options_mips (void); extern const disasm_options_and_args_t *disassembler_options_powerpc (void); diff --git a/opcodes/disassemble.c b/opcodes/disassemble.c index ce83423b6d..750d76ad86 100644 --- a/opcodes/disassemble.c +++ b/opcodes/disassemble.c @@ -666,6 +666,11 @@ disassemble_init_for_target (struct disassemble_info * info) disassemble_init_s390 (info); break; #endif +#ifdef ARCH_nds32 + case bfd_arch_nds32: + disassemble_init_nds32 (info); + break; + #endif default: break; } diff --git a/opcodes/nds32-dis.c b/opcodes/nds32-dis.c index 1207fd37c7..93a2709f89 100644 --- a/opcodes/nds32-dis.c +++ b/opcodes/nds32-dis.c @@ -35,6 +35,31 @@ /* Get fields macro define. */ #define MASK_OP(insn, mask) ((insn) & (0x3f << 25 | (mask))) +/* For mapping symbol. */ +enum map_type +{ + MAP_DATA0, + MAP_DATA1, + MAP_DATA2, + MAP_DATA3, + MAP_DATA4, + MAP_CODE, +}; + +struct nds32_private_data +{ + /* Whether any mapping symbols are present in the provided symbol + table. -1 if we do not know yet, otherwise 0 or 1. */ + int has_mapping_symbols; + + /* Track the last type (although this doesn't seem to be useful). */ + enum map_type last_mapping_type; + + /* Tracking symbol table information. */ + int last_symbol_index; + bfd_vma last_addr; +}; + /* Default text to print if an instruction isn't recognized. */ #define UNKNOWN_INSN_MSG _("*unknown*") #define NDS32_PARSE_INSN16 0x01 @@ -53,6 +78,10 @@ static void print_insn32 (bfd_vma pc, disassemble_info *info, uint32_t insn, uint32_t parse_mode); static uint32_t nds32_mask_opcode (uint32_t); static void nds32_special_opcode (uint32_t, struct nds32_opcode **); +static int get_mapping_symbol_type (struct disassemble_info *, int, + enum map_type *); +static int is_mapping_symbol (struct disassemble_info *, int, + enum map_type *); /* define in objdump.c. */ struct objdump_disasm_info @@ -836,7 +865,8 @@ nds32_mask_opcode (uint32_t insn) /* AMAWzSSA AMWzSSA */ return MASK_OP (insn, (0x1f << 20) | (0x3 << 7)); else - /* AMAWzSL.L AMAWzSL2.S AMAWzSL2.L AMWzSL.L AMWzSL.L AMWzSL2.S */ + /* AMAWzSL.L AMAWzSL2.S AMAWzSL2.L + AMWzSL.L AMWzSL.L AMWzSL2.S */ return MASK_OP (insn, (0x1f << 20) | (0x7 << 6)); case 0x2: if (__GF (insn, 6, 3) == 2) @@ -846,7 +876,8 @@ nds32_mask_opcode (uint32_t insn) /* AMAWyySSA AMWyySSA */ return MASK_OP (insn, (0x1f << 20) | (0x3 << 7)); else - /* AMAWyySL.L AMAWyySL2.S AMAWyySL2.L AMWyySL.L AMWyySL.L AMWyySL2.S */ + /* AMAWyySL.L AMAWyySL2.S AMAWyySL2.L + AMWyySL.L AMWyySL.L AMWyySL2.S */ return MASK_OP (insn, (0x1f << 20) | (0x7 << 6)); } return MASK_OP (insn, 0x1f << 20); @@ -949,46 +980,187 @@ print_insn_nds32 (bfd_vma pc, disassemble_info *info) { int status; bfd_byte buf[4]; + bfd_byte buf_data[16]; + long long given; + long long given1; uint32_t insn; - static int init = 1; - int i = 0; - struct nds32_opcode *opc; - struct nds32_opcode **slot; + int n; + int last_symbol_index = -1; + bfd_vma addr; + int is_data = FALSE; + bfd_boolean found = FALSE; + struct nds32_private_data *private_data; + unsigned int size = 16; + enum map_type mapping_type = MAP_CODE; + + if (info->private_data == NULL) + { + /* Note: remain lifecycle throughout whole execution. */ + static struct nds32_private_data private; + private.has_mapping_symbols = -1; /* unknown yet. */ + private.last_symbol_index = -1; + private.last_addr = 0; + info->private_data = &private; + } + private_data = info->private_data; - if (init) + if (info->symtab_size != 0) { - /* Build opcode table. */ - opcode_htab = htab_create_alloc (1024, htab_hash_hash, htab_hash_eq, - NULL, xcalloc, free); + int start; + if (pc == 0) + start = 0; + else + { + start = info->symtab_pos; + if (start < private_data->last_symbol_index) + start = private_data->last_symbol_index; + } + + if (0 > start) + start = 0; - while (nds32_opcodes[i].opcode != NULL) + if (private_data->has_mapping_symbols != 0 + && ((strncmp (".text", info->section->name, 5) == 0))) { - opc = &nds32_opcodes[i]; - slot = - (struct nds32_opcode **) htab_find_slot (opcode_htab, &opc->value, - INSERT); - if (*slot == NULL) + for (n = start; n < info->symtab_size; n++) { - /* This is the new one. */ - *slot = opc; + addr = bfd_asymbol_value (info->symtab[n]); + if (addr > pc) + break; + if (get_mapping_symbol_type (info, n, &mapping_type)) + { + last_symbol_index = n; + found = TRUE; + } } - else + + if (found) + private_data->has_mapping_symbols = 1; + else if (!found && private_data->has_mapping_symbols == -1) + { + /* Make sure there are no any mapping symbol. */ + for (n = 0; n < info->symtab_size; n++) + { + if (is_mapping_symbol (info, n, &mapping_type)) + { + private_data->has_mapping_symbols = -1; + break; + } + } + if (private_data->has_mapping_symbols == -1) + private_data->has_mapping_symbols = 0; + } + + private_data->last_symbol_index = last_symbol_index; + private_data->last_mapping_type = mapping_type; + is_data = (private_data->last_mapping_type == MAP_DATA0 + || private_data->last_mapping_type == MAP_DATA1 + || private_data->last_mapping_type == MAP_DATA2 + || private_data->last_mapping_type == MAP_DATA3 + || private_data->last_mapping_type == MAP_DATA4); + } + } + + /* Wonder data or instruction. */ + if (is_data) + { + unsigned int i1; + + /* Fix corner case: there is no next mapping symbol, + let mapping type decides size */ + if (last_symbol_index + 1 >= info->symtab_size) + { + if (mapping_type == MAP_DATA0) + size = 1; + if (mapping_type == MAP_DATA1) + size = 2; + if (mapping_type == MAP_DATA2) + size = 4; + if (mapping_type == MAP_DATA3) + size = 8; + if (mapping_type == MAP_DATA4) + size = 16; + } + for (n = last_symbol_index + 1; n < info->symtab_size; n++) + { + addr = bfd_asymbol_value (info->symtab[n]); + + enum map_type fake_mapping_type; + if (get_mapping_symbol_type (info, n, &fake_mapping_type) + && (addr > pc + && ((info->section == NULL) + || (info->section == info->symtab[n]->section))) + && (addr - pc < size)) + { + size = addr - pc; + break; + } + } + + if (size == 3) + size = (pc & 1) ? 1 : 2; + + /* Read bytes from BFD. */ + info->read_memory_func (pc, (bfd_byte *) buf_data, size, info); + given = 0; + given1 = 0; + /* Start assembling data. */ + /* Little endian of data. */ + if (info->endian == BFD_ENDIAN_LITTLE) + { + for (i1 = size - 1;; i1--) + { + if (i1 >= 8) + given1 = buf_data[i1] | (given1 << 8); + else + given = buf_data[i1] | (given << 8); + + if (i1 == 0) + break; + } + } + else + { + /* Big endian of data. */ + for (i1 = 0; i1 < size; i1++) { - /* Already exists. Append to the list. */ - opc = *slot; - while (opc->next) - opc = opc->next; - opc->next = &nds32_opcodes[i]; + if (i1 <= 7) + given = buf_data[i1] | (given << 8); + else + given1 = buf_data[i1] | (given1 << 8); } - i++; } - init = 0; + + info->bytes_per_line = 4; + + if (size == 16) + info->fprintf_func (info->stream, ".qword\t0x%016llx%016llx", + given, given1); + else if (size == 8) + info->fprintf_func (info->stream, ".dword\t0x%016llx", given); + else if (size == 4) + info->fprintf_func (info->stream, ".word\t0x%08llx", given); + else if (size == 2) + { + /* short */ + if (mapping_type == MAP_DATA0) + info->fprintf_func (info->stream, ".byte\t0x%02llx", given & 0xFF); + else + info->fprintf_func (info->stream, ".short\t0x%04llx", given); + } + else + { + /* byte */ + info->fprintf_func (info->stream, ".byte\t0x%02llx", given); + } + + return size; } status = info->read_memory_func (pc, (bfd_byte *) buf, 4, info); if (status) { - /* for the last 16-bit instruction. */ + /* For the last 16-bit instruction. */ status = info->read_memory_func (pc, (bfd_byte *) buf, 2, info); if (status) { @@ -1012,3 +1184,145 @@ print_insn_nds32 (bfd_vma pc, disassemble_info *info) return 4; } } + +/* Ignore disassembling unnecessary name. */ + +static bfd_boolean +nds32_symbol_is_valid (asymbol *sym, + struct disassemble_info *info ATTRIBUTE_UNUSED) +{ + const char *name; + + if (sym == NULL) + return FALSE; + + name = bfd_asymbol_name (sym); + + /* Mapping symbol is invalid. */ + if (name[0] == '$') + return FALSE; + return TRUE; +} + +static void +nds32_add_opcode_hash_table (unsigned indx) +{ + opcode_t *opc; + + opc = nds32_opcode_table[indx]; + if (opc == NULL) + return; + + while (opc->opcode != NULL) + { + opcode_t **slot; + + slot = (opcode_t **) htab_find_slot + (opcode_htab, &opc->value, INSERT); + if (*slot == NULL) + { + /* This is the new one. */ + *slot = opc; + } + else + { + opcode_t *tmp; + + /* Already exists. Append to the list. */ + tmp = *slot; + while (tmp->next) + tmp = tmp->next; + tmp->next = opc; + opc->next = NULL; + } + opc++; + } +} + +void +disassemble_init_nds32 (struct disassemble_info *info) +{ + static unsigned init_done = 0; + unsigned k; + + /* Set up symbol checking function. */ + info->symbol_is_valid = nds32_symbol_is_valid; + + /* Only need to initialize once: + High level will call this function for every object file. + For example, when disassemble all members of a library. */ + if (init_done) + return; + + /* Setup main core. */ + nds32_keyword_table[NDS32_MAIN_CORE] = &keywords[0]; + nds32_opcode_table[NDS32_MAIN_CORE] = &nds32_opcodes[0]; + nds32_field_table[NDS32_MAIN_CORE] = &operand_fields[0]; + + /* Build opcode table. */ + opcode_htab = htab_create_alloc (1024, htab_hash_hash, htab_hash_eq, + NULL, xcalloc, free); + + for (k = 0; k < NDS32_CORE_COUNT; k++) + { + /* Add op-codes. */ + nds32_add_opcode_hash_table (k); + } + + init_done = 1; +} + +static int +is_mapping_symbol (struct disassemble_info *info, int n, + enum map_type *map_type) +{ + const char *name = NULL; + + /* Get symbol name. */ + name = bfd_asymbol_name (info->symtab[n]); + + if (name[1] == 'c') + { + *map_type = MAP_CODE; + return TRUE; + } + else if (name[1] == 'd' && name[2] == '0') + { + *map_type = MAP_DATA0; + return TRUE; + } + else if (name[1] == 'd' && name[2] == '1') + { + *map_type = MAP_DATA1; + return TRUE; + } + else if (name[1] == 'd' && name[2] == '2') + { + *map_type = MAP_DATA2; + return TRUE; + } + else if (name[1] == 'd' && name[2] == '3') + { + *map_type = MAP_DATA3; + return TRUE; + } + else if (name[1] == 'd' && name[2] == '4') + { + *map_type = MAP_DATA4; + return TRUE; + } + + return FALSE; +} + +static int +get_mapping_symbol_type (struct disassemble_info *info, int n, + enum map_type *map_type) +{ + /* If the symbol is in a different section, ignore it. */ + if (info->section != NULL + && info->section != info->symtab[n]->section) + return FALSE; + + return is_mapping_symbol (info, n, map_type); +} -- 2.19.0