+ if ( (bfd_signed_vma) (foff) < CONSERVATIVE_20BIT
+ && (bfd_signed_vma) (foff) >= -CONSERVATIVE_20BIT)
+ {
+ /* Pattern sethi-ori transform to movi. */
+ reloc = R_NDS32_TLS_LE_20;
+ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), reloc);
+ insn = N32_TYPE1 (MOVI, N32_RT5 (insn), 0);
+ bfd_putb32 (insn, contents + laddr);
+ }
+}
+
+/* Relax LE TLS calculate address instruction pattern. */
+
+static void
+nds32_elf_relax_letlsadd (struct bfd_link_info *link_info, bfd *abfd,
+ asection *sec, Elf_Internal_Rela *irel,
+ Elf_Internal_Rela *internal_relocs,
+ bfd_byte *contents, Elf_Internal_Sym *isymbuf,
+ Elf_Internal_Shdr *symtab_hdr, bfd_boolean *again)
+{
+ /* Local TLS non-pic
+ sethi ta, hi20(symbol@tpoff) ; TLS_LE_HI20
+ ori ta, ta, lo12(symbol@tpoff) ; TLS_LE_LO12
+ add ra, ta, tp ; TLS_LE_ADD */
+
+ uint32_t insn;
+ bfd_vma laddr;
+ bfd_signed_vma foff;
+ Elf_Internal_Rela *i1_irelfn, *irelend;
+
+ irelend = internal_relocs + sec->reloc_count;
+ laddr = irel->r_offset;
+ insn = bfd_getb32 (contents + laddr);
+ i1_irelfn = find_relocs_at_address (irel, internal_relocs, irelend,
+ R_NDS32_PTR_RESOLVED);
+ foff = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr);
+ BFD_ASSERT (elf_hash_table (link_info)->tls_sec != NULL);
+ foff -= (elf_hash_table (link_info)->tls_sec->vma + TP_OFFSET);
+
+ /* The range is +/-16k. */
+ if ((bfd_signed_vma) (foff) < CONSERVATIVE_15BIT
+ && (bfd_signed_vma) (foff) >= -CONSERVATIVE_15BIT)
+ {
+ /* Transform add to addi. */
+ insn = N32_TYPE2 (ADDI, N32_RT5 (insn), N32_RB5 (insn), 0);
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_TLS_LE_15S0);
+
+ bfd_putb32 (insn, contents + laddr);
+ if (i1_irelfn != irelend)
+ {
+ i1_irelfn->r_addend |= 1;
+ *again = TRUE;
+ }
+ }
+}
+
+/* Relax LE TLS load store instruction pattern. */
+
+static void
+nds32_elf_relax_letlsls (struct bfd_link_info *link_info, bfd *abfd,
+ asection *sec, Elf_Internal_Rela *irel,
+ Elf_Internal_Rela *internal_relocs,
+ bfd_byte *contents, Elf_Internal_Sym *isymbuf,
+ Elf_Internal_Shdr *symtab_hdr, bfd_boolean *again)
+{
+
+ uint32_t insn;
+ bfd_vma laddr;
+ bfd_signed_vma foff;
+ Elf_Internal_Rela *i1_irelfn, *irelend;
+ int success = 0;
+
+ irelend = internal_relocs + sec->reloc_count;
+ laddr = irel->r_offset;
+ insn = bfd_getb32 (contents + laddr);
+ i1_irelfn = find_relocs_at_address (irel, internal_relocs, irelend,
+ R_NDS32_PTR_RESOLVED);
+ foff = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr);
+ BFD_ASSERT (elf_hash_table (link_info)->tls_sec != NULL);
+ foff -= (elf_hash_table (link_info)->tls_sec->vma + TP_OFFSET);
+
+ switch ((N32_OP6 (insn) << 8) | (insn & 0xff))
+ {
+ case (N32_OP6_MEM << 8) | N32_MEM_LB:
+ case (N32_OP6_MEM << 8) | N32_MEM_SB:
+ case (N32_OP6_MEM << 8) | N32_MEM_LBS:
+ /* The range is +/-16k. */
+ if ((bfd_signed_vma) (foff) < CONSERVATIVE_15BIT
+ && (bfd_signed_vma) (foff) >= -CONSERVATIVE_15BIT)
+ {
+ insn =
+ ((insn & 0xff) << 25) | (insn & 0x1f00000) | ((insn & 0x7c00) << 5);
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_TLS_LE_15S0);
+ success = 1;
+ break;
+ }
+ case (N32_OP6_MEM << 8) | N32_MEM_LH:
+ case (N32_OP6_MEM << 8) | N32_MEM_SH:
+ case (N32_OP6_MEM << 8) | N32_MEM_LHS:
+ /* The range is +/-32k. */
+ if ((bfd_signed_vma) (foff) < CONSERVATIVE_15BIT_S1
+ && (bfd_signed_vma) (foff) >= -CONSERVATIVE_15BIT_S1)
+ {
+ insn =
+ ((insn & 0xff) << 25) | (insn & 0x1f00000) | ((insn & 0x7c00) << 5);
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_TLS_LE_15S1);
+ success = 1;
+ break;
+ }
+ case (N32_OP6_MEM << 8) | N32_MEM_LW:
+ case (N32_OP6_MEM << 8) | N32_MEM_SW:
+ /* The range is +/-64k. */
+ if ((bfd_signed_vma) (foff) < CONSERVATIVE_15BIT_S2
+ && (bfd_signed_vma) (foff) >= -CONSERVATIVE_15BIT_S2)
+ {
+ insn =
+ ((insn & 0xff) << 25) | (insn & 0x1f00000) | ((insn & 0x7c00) << 5);
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_TLS_LE_15S2);
+ success = 1;
+ break;
+ }
+ default:
+ break;
+ }
+
+ if (success)
+ {
+ bfd_putb32 (insn, contents + laddr);
+ if (i1_irelfn != irelend)
+ {
+ i1_irelfn->r_addend |= 1;
+ *again = TRUE;
+ }
+ }
+}
+
+/* Relax PTR relocation for nds32_elf_relax_section. */
+
+static bfd_boolean
+nds32_elf_relax_ptr (bfd *abfd, asection *sec, Elf_Internal_Rela *irel,
+ Elf_Internal_Rela *internal_relocs, int *insn_len,
+ int *seq_len, bfd_byte *contents)
+{
+ Elf_Internal_Rela *ptr_irel, *irelend, *count_irel, *re_irel;
+
+ irelend = internal_relocs + sec->reloc_count;
+
+ re_irel =
+ find_relocs_at_address_addr (irel, internal_relocs, irelend,
+ R_NDS32_PTR_RESOLVED, irel->r_addend);
+
+ if (re_irel == irelend)
+ {
+ (*_bfd_error_handler)
+ ("%B: warning: R_NDS32_PTR points to unrecognized reloc at 0x%lx.",
+ abfd, (long) irel->r_offset);
+ return FALSE;
+ }
+
+ if (re_irel->r_addend != 1)
+ return FALSE;
+
+ /* Pointed target is relaxed and no longer needs this void *,
+ change the type to NONE. */
+ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
+
+ /* Find PTR_COUNT to decide remove it or not. If PTR_COUNT does
+ not exist, it means only count 1 and remove it directly. */
+ /* TODO: I hope we can obsolate R_NDS32_COUNT in the future. */
+ count_irel = find_relocs_at_address (irel, internal_relocs, irelend,
+ R_NDS32_PTR_COUNT);
+ ptr_irel = find_relocs_at_address (irel, internal_relocs, irelend,
+ R_NDS32_PTR);
+ if (count_irel != irelend)
+ {
+ if (--count_irel->r_addend > 0)
+ return FALSE;
+ }
+
+ if (ptr_irel != irelend)
+ return FALSE;
+
+ /* If the PTR_COUNT is already 0, remove current instruction. */
+ *seq_len = nds32_elf_insn_size (abfd, contents, irel->r_offset);
+ *insn_len = 0;
+ return TRUE;
+}
+
+/* Relax PLT_GOT_SUFF relocation for nds32_elf_relax_section. */
+
+static void
+nds32_elf_relax_pltgot_suff (struct bfd_link_info *link_info, bfd *abfd,
+ asection *sec, Elf_Internal_Rela *irel,
+ Elf_Internal_Rela *internal_relocs,
+ bfd_byte *contents, Elf_Internal_Sym *isymbuf,
+ Elf_Internal_Shdr *symtab_hdr, bfd_boolean *again)
+{
+ uint32_t insn;
+ bfd_signed_vma foff;
+ Elf_Internal_Rela *i1_irelfn, *irelend;
+ bfd_vma local_sda, laddr;
+
+ irelend = internal_relocs + sec->reloc_count;
+ laddr = irel->r_offset;
+ insn = bfd_getb32 (contents + laddr);
+
+ /* FIXME: It's a little trouble to turn JRAL5 to JAL since
+ we need additional space. It might be help if we could
+ borrow some space from instructions to be eliminated
+ such as sethi, ori, add. */
+ if (insn & 0x80000000)
+ return;
+
+ if (nds32_elf_check_dup_relocs
+ (irel, internal_relocs, irelend, R_NDS32_PLT_GOT_SUFF))
+ return;
+
+ i1_irelfn =
+ find_relocs_at_address (irel, internal_relocs, irelend,
+ R_NDS32_PTR_RESOLVED);
+
+ /* FIXIT 090606
+ The boundary should be reduced since the .plt section hasn't
+ been created and the address of specific entry is still unknown
+ Maybe the range between the function call and the begin of the
+ .text section can be used to decide if the .plt is in the range
+ of function call. */
+
+ if (N32_OP6 (insn) == N32_OP6_ALU1
+ && N32_SUB5 (insn) == N32_ALU1_ADD)
+ {
+ /* Get the value of the symbol referred to by the reloc. */
+ nds32_elf_final_sda_base (sec->output_section->owner, link_info,
+ &local_sda, FALSE);
+ foff = (bfd_signed_vma) (calculate_plt_memory_address
+ (abfd, link_info, isymbuf, irel,
+ symtab_hdr) - local_sda);
+ /* This condition only happened when symbol is undefined. */
+ if (foff == 0)
+ return;
+
+ if (foff < -CONSERVATIVE_19BIT || foff >= CONSERVATIVE_19BIT)
+ return;
+ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+ R_NDS32_PLT_GOTREL_LO19);
+ /* addi.gp */
+ insn = N32_TYPE1 (SBGP, N32_RT5 (insn), __BIT (19));
+ }
+ else if (N32_OP6 (insn) == N32_OP6_JREG
+ && N32_SUB5 (insn) == N32_JREG_JRAL)
+ {
+ /* Get the value of the symbol referred to by the reloc. */
+ foff =
+ calculate_plt_offset (abfd, sec, link_info, isymbuf, irel, symtab_hdr);
+ /* This condition only happened when symbol is undefined. */
+ if (foff == 0)
+ return;
+ if (foff < -CONSERVATIVE_24BIT_S1 || foff >= CONSERVATIVE_24BIT_S1)
+ return;
+ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_25_PLTREL);
+ insn = INSN_JAL;
+ }
+ else
+ return;
+
+ bfd_putb32 (insn, contents + laddr);
+ if (i1_irelfn != irelend)
+ {
+ i1_irelfn->r_addend |= 1;
+ *again = TRUE;
+ }
+}
+
+/* Relax GOT_SUFF relocation for nds32_elf_relax_section. */
+
+static void
+nds32_elf_relax_got_suff (struct bfd_link_info *link_info, bfd *abfd,
+ asection *sec, Elf_Internal_Rela *irel,
+ Elf_Internal_Rela *internal_relocs,
+ bfd_byte *contents, Elf_Internal_Shdr *symtab_hdr,
+ bfd_boolean *again)
+{
+ uint32_t insn;
+ bfd_signed_vma foff;
+ Elf_Internal_Rela *i1_irelfn, *irelend;
+ bfd_vma local_sda, laddr;
+
+ irelend = internal_relocs + sec->reloc_count;
+ laddr = irel->r_offset;
+ insn = bfd_getb32 (contents + laddr);
+ if (insn & 0x80000000)
+ return;
+
+ if (nds32_elf_check_dup_relocs
+ (irel, internal_relocs, irelend, R_NDS32_GOT_SUFF))
+ return;
+
+ i1_irelfn = find_relocs_at_address (irel, internal_relocs, irelend,
+ R_NDS32_PTR_RESOLVED);
+
+ nds32_elf_final_sda_base (sec->output_section->owner, link_info,
+ &local_sda, FALSE);
+ foff = calculate_got_memory_address (abfd, link_info, irel,
+ symtab_hdr) - local_sda;
+
+ if (foff < CONSERVATIVE_19BIT && foff >= -CONSERVATIVE_19BIT)
+ {
+ /* Turn LW to LWI.GP. Change relocation type to R_NDS32_GOT_REL. */
+ insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (6, 17, 3));
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_GOT17S2_RELA);
+ bfd_putb32 (insn, contents + laddr);
+ if (i1_irelfn != irelend)
+ {
+ i1_irelfn->r_addend |= 1;
+ *again = TRUE;
+ }
+ }
+}
+
+/* Relax PLT_GOT_SUFF relocation for nds32_elf_relax_section. */
+
+static void
+nds32_elf_relax_gotoff_suff (struct bfd_link_info *link_info, bfd *abfd,
+ asection *sec, Elf_Internal_Rela *irel,
+ Elf_Internal_Rela *internal_relocs,
+ bfd_byte *contents, Elf_Internal_Sym *isymbuf,
+ Elf_Internal_Shdr *symtab_hdr, bfd_boolean *again)
+{
+ int opc_insn_gotoff;
+ uint32_t insn;
+ bfd_signed_vma foff;
+ Elf_Internal_Rela *i1_irelfn, *i2_irelfn, *irelend;
+ bfd_vma local_sda, laddr;
+
+ irelend = internal_relocs + sec->reloc_count;
+ laddr = irel->r_offset;
+ insn = bfd_getb32 (contents + laddr);
+
+ if (insn & 0x80000000)
+ return;
+
+ if (nds32_elf_check_dup_relocs
+ (irel, internal_relocs, irelend, R_NDS32_GOTOFF_SUFF))
+ return;
+
+ i1_irelfn = find_relocs_at_address (irel, internal_relocs, irelend,
+ R_NDS32_PTR_RESOLVED);
+ nds32_elf_final_sda_base (sec->output_section->owner, link_info,
+ &local_sda, FALSE);
+ foff = calculate_memory_address (abfd, irel, isymbuf, symtab_hdr);
+ foff = foff - local_sda;
+
+ if (foff >= CONSERVATIVE_19BIT || foff < -CONSERVATIVE_19BIT)
+ return;
+
+ /* Concatenate opcode and sub-opcode for switch case.
+ It may be MEM or ALU1. */
+ opc_insn_gotoff = (N32_OP6 (insn) << 8) | (insn & 0xff);
+ switch (opc_insn_gotoff)
+ {
+ case (N32_OP6_MEM << 8) | N32_MEM_LW:
+ /* 4-byte aligned. */
+ insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (6, 17, 3));
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA17S2_RELA);
+ break;
+ case (N32_OP6_MEM << 8) | N32_MEM_SW:
+ insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __MF (7, 17, 3));
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA17S2_RELA);
+ break;
+ case (N32_OP6_MEM << 8) | N32_MEM_LH:
+ /* 2-byte aligned. */
+ insn = N32_TYPE1 (HWGP, N32_RT5 (insn), 0);
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA18S1_RELA);
+ break;
+ case (N32_OP6_MEM << 8) | N32_MEM_LHS:
+ insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __BIT (18));
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA18S1_RELA);
+ break;
+ case (N32_OP6_MEM << 8) | N32_MEM_SH:
+ insn = N32_TYPE1 (HWGP, N32_RT5 (insn), __BIT (19));
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA18S1_RELA);
+ break;
+ case (N32_OP6_MEM << 8) | N32_MEM_LB:
+ /* 1-byte aligned. */
+ insn = N32_TYPE1 (LBGP, N32_RT5 (insn), 0);
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA19S0_RELA);
+ break;
+ case (N32_OP6_MEM << 8) | N32_MEM_LBS:
+ insn = N32_TYPE1 (LBGP, N32_RT5 (insn), __BIT (19));
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA19S0_RELA);
+ break;
+ case (N32_OP6_MEM << 8) | N32_MEM_SB:
+ insn = N32_TYPE1 (SBGP, N32_RT5 (insn), 0);
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA19S0_RELA);
+ break;
+ case (N32_OP6_ALU1 << 8) | N32_ALU1_ADD:
+ insn = N32_TYPE1 (SBGP, N32_RT5 (insn), __BIT (19));
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_SDA19S0_RELA);
+ break;
+ default:
+ return;
+ }
+
+ bfd_putb32 (insn, contents + laddr);
+ if (i1_irelfn != irelend)
+ {
+ i1_irelfn->r_addend |= 1;
+ *again = TRUE;
+ }
+ if ((i2_irelfn = find_relocs_at_address (irel, internal_relocs, irelend,
+ R_NDS32_INSN16)) != irelend)
+ i2_irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
+
+}
+
+static bfd_boolean
+nds32_relax_adjust_label (bfd *abfd, asection *sec,
+ Elf_Internal_Rela *internal_relocs,
+ bfd_byte *contents,
+ nds32_elf_blank_t **relax_blank_list,
+ int optimize, int opt_size)
+{
+ /* This code block is used to adjust 4-byte alignment by relax a pair
+ of instruction a time.
+
+ It recognizes three types of relocations.
+ 1. R_NDS32_LABEL - a aligment.
+ 2. R_NDS32_INSN16 - relax a 32-bit instruction to 16-bit.
+ 3. is_16bit_NOP () - remove a 16-bit instruction. */
+
+ /* TODO: It seems currently implementation only support 4-byte aligment.
+ We should handle any-aligment. */
+
+ Elf_Internal_Rela *insn_rel = NULL, *label_rel = NULL, *irel;
+ Elf_Internal_Rela *tmp_rel, *tmp2_rel = NULL;
+ Elf_Internal_Rela rel_temp;
+ Elf_Internal_Rela *irelend;
+ bfd_vma address;
+ uint16_t insn16;
+
+ /* Checking for branch relaxation relies on the relocations to
+ be sorted on 'r_offset'. This is not guaranteed so we must sort. */
+ nds32_insertion_sort (internal_relocs, sec->reloc_count,
+ sizeof (Elf_Internal_Rela), compar_reloc);
+
+ irelend = internal_relocs + sec->reloc_count;
+
+ /* Force R_NDS32_LABEL before R_NDS32_INSN16. */
+ /* FIXME: Can we generate the right order in assembler?
+ So we don't have to swapping them here. */
+
+ for (label_rel = internal_relocs, insn_rel = internal_relocs;
+ label_rel < irelend; label_rel++)
+ {
+ if (ELF32_R_TYPE (label_rel->r_info) != R_NDS32_LABEL)
+ continue;
+
+ /* Find the first reloc has the same offset with label_rel. */
+ while (insn_rel < irelend && insn_rel->r_offset < label_rel->r_offset)
+ insn_rel++;
+
+ for (;insn_rel < irelend && insn_rel->r_offset == label_rel->r_offset;
+ insn_rel++)
+ /* Check if there were R_NDS32_INSN16 and R_NDS32_LABEL at the same
+ address. */
+ if (ELF32_R_TYPE (insn_rel->r_info) == R_NDS32_INSN16)
+ break;
+
+ if (insn_rel < irelend && insn_rel->r_offset == label_rel->r_offset
+ && insn_rel < label_rel)
+ {
+ /* Swap the two reloc if the R_NDS32_INSN16 is
+ before R_NDS32_LABEL. */
+ memcpy (&rel_temp, insn_rel, sizeof (Elf_Internal_Rela));
+ memcpy (insn_rel, label_rel, sizeof (Elf_Internal_Rela));
+ memcpy (label_rel, &rel_temp, sizeof (Elf_Internal_Rela));
+ }
+ }
+
+ label_rel = NULL;
+ insn_rel = NULL;
+ /* If there were a sequence of R_NDS32_LABEL end up with .align 2
+ or higher, remove other R_NDS32_LABEL with lower alignment.
+ If an R_NDS32_INSN16 in between R_NDS32_LABELs must be converted,
+ then the R_NDS32_LABEL sequence is broke. */
+ for (tmp_rel = internal_relocs; tmp_rel < irelend; tmp_rel++)
+ {
+ if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_LABEL)
+ {
+ if (label_rel == NULL)
+ {
+ if (tmp_rel->r_addend < 2)
+ label_rel = tmp_rel;
+ continue;
+ }
+ else if (tmp_rel->r_addend > 1)
+ {
+ /* Remove all LABEL relocation from label_rel to tmp_rel
+ including relocations with same offset as tmp_rel. */
+ for (tmp2_rel = label_rel; tmp2_rel < tmp_rel
+ || tmp2_rel->r_offset == tmp_rel->r_offset; tmp2_rel++)
+ {
+ if (ELF32_R_TYPE (tmp2_rel->r_info) == R_NDS32_LABEL
+ && tmp2_rel->r_addend < 2)
+ tmp2_rel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (tmp2_rel->r_info),
+ R_NDS32_NONE);
+ }
+ label_rel = NULL;
+ }
+ }
+ else if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_INSN16 && label_rel)
+ {
+ /* A new INSN16 which can be converted, so clear label_rel. */
+ if (is_convert_32_to_16 (abfd, sec, tmp_rel, internal_relocs,
+ irelend, &insn16)
+ || is_16bit_NOP (abfd, sec, tmp_rel))
+ label_rel = NULL;
+ }
+ }
+
+ label_rel = NULL;
+ insn_rel = NULL;
+ /* Optimized for speed and nothing has not been relaxed.
+ It's time to align labels.
+ We may convert a 16-bit instruction right before a label to
+ 32-bit, in order to align the label if necessary
+ all reloc entries has been sorted by r_offset. */
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ if (ELF32_R_TYPE (irel->r_info) != R_NDS32_INSN16
+ && ELF32_R_TYPE (irel->r_info) != R_NDS32_LABEL)
+ continue;
+
+ if (ELF32_R_TYPE (irel->r_info) == R_NDS32_INSN16)
+ {
+ /* A new INSN16 found, resize the old one. */
+ if (is_convert_32_to_16
+ (abfd, sec, irel, internal_relocs, irelend, &insn16)
+ || is_16bit_NOP (abfd, sec, irel))
+ {
+ if (insn_rel)
+ {
+ /* Previous INSN16 reloc exists, reduce its
+ size to 16-bit. */
+ if (is_convert_32_to_16 (abfd, sec, insn_rel, internal_relocs,
+ irelend, &insn16))
+ {
+ nds32_elf_write_16 (abfd, contents, insn_rel,
+ internal_relocs, irelend, insn16);
+
+ if (!insert_nds32_elf_blank_recalc_total
+ (relax_blank_list, insn_rel->r_offset + 2, 2))
+ return FALSE;
+ }
+ else if (is_16bit_NOP (abfd, sec, insn_rel))
+ {
+ if (!insert_nds32_elf_blank_recalc_total
+ (relax_blank_list, insn_rel->r_offset, 2))
+ return FALSE;
+ }
+ insn_rel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info), R_NDS32_NONE);
+ }
+ /* Save the new one for later use. */
+ insn_rel = irel;
+ }
+ else
+ irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info),
+ R_NDS32_NONE);
+ }
+ else if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL)
+ {
+ /* Search for label. */
+ int force_relax = 0;
+
+ /* Label on 16-bit instruction or optimization
+ needless, just reset this reloc. */
+ insn16 = bfd_getb16 (contents + irel->r_offset);
+ if ((irel->r_addend & 0x1f) < 2 && (!optimize || (insn16 & 0x8000)))
+ {
+ irel->r_info =
+ ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE);
+ continue;
+ }
+
+ address =
+ irel->r_offset - get_nds32_elf_blank_total (relax_blank_list,
+ irel->r_offset, 1);
+
+ if (!insn_rel)
+ {
+ /* Check if there is case which can not be aligned. */
+ if (irel->r_addend == 2 && address & 0x2)
+ return FALSE;
+ continue;
+ }
+
+ /* Try to align this label. */
+
+ if ((irel->r_addend & 0x1f) < 2)
+ {
+ /* Check if there is a INSN16 at the same address.
+ Label_rel always seats before insn_rel after
+ our sort. */
+
+ /* Search for INSN16 at LABEL location. If INSN16 is at
+ same location and this LABEL alignment is lower than 2,
+ the INSN16 can be converted to 2-byte. */
+ for (tmp_rel = irel;
+ tmp_rel < irelend && tmp_rel->r_offset == irel->r_offset;
+ tmp_rel++)
+ {
+ if (ELF32_R_TYPE (tmp_rel->r_info) == R_NDS32_INSN16
+ && (is_convert_32_to_16
+ (abfd, sec, tmp_rel, internal_relocs,
+ irelend, &insn16)
+ || is_16bit_NOP (abfd, sec, tmp_rel)))
+ {
+ force_relax = 1;
+ break;
+ }
+ }
+ }
+
+ if (force_relax || irel->r_addend == 1 || address & 0x2)
+ {
+ /* Label not aligned. */
+ /* Previous reloc exists, reduce its size to 16-bit. */
+ if (is_convert_32_to_16 (abfd, sec, insn_rel,
+ internal_relocs, irelend, &insn16))
+ {
+ nds32_elf_write_16 (abfd, contents, insn_rel,
+ internal_relocs, irelend, insn16);
+
+ if (!insert_nds32_elf_blank_recalc_total
+ (relax_blank_list, insn_rel->r_offset + 2, 2))
+ return FALSE;
+ }
+ else if (is_16bit_NOP (abfd, sec, insn_rel))
+ {
+ if (!insert_nds32_elf_blank_recalc_total
+ (relax_blank_list, insn_rel->r_offset, 2))
+ return FALSE;
+ }
+
+ }
+ /* INSN16 reloc is used. */
+ insn_rel = NULL;
+ }
+ }
+
+ address =
+ sec->size - get_nds32_elf_blank_total (relax_blank_list, sec->size, 0);
+ if (insn_rel && (address & 0x2 || opt_size))
+ {
+ if (is_convert_32_to_16 (abfd, sec, insn_rel, internal_relocs,
+ irelend, &insn16))
+ {
+ nds32_elf_write_16 (abfd, contents, insn_rel, internal_relocs,
+ irelend, insn16);
+ if (!insert_nds32_elf_blank_recalc_total
+ (relax_blank_list, insn_rel->r_offset + 2, 2))
+ return FALSE;
+ insn_rel->r_info = ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info),
+ R_NDS32_NONE);
+ }
+ else if (is_16bit_NOP (abfd, sec, insn_rel))
+ {
+ if (!insert_nds32_elf_blank_recalc_total
+ (relax_blank_list, insn_rel->r_offset, 2))
+ return FALSE;
+ insn_rel->r_info = ELF32_R_INFO (ELF32_R_SYM (insn_rel->r_info),
+ R_NDS32_NONE);
+ }
+ }
+ insn_rel = NULL;
+ return TRUE;
+}
+
+/* Pick relaxation round. */
+
+static int
+nds32_elf_pick_relax (bfd_boolean init, asection *sec, bfd_boolean *again,
+ struct elf_nds32_link_hash_table *table,
+ struct bfd_link_info *link_info)
+{
+ static asection *final_sec, *first_sec = NULL;
+ static bfd_boolean normal_again = FALSE;
+ static bfd_boolean set = FALSE;
+ static bfd_boolean first = TRUE;
+ int round_table[] = {
+ NDS32_RELAX_NORMAL_ROUND,
+ NDS32_RELAX_JUMP_IFC_ROUND,
+ NDS32_RELAX_EX9_BUILD_ROUND,
+ NDS32_RELAX_EX9_REPLACE_ROUND,
+ };
+ static int pass = 0;
+ static int relax_round;
+
+ /* The new round. */
+ if (init && first_sec == sec)
+ {
+ set = TRUE;
+ normal_again = FALSE;
+ }
+
+ if (first)
+ {
+ /* Run an empty run to get the final section. */
+ relax_round = NDS32_RELAX_EMPTY_ROUND;
+
+ /* It has to enter relax again because we can
+ not make sure what the final turn is. */
+ *again = TRUE;
+
+ first = FALSE;
+ first_sec = sec;
+ }
+
+ if (!set)
+ {
+ /* Not reenter yet. */
+ final_sec = sec;
+ return relax_round;
+ }
+
+ relax_round = round_table[pass];
+
+ if (!init && relax_round == NDS32_RELAX_NORMAL_ROUND && *again)
+ normal_again = TRUE;
+
+ if (!init && final_sec == sec)
+ {
+ switch (relax_round)
+ {
+ case NDS32_RELAX_NORMAL_ROUND:
+ if (!normal_again)
+ {
+ /* Normal relaxation done. */
+ if (table->target_optimize & NDS32_RELAX_JUMP_IFC_ON)
+ {
+ pass++;
+ *again = TRUE;
+ }
+ else if (table->target_optimize & NDS32_RELAX_EX9_ON)
+ {
+ pass += 2; /* NDS32_RELAX_EX9_BUILD_ROUND */
+ *again = TRUE;
+ }
+ else if (table->ex9_import_file)
+ {
+ /* Import ex9 table. */
+ if (table->update_ex9_table)
+ pass += 2; /* NDS32_RELAX_EX9_BUILD_ROUND */
+ else
+ pass += 3; /* NDS32_RELAX_EX9_REPLACE_ROUND */
+ nds32_elf_ex9_import_table (link_info);
+ *again = TRUE;
+ }
+ }
+ break;
+ case NDS32_RELAX_JUMP_IFC_ROUND:
+ if (!nds32_elf_ifc_finish (link_info))
+ (*_bfd_error_handler) (_("error: Jump IFC Fail."));
+ if (table->target_optimize & NDS32_RELAX_EX9_ON)
+ {
+ pass++;
+ *again = TRUE;
+ }
+ break;
+ case NDS32_RELAX_EX9_BUILD_ROUND:
+ nds32_elf_ex9_finish (link_info);
+ pass++;
+ *again = TRUE;
+ break;
+ case NDS32_RELAX_EX9_REPLACE_ROUND:
+ if (table->target_optimize & NDS32_RELAX_JUMP_IFC_ON)
+ {
+ /* Do jump IFC optimization again. */
+ if (!nds32_elf_ifc_finish (link_info))
+ (*_bfd_error_handler) (_("error: Jump IFC Fail."));
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return relax_round;
+}
+
+static bfd_boolean
+nds32_elf_relax_section (bfd *abfd, asection *sec,
+ struct bfd_link_info *link_info, bfd_boolean *again)
+{
+ nds32_elf_blank_t *relax_blank_list = NULL;
+ Elf_Internal_Shdr *symtab_hdr;
+ Elf_Internal_Rela *internal_relocs;
+ Elf_Internal_Rela *irel;
+ Elf_Internal_Rela *irelend;
+ Elf_Internal_Sym *isymbuf = NULL;
+ bfd_byte *contents = NULL;
+ bfd_boolean result = TRUE;
+ int optimize = 0;
+ int opt_size = 0;
+ uint32_t insn;
+ uint16_t insn16;
+
+ /* Target dependnet option. */
+ struct elf_nds32_link_hash_table *table;
+ int load_store_relax;
+ int relax_round;
+
+ relax_blank_list = NULL;
+
+ *again = FALSE;
+
+ /* Nothing to do for
+ * relocatable link or
+ * non-relocatable section or
+ * non-code section or
+ * empty content or
+ * no reloc entry. */
+ if (bfd_link_relocatable (link_info)
+ || (sec->flags & SEC_RELOC) == 0
+ || (sec->flags & SEC_EXCLUDE) == 1
+ || (sec->flags & SEC_CODE) == 0
+ || sec->size == 0)
+ return TRUE;
+
+ /* 09.12.11 Workaround. */
+ /* We have to adjust align for R_NDS32_LABEL if needed.
+ The adjust approach only can fix 2-byte align once. */
+ if (sec->alignment_power > 2)
+ return TRUE;
+
+ /* The optimization type to do. */
+
+ table = nds32_elf_hash_table (link_info);
+ relax_round = nds32_elf_pick_relax (TRUE, sec, again, table, link_info);
+ switch (relax_round)
+ {
+ case NDS32_RELAX_JUMP_IFC_ROUND:
+ /* Here is the entrance of ifc jump relaxation. */
+ if (!nds32_elf_ifc_calc (link_info, abfd, sec))
+ return FALSE;
+ nds32_elf_pick_relax (FALSE, sec, again, table, link_info);
+ return TRUE;
+
+ case NDS32_RELAX_EX9_BUILD_ROUND:
+ /* Here is the entrance of ex9 relaxation. There are two pass of
+ ex9 relaxation. The one is to traverse all instructions and build
+ the hash table. The other one is to compare instructions and replace
+ it by ex9.it. */
+ if (!nds32_elf_ex9_build_hash_table (abfd, sec, link_info))
+ return FALSE;
+ nds32_elf_pick_relax (FALSE, sec, again, table, link_info);
+ return TRUE;
+
+ case NDS32_RELAX_EX9_REPLACE_ROUND:
+ if (!nds32_elf_ex9_replace_instruction (link_info, abfd, sec))
+ return FALSE;
+ return TRUE;
+
+ case NDS32_RELAX_EMPTY_ROUND:
+ nds32_elf_pick_relax (FALSE, sec, again, table, link_info);
+ return TRUE;
+
+ case NDS32_RELAX_NORMAL_ROUND:
+ default:
+ if (sec->reloc_count == 0)
+ return TRUE;
+ break;
+ }
+
+ /* The begining of general relaxation. */
+
+ if (is_SDA_BASE_set == 0)
+ {
+ bfd_vma gp;
+ is_SDA_BASE_set = 1;
+ nds32_elf_final_sda_base (sec->output_section->owner, link_info,
+ &gp, FALSE);
+ relax_range_measurement (abfd);
+ }
+
+ if (is_ITB_BASE_set == 0)
+ {
+ /* Set the _ITB_BASE_. */
+ if (!nds32_elf_ex9_itb_base (link_info))
+ {
+ (*_bfd_error_handler) (_("%B: error: Cannot set _ITB_BASE_"), abfd);
+ bfd_set_error (bfd_error_bad_value);
+ }
+ }
+
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ /* Relocations MUST be kept in memory, because relaxation adjust them. */
+ internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+ TRUE /* keep_memory */);
+ if (internal_relocs == NULL)
+ goto error_return;
+
+ irelend = internal_relocs + sec->reloc_count;
+ irel = find_relocs_at_address (internal_relocs, internal_relocs,
+ irelend, R_NDS32_RELAX_ENTRY);
+
+ if (irel == irelend)
+ return TRUE;
+
+ if (ELF32_R_TYPE (irel->r_info) == R_NDS32_RELAX_ENTRY)
+ {
+ if (irel->r_addend & R_NDS32_RELAX_ENTRY_DISABLE_RELAX_FLAG)
+ {
+ nds32_elf_pick_relax (FALSE, sec, again, table, link_info);
+ return TRUE;
+ }
+
+ if (irel->r_addend & R_NDS32_RELAX_ENTRY_OPTIMIZE_FLAG)
+ optimize = 1;
+
+ if (irel->r_addend & R_NDS32_RELAX_ENTRY_OPTIMIZE_FOR_SPACE_FLAG)
+ opt_size = 1;
+ }
+
+ load_store_relax = table->load_store_relax;
+
+ /* Get symbol table and section content. */
+ if (!nds32_get_section_contents (abfd, sec, &contents, TRUE)
+ || !nds32_get_local_syms (abfd, sec, &isymbuf))
+ goto error_return;
+
+ /* Do relax loop only when finalize is not done.
+ Take care of relaxable relocs except INSN16. */
+ for (irel = internal_relocs; irel < irelend; irel++)
+ {
+ int seq_len; /* Original length of instruction sequence. */
+ int insn_len = 0; /* Final length of instruction sequence. */
+ bfd_boolean removed;
+
+ insn = 0;
+ if (ELF32_R_TYPE (irel->r_info) == R_NDS32_LABEL
+ && (irel->r_addend & 0x1f) >= 2)
+ optimize = 1;
+
+ /* Relocation Types
+ R_NDS32_LONGCALL1 53
+ R_NDS32_LONGCALL2 54
+ R_NDS32_LONGCALL3 55
+ R_NDS32_LONGJUMP1 56
+ R_NDS32_LONGJUMP2 57
+ R_NDS32_LONGJUMP3 58
+ R_NDS32_LOADSTORE 59 */
+ if (ELF32_R_TYPE (irel->r_info) >= R_NDS32_LONGCALL1
+ && ELF32_R_TYPE (irel->r_info) <= R_NDS32_LOADSTORE)
+ seq_len = GET_SEQ_LEN (irel->r_addend);
+
+ /* Relocation Types
+ R_NDS32_LONGCALL4 107
+ R_NDS32_LONGCALL5 108
+ R_NDS32_LONGCALL6 109
+ R_NDS32_LONGJUMP4 110
+ R_NDS32_LONGJUMP5 111
+ R_NDS32_LONGJUMP6 112
+ R_NDS32_LONGJUMP7 113 */
+ else if (ELF32_R_TYPE (irel->r_info) >= R_NDS32_LONGCALL4
+ && ELF32_R_TYPE (irel->r_info) <= R_NDS32_LONGJUMP7)
+ seq_len = 4;
+
+ /* Relocation Types
+ R_NDS32_LO12S0_RELA 30
+ R_NDS32_LO12S1_RELA 29
+ R_NDS32_LO12S2_RELA 28
+ R_NDS32_LO12S2_SP_RELA 71
+ R_NDS32_LO12S2_DP_RELA 70
+ R_NDS32_GOT_LO12 46
+ R_NDS32_GOTOFF_LO12 50
+ R_NDS32_PLTREL_LO12 65
+ R_NDS32_PLT_GOTREL_LO12 67
+ R_NDS32_17IFC_PCREL_RELA 96
+ R_NDS32_GOT_SUFF 193
+ R_NDS32_GOTOFF_SUFF 194
+ R_NDS32_PLT_GOT_SUFF 195
+ R_NDS32_MULCALL_SUFF 196
+ R_NDS32_PTR 197 */
+ else if ((ELF32_R_TYPE (irel->r_info) <= R_NDS32_LO12S0_RELA
+ && ELF32_R_TYPE (irel->r_info) >= R_NDS32_LO12S2_RELA)
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_SP_RELA
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_LO12S2_DP_RELA
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_GOT_LO12
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_GOTOFF_LO12
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_GOTPC_LO12
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_PLTREL_LO12
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_PLT_GOTREL_LO12
+ || (ELF32_R_TYPE (irel->r_info) >= R_NDS32_GOT_SUFF
+ && ELF32_R_TYPE (irel->r_info) <= R_NDS32_PTR)
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_17IFC_PCREL_RELA
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_TLS_LE_LO12
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_TLS_LE_ADD
+ || ELF32_R_TYPE (irel->r_info) == R_NDS32_TLS_LE_LS)
+ seq_len = 0;
+ else
+ continue;
+
+ insn_len = seq_len;
+ removed = FALSE;
+
+ switch (ELF32_R_TYPE (irel->r_info))
+ {
+ case R_NDS32_LONGCALL1:
+ removed = nds32_elf_relax_longcall1 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGCALL2:
+ removed = nds32_elf_relax_longcall2 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGCALL3:
+ removed = nds32_elf_relax_longcall3 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGJUMP1:
+ removed = nds32_elf_relax_longjump1 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGJUMP2:
+ removed = nds32_elf_relax_longjump2 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGJUMP3:
+ removed = nds32_elf_relax_longjump3 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGCALL4:
+ removed = nds32_elf_relax_longcall4 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGCALL5:
+ removed = nds32_elf_relax_longcall5 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGCALL6:
+ removed = nds32_elf_relax_longcall6 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGJUMP4:
+ removed = nds32_elf_relax_longjump4 (abfd, sec, irel, internal_relocs,
+ &insn_len, contents, isymbuf,
+ symtab_hdr);
+ break;
+ case R_NDS32_LONGJUMP5:
+ removed = nds32_elf_relax_longjump5 (abfd, sec, irel, internal_relocs,
+ &insn_len, &seq_len, contents,
+ isymbuf, symtab_hdr);
+ break;
+ case R_NDS32_LONGJUMP6:
+ removed = nds32_elf_relax_longjump6 (abfd, sec, irel, internal_relocs,
+ &insn_len, &seq_len, contents,
+ isymbuf, symtab_hdr);
+ break;
+ case R_NDS32_LONGJUMP7:
+ removed = nds32_elf_relax_longjump7 (abfd, sec, irel, internal_relocs,
+ &insn_len, &seq_len, contents,
+ isymbuf, symtab_hdr);
+ break;
+ case R_NDS32_LOADSTORE:
+ removed = nds32_elf_relax_loadstore (link_info, abfd, sec, irel,
+ internal_relocs, &insn_len,
+ contents, isymbuf, symtab_hdr,
+ load_store_relax);
+ break;
+ case R_NDS32_LO12S0_RELA:
+ case R_NDS32_LO12S1_RELA:
+ case R_NDS32_LO12S2_DP_RELA:
+ case R_NDS32_LO12S2_SP_RELA:
+ case R_NDS32_LO12S2_RELA:
+ /* Relax for low part. */
+ nds32_elf_relax_lo12 (link_info, abfd, sec, irel, internal_relocs,
+ contents, isymbuf, symtab_hdr);