From 06096f6e0c0ca69bcd265286121bc8ad3c98ac7c Mon Sep 17 00:00:00 2001 From: Nelson Chu Date: Thu, 23 Aug 2018 11:19:08 +0800 Subject: [PATCH 10/13] NDS32: Update the gp related relaxations. To the begin with, nds32_elf_final_sda_base is used to find the suitable global pointer for each relax round. We improve it to find the better gp. Furthermore, we notice that gp may changed dramatically after relaxing, and then cause the truncated-to-fit errors sometimes. It is related to the DATA_SEGMENT_ALIGN in the linker script. Generally, gp is fixed or smaller after relaxing, but the large DATA_SEGMENT_ALIGN size may make gp even bigger. We didn't consider the latter originally. Therefore, we must reserve more size (minimum but safe enough size) to prevent the truncated error when handling gp relaxations. bfd/ * elf32-nds32.c (NDS32_GUARD_SEC_P, elf32_nds32_local_gp_offset): New macro. (struct elf_nds32_link_hash_entry): New `offset_to_gp' field. (struct elf_nds32_obj_tdata): New `offset_to_gp' and `hdr_size' fields. (elf32_nds32_allocate_local_sym_info, nds32_elf_relax_guard, nds32_elf_is_target_special_symbol, nds32_elf_maybe_function_sym): New functions. (nds32_info_to_howto_rel): Add BFD_ASSERT. (bfd_elf32_bfd_reloc_type_table_lookup, nds32_elf_link_hash_newfunc, nds32_elf_link_hash_table_create, nds32_elf_relocate_section, nds32_elf_relax_loadstore, nds32_elf_relax_lo12, nds32_relax_adjust_label, bfd_elf32_nds32_set_target_option, nds32_fag_mark_relax): Updated. (nds32_elf_final_sda_base): Improve it to find the better gp value. (insert_nds32_elf_blank): Must consider `len' when inserting blanks. * elf32-nds32.h (bfd_elf32_nds32_set_target_option): Update prototype. (struct elf_nds32_link_hash_table): Add new variable `hyper_relax'. ld/ * emultempl/nds32elf.em (hyper_relax): New variable. (nds32_elf_create_output_section_statements): the parameters of bfd_elf32_nds32_set_target_option (PARSE_AND_LIST_PROLOGUE, PARSE_AND_LIST_OPTIONS, PARSE_AND_LIST_ARGS_CASES): Add new option --mhyper-relax. --- bfd/ChangeLog | 19 ++ bfd/elf32-nds32.c | 612 ++++++++++++++++++++++++++++----------- bfd/elf32-nds32.h | 4 +- ld/ChangeLog | 8 + ld/emultempl/nds32elf.em | 20 ++ 5 files changed, 498 insertions(+), 165 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 93b5ddccb8..b630b39402 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,22 @@ +2018-09-17 Nelson Chu + + * elf32-nds32.c (NDS32_GUARD_SEC_P, elf32_nds32_local_gp_offset): + New macro. + (struct elf_nds32_link_hash_entry): New `offset_to_gp' field. + (struct elf_nds32_obj_tdata): New `offset_to_gp' and `hdr_size' fields. + (elf32_nds32_allocate_local_sym_info, nds32_elf_relax_guard, + nds32_elf_is_target_special_symbol, nds32_elf_maybe_function_sym): + New functions. + (nds32_info_to_howto_rel): Add BFD_ASSERT. + (bfd_elf32_bfd_reloc_type_table_lookup, nds32_elf_link_hash_newfunc, + nds32_elf_link_hash_table_create, nds32_elf_relocate_section, + nds32_elf_relax_loadstore, nds32_elf_relax_lo12, nds32_relax_adjust_label, + bfd_elf32_nds32_set_target_option, nds32_fag_mark_relax): Updated. + (nds32_elf_final_sda_base): Improve it to find the better gp value. + (insert_nds32_elf_blank): Must consider `len' when inserting blanks. + * elf32-nds32.h (bfd_elf32_nds32_set_target_option): Update prototype. + (struct elf_nds32_link_hash_table): Add new variable `hyper_relax'. + 2018-09-17 Nelson Chu * reloc.c: Add BFD_RELOC_NDS32_LSI. diff --git a/bfd/elf32-nds32.c b/bfd/elf32-nds32.c index 49b35ef9eb..1ac95be58f 100644 --- a/bfd/elf32-nds32.c +++ b/bfd/elf32-nds32.c @@ -59,7 +59,7 @@ static bfd_reloc_status_type nds32_elf_do_9_pcrel_reloc /* Nds32 helper functions. */ static bfd_vma calculate_memory_address -(bfd *, Elf_Internal_Rela *, Elf_Internal_Sym *, Elf_Internal_Shdr *); + (bfd *, Elf_Internal_Rela *, Elf_Internal_Sym *, Elf_Internal_Shdr *); static int nds32_get_section_contents (bfd *, asection *, bfd_byte **, bfd_boolean); static int nds32_get_local_syms (bfd *, asection *ATTRIBUTE_UNUSED, @@ -87,6 +87,10 @@ enum section. */ #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" +#define NDS32_GUARD_SEC_P(flags) ((flags) & SEC_ALLOC \ + && (flags) & SEC_LOAD \ + && (flags) & SEC_READONLY) + /* The nop opcode we use. */ #define NDS32_NOP32 0x40000009 #define NDS32_NOP16 0x9200 @@ -190,6 +194,8 @@ struct elf_nds32_link_hash_entry #define GOT_NORMAL 1 #define GOT_TLS_IE 2 unsigned int tls_type; + + int offset_to_gp; }; /* Get the nds32 ELF linker hash table from a link_info structure. */ @@ -206,6 +212,9 @@ struct elf_nds32_obj_tdata /* tls_type for each local got entry. */ char *local_got_tls_type; + + unsigned int hdr_size; + int* offset_to_gp; }; #define elf_nds32_tdata(bfd) \ @@ -214,6 +223,9 @@ struct elf_nds32_obj_tdata #define elf32_nds32_local_got_tls_type(bfd) \ (elf_nds32_tdata (bfd)->local_got_tls_type) +#define elf32_nds32_local_gp_offset(bfd) \ + (elf_nds32_tdata (bfd)->offset_to_gp) + #define elf32_nds32_hash_entry(ent) ((struct elf_nds32_link_hash_entry *)(ent)) static bfd_boolean @@ -2871,6 +2883,13 @@ bfd_elf32_bfd_reloc_type_table_lookup (enum elf_nds32_reloc_type code) } else { + if ((size_t) (code - R_NDS32_RELAX_ENTRY) + >= ARRAY_SIZE (nds32_elf_relax_howto_table)) + { + int i = code; + i += 1; + } + BFD_ASSERT ((size_t) (code - R_NDS32_RELAX_ENTRY) < ARRAY_SIZE (nds32_elf_relax_howto_table)); return &nds32_elf_relax_howto_table[code - R_NDS32_RELAX_ENTRY]; @@ -2887,7 +2906,7 @@ bfd_elf32_bfd_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, { if (nds32_reloc_map[i].bfd_reloc_val == code) return bfd_elf32_bfd_reloc_type_table_lookup - (nds32_reloc_map[i].elf_reloc_val); + (nds32_reloc_map[i].elf_reloc_val); } return NULL; @@ -2910,6 +2929,8 @@ nds32_info_to_howto_rel (bfd *abfd, arelent *cache_ptr, bfd_set_error (bfd_error_bad_value); return FALSE; } + + BFD_ASSERT (ELF32_R_TYPE (dst->r_info) <= R_NDS32_GNU_VTENTRY); cache_ptr->howto = bfd_elf32_bfd_reloc_type_table_lookup (r_type); return TRUE; } @@ -3121,12 +3142,13 @@ nds32_elf_final_sda_base (bfd *output_bfd, struct bfd_link_info *info, struct elf_nds32_link_hash_table *table; struct bfd_link_hash_entry *h, *h2; long unsigned int total = 0; + asection *first = NULL, *final = NULL, *temp; + bfd_vma sda_base = 0; h = bfd_link_hash_lookup (info->hash, "_SDA_BASE_", FALSE, FALSE, TRUE); - if (!h || (h->type != bfd_link_hash_defined && h->type != bfd_link_hash_defweak)) + if (!h || (h->type != bfd_link_hash_defined + && h->type != bfd_link_hash_defweak)) { - asection *first = NULL, *final = NULL, *temp; - bfd_vma sda_base; /* The first section must be 4-byte aligned to promise _SDA_BASE_ being 4 byte-aligned. Therefore, it has to set the first section ".data" 4 byte-aligned. */ @@ -3144,7 +3166,7 @@ nds32_elf_final_sda_base (bfd *output_bfd, struct bfd_link_info *info, } /* Get the first and final section. */ - while (i < sizeof (sec_name) / sizeof (sec_name [0])) + while (i < ARRAY_SIZE (sec_name)) { temp = bfd_get_section_by_name (output_bfd, sec_name[i]); if (temp && !first && (temp->size != 0 || temp->rawsize != 0)) @@ -3186,7 +3208,7 @@ nds32_elf_final_sda_base (bfd *output_bfd, struct bfd_link_info *info, /* Find the section sda_base located. */ i = 0; - while (i < sizeof (sec_name) / sizeof (sec_name [0])) + while (i < ARRAY_SIZE (sec_name)) { final = bfd_get_section_by_name (output_bfd, sec_name[i]); if (final && (final->size != 0 || final->rawsize != 0) @@ -3201,45 +3223,81 @@ nds32_elf_final_sda_base (bfd *output_bfd, struct bfd_link_info *info, } else { - /* There is not any data section in output bfd, and set _SDA_BASE_ in - first output section. */ - first = output_bfd->sections; - while (first && first->size == 0 && first->rawsize == 0) - first = first->next; + /* If there is not any default data section in output bfd, try to find + the first data section. If no data section be found, just simplily + choose the first output section. */ + temp = output_bfd->sections; + while (temp) + { + if (temp->flags & SEC_ALLOC + && (((temp->flags & SEC_DATA) + && ((temp->flags & SEC_READONLY) == 0)) + || (temp->flags & SEC_LOAD) == 0) + && (temp->size != 0 || temp->rawsize != 0)) + { + if (!first) + first = temp; + final = temp; + } + temp = temp->next; + } + + /* There is no data or bss section. */ + if (!first || (first->size == 0 && first->rawsize == 0)) + { + first = output_bfd->sections; + while (first && first->size == 0 && first->rawsize == 0) + first = first->next; + } + + /* There is no concrete section. */ if (!first) { *psb = elf_gp (output_bfd); return bfd_reloc_ok; } - sda_base = first->vma + first->rawsize; + + if (final && (final->vma + final->rawsize - first->vma) <= 0x4000) + sda_base = final->vma / 2 + final->rawsize / 2 + first->vma / 2; + else + sda_base = first->vma + 0x2000; } sda_base -= first->vma; sda_base = sda_base & (~7); if (!_bfd_generic_link_add_one_symbol - (info, output_bfd, "_SDA_BASE_", BSF_GLOBAL | BSF_WEAK, first, - (bfd_vma) sda_base, (const char *) NULL, FALSE, - get_elf_backend_data (output_bfd)->collect, &h)) + (info, output_bfd, "_SDA_BASE_", BSF_GLOBAL | BSF_WEAK, first, + (bfd_vma) sda_base, (const char *) NULL, FALSE, + get_elf_backend_data (output_bfd)->collect, &h)) return FALSE; sda_rela_sec = first; + } - table = nds32_elf_hash_table (info); - relax_fp_as_gp = table->relax_fp_as_gp; - if (relax_fp_as_gp) - { - h2 = bfd_link_hash_lookup (info->hash, FP_BASE_NAME, - FALSE, FALSE, FALSE); - /* Define a weak FP_BASE_NAME here to prevent the undefined symbol. - And set FP equal to SDA_BASE to do relaxation for - la $fp, _FP_BASE_. */ - if (!_bfd_generic_link_add_one_symbol - (info, output_bfd, FP_BASE_NAME, BSF_GLOBAL | BSF_WEAK, - first, (bfd_vma) sda_base, (const char *) NULL, - FALSE, get_elf_backend_data (output_bfd)->collect, &h2)) - return FALSE; - } + /* Set _FP_BASE_ to _SDA_BASE_. */ + table = nds32_elf_hash_table (info); + relax_fp_as_gp = table->relax_fp_as_gp; + h2 = bfd_link_hash_lookup (info->hash, FP_BASE_NAME, FALSE, FALSE, FALSE); + /* _SDA_BASE_ is difined in linker script. */ + if (!first) + { + first = h->u.def.section; + sda_base = h->u.def.value; + } + + if (relax_fp_as_gp && h2 + && (h2->type == bfd_link_hash_undefweak + || h2->type == bfd_link_hash_undefined)) + { + /* Define a weak FP_BASE_NAME here to prevent the undefined symbol. + And set FP equal to SDA_BASE to do relaxation for + la $fp, _FP_BASE_. */ + if (!_bfd_generic_link_add_one_symbol + (info, output_bfd, FP_BASE_NAME, BSF_GLOBAL | BSF_WEAK, + first, sda_base, (const char *) NULL, + FALSE, get_elf_backend_data (output_bfd)->collect, &h2)) + return FALSE; } if (add_symbol) @@ -3258,8 +3316,9 @@ nds32_elf_final_sda_base (bfd *output_bfd, struct bfd_link_info *info, } } - *psb = h->u.def.value + h->u.def.section->output_section->vma - + h->u.def.section->output_offset; + *psb = h->u.def.value + + h->u.def.section->output_section->vma + + h->u.def.section->output_offset; return bfd_reloc_ok; } @@ -3299,6 +3358,7 @@ nds32_elf_link_hash_newfunc (struct bfd_hash_entry *entry, eh = (struct elf_nds32_link_hash_entry *) ret; eh->dyn_relocs = NULL; eh->tls_type = GOT_UNKNOWN; + eh->offset_to_gp = 0; } return (struct bfd_hash_entry *) ret; @@ -3327,6 +3387,10 @@ nds32_elf_link_hash_table_create (bfd *abfd) return NULL; } + ret->sdynbss = NULL; + ret->srelbss = NULL; + ret->sym_ld_script = NULL; + return &ret->root.root; } @@ -4413,6 +4477,7 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, table = nds32_elf_hash_table (info); eliminate_gc_relocs = table->eliminate_gc_relocs; /* By this time, we can adjust the value of _SDA_BASE_. */ + /* Explain _SDA_BASE_ */ if ((!bfd_link_relocatable (info))) { is_SDA_BASE_set = 1; @@ -4435,6 +4500,7 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, Elf_Internal_Sym *sym = NULL; asection *sec; bfd_vma relocation; + Elf_Internal_Rela *lorel; /* We can't modify r_addend here as elf_link_input_bfd has an assert to ensure it's zero (we use REL relocs, not RELA). Therefore this @@ -4462,8 +4528,7 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, || r_type == R_NDS32_RELA_GNU_VTINHERIT || (r_type >= R_NDS32_INSN16 && r_type <= R_NDS32_25_FIXED_RELA) || r_type == R_NDS32_DATA - || r_type == R_NDS32_TRAN - || (r_type >= R_NDS32_LONGCALL4 && r_type <= R_NDS32_LONGJUMP7)) + || r_type == R_NDS32_TRAN) continue; /* If we enter the fp-as-gp region. Resolve the address @@ -4484,9 +4549,13 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, fpbase_addr = elf_gp (output_bfd); } - if (((r_type >= R_NDS32_DWARF2_OP1_RELA - && r_type <= R_NDS32_DWARF2_LEB_RELA) - || r_type >= R_NDS32_RELAX_ENTRY) && !bfd_link_relocatable (info)) + /* Skip the relocations used for relaxation. */ + /* We have to update LONGCALL and LONGJUMP relocations + when generating the relocatable files. */ + if (!bfd_link_relocatable (info) + && (r_type >= R_NDS32_RELAX_ENTRY + || (r_type >= R_NDS32_LONGCALL4 + && r_type <= R_NDS32_LONGJUMP7))) continue; howto = bfd_elf32_bfd_reloc_type_table_lookup (r_type); @@ -4505,10 +4574,24 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); addend = rel->r_addend; + + if (bfd_link_relocatable (info)) + { + /* This is a relocatable link. We don't have to change + anything, unless the reloc is against a section symbol, + in which case we have to adjust according to where the + section symbol winds up in the output section. */ + if (sym != NULL && ELF_ST_TYPE (sym->st_info) == STT_SECTION) + rel->r_addend += sec->output_offset + sym->st_value; + + continue; + } } else { /* External symbol. */ + if (bfd_link_relocatable (info)) + continue; bfd_boolean warned, ignored, unresolved_reloc; int symndx = r_symndx - symtab_hdr->sh_info; @@ -4521,6 +4604,20 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, Handle it specially. */ switch ((int) r_type) { + case R_NDS32_HI20_RELA: + case R_NDS32_LO12S0_RELA: + if (strcmp (elf_sym_hashes (input_bfd)[symndx]->root.root.string, + FP_BASE_NAME) == 0) + { + if (!bfd_link_pie (info)) + { + _bfd_error_handler + ("%pB: warning: _FP_BASE_ setting insns relaxation failed.", + input_bfd); + } + relocation = fpbase_addr; + break; + } case R_NDS32_SDA19S0_RELA: case R_NDS32_SDA15S0_RELA: case R_NDS32_20_RELA: @@ -4531,19 +4628,6 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, break; } } - - } - - if (bfd_link_relocatable (info)) - { - /* This is a relocatable link. We don't have to change - anything, unless the reloc is against a section symbol, - in which case we have to adjust according to where the - section symbol winds up in the output section. */ - if (sym != NULL && ELF_ST_TYPE (sym->st_info) == STT_SECTION) - rel->r_addend += sec->output_offset + sym->st_value; - - continue; } /* Sanity check the address. */ @@ -4553,9 +4637,7 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, goto check_reloc; } - if ((r_type >= R_NDS32_DWARF2_OP1_RELA - && r_type <= R_NDS32_DWARF2_LEB_RELA) - || r_type >= R_NDS32_RELAX_ENTRY) + if (r_type >= R_NDS32_RELAX_ENTRY) continue; switch ((int) r_type) @@ -4907,32 +4989,27 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, goto check_reloc; case R_NDS32_HI20: + /* We allow an arbitrary number of HI20 relocs before the + LO12 reloc. This permits gcc to emit the HI and LO relocs + itself. */ + for (lorel = rel + 1; + (lorel < relend + && ELF32_R_TYPE (lorel->r_info) == R_NDS32_HI20); lorel++) + continue; + if (lorel < relend + && (ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S3 + || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S2 + || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S1 + || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S0)) { - Elf_Internal_Rela *lorel; - - /* We allow an arbitrary number of HI20 relocs before the - LO12 reloc. This permits gcc to emit the HI and LO relocs - itself. */ - for (lorel = rel + 1; - (lorel < relend - && ELF32_R_TYPE (lorel->r_info) == R_NDS32_HI20); lorel++) - continue; - if (lorel < relend - && (ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S3 - || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S2 - || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S1 - || ELF32_R_TYPE (lorel->r_info) == R_NDS32_LO12S0)) - { - nds32_elf_relocate_hi20 (input_bfd, r_type, rel, lorel, - contents, relocation + addend); - r = bfd_reloc_ok; - } - else - r = _bfd_final_link_relocate (howto, input_bfd, input_section, - contents, offset, relocation, - addend); + nds32_elf_relocate_hi20 (input_bfd, r_type, rel, lorel, + contents, relocation + addend); + r = bfd_reloc_ok; } - + else + r = _bfd_final_link_relocate (howto, input_bfd, input_section, + contents, offset, relocation, + addend); goto check_reloc; case R_NDS32_GOT17S2_RELA: @@ -5059,48 +5136,46 @@ nds32_elf_relocate_section (bfd * output_bfd ATTRIBUTE_UNUSED, case R_NDS32_SDA19S0_RELA: case R_NDS32_SDA15S0_RELA: case R_NDS32_SDA15S0: - { - align = 0x0; + align = 0x0; handle_sda: - BFD_ASSERT (sec != NULL); + BFD_ASSERT (sec != NULL); - /* If the symbol is in the abs section, the out_bfd will be null. - This happens when the relocation has a symbol@GOTOFF. */ - r = nds32_elf_final_sda_base (output_bfd, info, &gp, FALSE); - if (r != bfd_reloc_ok) - { - _bfd_error_handler - (_("%pB: warning: relocate SDA_BASE failed"), input_bfd); - ret = FALSE; - goto check_reloc; - } + /* If the symbol is in the abs section, the out_bfd will be null. + This happens when the relocation has a symbol@GOTOFF. */ + r = nds32_elf_final_sda_base (output_bfd, info, &gp, FALSE); + if (r != bfd_reloc_ok) + { + _bfd_error_handler + (_("%pB: warning: relocate SDA_BASE failed"), input_bfd); + ret = FALSE; + goto check_reloc; + } - /* At this point `relocation' contains the object's - address. */ - if (r_type == R_NDS32_SDA_FP7U2_RELA) - { - relocation -= fpbase_addr; - } - else - relocation -= gp; - /* Now it contains the offset from _SDA_BASE_. */ + /* At this point `relocation' contains the object's + address. */ + if (r_type == R_NDS32_SDA_FP7U2_RELA) + { + relocation -= fpbase_addr; + } + else + relocation -= gp; + /* Now it contains the offset from _SDA_BASE_. */ - /* Make sure alignment is correct. */ + /* Make sure alignment is correct. */ - if (relocation & align) - { - /* Incorrect alignment. */ - _bfd_error_handler - /* xgettext:c-format */ - (_("%pB(%pA): warning: unaligned small data access" - " of type %d"), - input_bfd, input_section, r_type); - ret = FALSE; - goto check_reloc; - } + if (relocation & align) + { + /* Incorrect alignment. */ + _bfd_error_handler + /* xgettext:c-format */ + (_("%pB(%pA): warning: unaligned small data access" + " of type %d"), + input_bfd, input_section, r_type); + ret = FALSE; + goto check_reloc; } - break; + case R_NDS32_17IFC_PCREL_RELA: case R_NDS32_10IFCU_PCREL_RELA: /* do nothing */ @@ -5340,7 +5415,7 @@ check_reloc: errmsg = _("internal error: unknown error"); /* Fall through. */ - common_error: +common_error: (*info->callbacks->warning) (info, errmsg, name, input_bfd, input_section, offset); break; @@ -5348,6 +5423,10 @@ check_reloc: } } + /* Resotre header size to avoid overflow load. */ + if (elf_nds32_tdata (input_bfd)->hdr_size != 0) + symtab_hdr->sh_size = elf_nds32_tdata (input_bfd)->hdr_size; + return ret; } @@ -6082,6 +6161,38 @@ nds32_elf_gc_mark_hook (asection *sec, struct bfd_link_info *info, return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } +/* Ensure that we have allocated bookkeeping structures for ABFD's local + symbols. */ + +static bfd_boolean +elf32_nds32_allocate_local_sym_info (bfd *abfd) +{ + if (elf_local_got_refcounts (abfd) == NULL) + { + bfd_size_type num_syms; + bfd_size_type size; + char *data; + + num_syms = elf_tdata (abfd)->symtab_hdr.sh_info; + /* This space is for got_refcounts, got_tls_type, tlsdesc_gotent, and + gp_offset. The details can refer to struct elf_nds32_obj_tdata. */ + size = num_syms * (sizeof (bfd_signed_vma) + sizeof (char) + + sizeof (bfd_vma) + sizeof (int) + + sizeof (bfd_boolean) + sizeof (bfd_vma)); + data = bfd_zalloc (abfd, size); + if (data == NULL) + return FALSE; + + elf_local_got_refcounts (abfd) = (bfd_signed_vma *) data; + data += num_syms * sizeof (bfd_signed_vma); + + elf32_nds32_local_gp_offset (abfd) = (int *) data; + data += num_syms * sizeof (int); + } + + return TRUE; +} + /* Look through the relocs for a section during the first phase. Since we don't do .gots or .plts, we just need to consider the virtual table relocs for gc. */ @@ -8136,8 +8247,9 @@ insert_nds32_elf_blank (nds32_elf_blank_t **blank_p, bfd_vma addr, bfd_vma len) if (addr < blank_t->offset + blank_t->size) { - if (addr > blank_t->offset + blank_t->size) - blank_t->size = addr - blank_t->offset; + /* Extend the origin blank. */ + if (addr + len > blank_t->offset + blank_t->size) + blank_t->size = addr + len - blank_t->offset; } else { @@ -8599,7 +8711,7 @@ relax_range_measurement (bfd *abfd) bfd_vma align; static int decide_relax_range = 0; int i; - int range_number = sizeof (sdata_init_range) / sizeof (sdata_init_range[0]); + int range_number = ARRAY_SIZE (sdata_init_range); if (decide_relax_range) return; @@ -10371,6 +10483,134 @@ nds32_elf_relax_longjump7 (bfd *abfd, asection *sec, Elf_Internal_Rela *irel, return TRUE; } +/* We figure out and reassign the best gp value in nds32_elf_final_sda_base + for each relax round. But the gp may changed dramatically and then cause + the truncated to fit errors for the the converted gp instructions. + Therefore, we must reserve the minimum but safe enough size to prevent it. */ + +static bfd_boolean +nds32_elf_relax_guard (bfd_vma *access_addr, bfd_vma local_sda, asection *sec, + Elf_Internal_Rela *irel, bfd_boolean *again, + bfd_boolean init, + struct elf_nds32_link_hash_table *table, + Elf_Internal_Sym *isymbuf, Elf_Internal_Shdr *symtab_hdr) + +{ + int offset_to_gp; + static bfd_boolean sec_pass = FALSE; + static asection *first_sec = NULL, *sym_sec; + /* Record the number of instructions which may be removed. */ + static int count = 0, record_count; + Elf_Internal_Sym *isym; + struct elf_link_hash_entry *h = NULL; + int indx; + unsigned long r_symndx; + bfd *abfd = sec->owner; + static bfd_vma record_sda = 0; + int sda_offset = 0; + + /* Force doing relaxation when hyper-relax is high. */ + if (table->hyper_relax == 2) + return TRUE; + + /* Do not relax the load/store patterns for the first + relax round. */ + if (init) + { + if (!first_sec) + first_sec = sec; + else if (first_sec == sec) + { + record_count = count; + count = 0; + sec_pass = TRUE; + } + + if (!sec_pass) + *again = TRUE; + + return TRUE; + } + + /* Generally, _SDA_BASE_ is fixed or smaller. But the large + DATA_SEGMENT_ALIGN size in the linker script may make it + get even bigger. */ + if (record_sda == 0) + record_sda = local_sda; + else if (local_sda > record_sda) + sda_offset = local_sda - record_sda; + + /* Assume the instruction will be removed in the best case. */ + count++; + + /* We record the offset to gp for each symbol, and then check + if it is changed dramatically after relaxing. + (global symbol): elf32_nds32_hash_entry (h)->offset_to_gp + (local symbol) : elf32_nds32_local_gp_offset (abfd)[r_symndx]. */ + r_symndx = ELF32_R_SYM (irel->r_info); + if (r_symndx >= symtab_hdr->sh_info) + { + /* Global symbols. */ + indx = ELF32_R_SYM (irel->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + sym_sec = h->root.u.def.section; + if (NDS32_GUARD_SEC_P (sym_sec->flags) + || bfd_is_abs_section (sym_sec)) + { + /* Forbid doing relaxation when hyper-relax is low. */ + if (table->hyper_relax == 0) + return FALSE; + + offset_to_gp = *access_addr - local_sda; + if (elf32_nds32_hash_entry (h)->offset_to_gp == 0) + elf32_nds32_hash_entry (h)->offset_to_gp = offset_to_gp; + else if (abs (elf32_nds32_hash_entry (h)->offset_to_gp) + < abs (offset_to_gp) - sda_offset) + { + /* This may cause the error, so we reserve the + safe enough size for relaxing. */ + if (*access_addr >= local_sda) + *access_addr += (record_count * 4); + else + *access_addr -= (record_count * 4); + } + return sec_pass; + } + } + else + { + /* Local symbols. */ + if (!elf32_nds32_allocate_local_sym_info (abfd)) + return FALSE; + isym = isymbuf + r_symndx; + + sym_sec = bfd_section_from_elf_index (abfd, isym->st_shndx); + if (NDS32_GUARD_SEC_P (sym_sec->flags)) + { + /* Forbid doing relaxation when hyper-relax is low. */ + if (table->hyper_relax == 0) + return FALSE; + + offset_to_gp = *access_addr - local_sda; + if (elf32_nds32_local_gp_offset (abfd)[r_symndx] == 0) + elf32_nds32_local_gp_offset (abfd)[r_symndx] = offset_to_gp; + else if (abs (elf32_nds32_local_gp_offset (abfd)[r_symndx]) + < abs (offset_to_gp) - sda_offset) + { + /* This may cause the error, so we reserve the + safe enough size for relaxing. */ + if (*access_addr >= local_sda) + *access_addr += (record_count * 4); + else + *access_addr -= (record_count * 4); + } + return sec_pass; + } + } + + return TRUE; +} + #define GET_LOADSTORE_RANGE(addend) (((addend) >> 8) & 0x3f) /* Relax LOADSTORE relocation for nds32_elf_relax_section. */ @@ -10380,7 +10620,8 @@ nds32_elf_relax_loadstore (struct bfd_link_info *link_info, bfd *abfd, asection *sec, Elf_Internal_Rela *irel, Elf_Internal_Rela *internal_relocs, int *insn_len, bfd_byte *contents, Elf_Internal_Sym *isymbuf, - Elf_Internal_Shdr *symtab_hdr, int load_store_relax) + Elf_Internal_Shdr *symtab_hdr, int load_store_relax, + struct elf_nds32_link_hash_table *table) { int eliminate_sethi = 0, range_type; unsigned int i; @@ -10390,6 +10631,8 @@ nds32_elf_relax_loadstore (struct bfd_link_info *link_info, bfd *abfd, Elf_Internal_Rela *hi_irelfn = NULL, *irelend; bfd_vma access_addr = 0; bfd_vma range_l = 0, range_h = 0; /* Upper/lower bound. */ + struct elf_link_hash_entry *h = NULL; + int indx; enum elf_nds32_reloc_type checked_types[] = { R_NDS32_HI20_RELA, R_NDS32_GOT_HI20, R_NDS32_GOTPC_HI20, R_NDS32_GOTOFF_HI20, @@ -10413,9 +10656,11 @@ nds32_elf_relax_loadstore (struct bfd_link_info *link_info, bfd *abfd, if (hi_irelfn == irelend) { - _bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LOADSTORE", - (uint64_t) irel->r_offset); - return FALSE; + /* Not R_NDS32_HI20_RELA. */ + if (i != 0) + _bfd_error_handler (unrecognized_reloc_msg, abfd, "R_NDS32_LOADSTORE", + (uint64_t) irel->r_offset); + return FALSE; } range_type = GET_LOADSTORE_RANGE (irel->r_addend); @@ -10429,36 +10674,36 @@ nds32_elf_relax_loadstore (struct bfd_link_info *link_info, bfd *abfd, access_addr = calculate_memory_address (abfd, hi_irelfn, isymbuf, symtab_hdr); - if (range_type == NDS32_LOADSTORE_IMM) + if (ELF32_R_SYM (hi_irelfn->r_info) >= symtab_hdr->sh_info) { - struct elf_link_hash_entry *h = NULL; - int indx; - - if (ELF32_R_SYM (hi_irelfn->r_info) >= symtab_hdr->sh_info) - { - indx = ELF32_R_SYM (hi_irelfn->r_info) - symtab_hdr->sh_info; - h = elf_sym_hashes (abfd)[indx]; - } + indx = ELF32_R_SYM (hi_irelfn->r_info) - symtab_hdr->sh_info; + h = elf_sym_hashes (abfd)[indx]; + } - if ((access_addr < CONSERVATIVE_20BIT) - && (!h || (h && strcmp (h->root.root.string, FP_BASE_NAME) != 0))) - { - eliminate_sethi = 1; - break; - } + /* Try movi. */ + if (range_type == NDS32_LOADSTORE_IMM + && access_addr < CONSERVATIVE_20BIT + && (!h || (h && strcmp (h->root.root.string, FP_BASE_NAME) != 0))) + { + eliminate_sethi = 1; + break; + } - /* This is avoid to relax symbol address which is fixed - relocations. Ex: _stack. */ - if (h && bfd_is_abs_symbol (&h->root)) - return FALSE; + if (h && strcmp (h->root.root.string, FP_BASE_NAME) == 0) + { + eliminate_sethi = 1; + break; } + else if (!nds32_elf_relax_guard (&access_addr, local_sda, sec, hi_irelfn, + NULL, FALSE, table, isymbuf, symtab_hdr)) + return FALSE; if (!load_store_relax) return FALSE; /* Case for set gp register. */ if (N32_RT5 (insn) == REG_GP) - break; + return FALSE; if (range_type == NDS32_LOADSTORE_FLOAT_S || range_type == NDS32_LOADSTORE_FLOAT_D) @@ -10487,8 +10732,9 @@ nds32_elf_relax_loadstore (struct bfd_link_info *link_info, bfd *abfd, irel->r_info = ELF32_R_INFO (ELF32_R_SYM (irel->r_info), R_NDS32_NONE); *insn_len = 0; + return TRUE; } - return TRUE; + return FALSE; } /* Relax LO12 relocation for nds32_elf_relax_section. */ @@ -10497,7 +10743,8 @@ static void nds32_elf_relax_lo12 (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) + Elf_Internal_Sym *isymbuf, Elf_Internal_Shdr *symtab_hdr, + struct elf_nds32_link_hash_table *table) { uint32_t insn; bfd_vma local_sda, laddr; @@ -10527,6 +10774,7 @@ nds32_elf_relax_lo12 (struct bfd_link_info *link_info, bfd *abfd, h = elf_sym_hashes (abfd)[indx]; } + /* Try movi. */ if (N32_OP6 (insn) == N32_OP6_ORI && access_addr < CONSERVATIVE_20BIT && (!h || (h && strcmp (h->root.root.string, FP_BASE_NAME) != 0))) { @@ -10535,13 +10783,16 @@ nds32_elf_relax_lo12 (struct bfd_link_info *link_info, bfd *abfd, insn = N32_TYPE1 (MOVI, N32_RT5 (insn), 0); bfd_putb32 (insn, contents + laddr); } - /* This is avoid to relax symbol address which is fixed - relocations. Ex: _stack. */ - else if (N32_OP6 (insn) == N32_OP6_ORI - && h && bfd_is_abs_symbol (&h->root)) - return; else { + if (h && strcmp (h->root.root.string, FP_BASE_NAME) == 0) + { + /* Fall through. */ + } + else if (!nds32_elf_relax_guard (&access_addr, local_sda, sec, irel, NULL, + FALSE, table, isymbuf, symtab_hdr)) + return; + range_l = sdata_range[1][0]; range_h = sdata_range[1][1]; switch (ELF32_R_TYPE (irel->r_info)) @@ -10572,7 +10823,8 @@ nds32_elf_relax_lo12 (struct bfd_link_info *link_info, bfd *abfd, /* There are range_h and range_l because linker has to promise all sections move cross one page together. */ if ((local_sda <= access_addr && (access_addr - local_sda) < range_h) - || (local_sda > access_addr && (local_sda - access_addr) <= range_l)) + || (local_sda > access_addr && (local_sda - access_addr) <= range_l) + || (h && strcmp (h->root.root.string, FP_BASE_NAME) == 0)) { if (N32_OP6 (insn) == N32_OP6_ORI && N32_RT5 (insn) == REG_GP) { @@ -10594,9 +10846,9 @@ nds32_elf_relax_lo12 (struct bfd_link_info *link_info, bfd *abfd, if (irelfn != irelend && reloc != R_NDS32_SDA17S2_RELA) irelfn->r_info = ELF32_R_INFO (ELF32_R_SYM (irelfn->r_info), R_NDS32_NONE); - } } + return; } @@ -10813,9 +11065,11 @@ nds32_relax_adjust_label (bfd *abfd, asection *sec, { /* 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++) + for (tmp2_rel = label_rel; tmp2_rel < tmp_rel; tmp2_rel++) { + if (tmp2_rel->r_offset == tmp_rel->r_offset) + break; + if (ELF32_R_TYPE (tmp2_rel->r_info) == R_NDS32_LABEL && tmp2_rel->r_addend < 2) tmp2_rel->r_info = @@ -10842,7 +11096,8 @@ nds32_relax_adjust_label (bfd *abfd, asection *sec, 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++) + for (irel = internal_relocs; + irel < irelend && irel->r_offset < sec->size; irel++) { if (ELF32_R_TYPE (irel->r_info) != R_NDS32_INSN16 && ELF32_R_TYPE (irel->r_info) != R_NDS32_LABEL) @@ -11027,9 +11282,10 @@ nds32_elf_relax_section (bfd *abfd, asection *sec, * no reloc entry. */ if (bfd_link_relocatable (link_info) || (sec->flags & SEC_RELOC) == 0 - || (sec->flags & SEC_EXCLUDE) != 0 + || (sec->flags & SEC_EXCLUDE) == 1 || (sec->flags & SEC_CODE) == 0 - || sec->size == 0) + || sec->size == 0 + || sec->reloc_count == 0) return TRUE; /* 09.12.11 Workaround. */ @@ -11042,6 +11298,12 @@ nds32_elf_relax_section (bfd *abfd, asection *sec, table = nds32_elf_hash_table (link_info); + /* Save the first section for abs symbol relaxation. + This is used for checking gp relaxation in the + nds32_elf_relax_loadstore and nds32_elf_relax_lo12. */ + nds32_elf_relax_guard (NULL, 0, sec, NULL, again, TRUE, + table, NULL, NULL); + /* The begining of general relaxation. */ if (is_SDA_BASE_set == 0) @@ -11233,16 +11495,16 @@ nds32_elf_relax_section (bfd *abfd, asection *sec, removed = nds32_elf_relax_loadstore (link_info, abfd, sec, irel, internal_relocs, &insn_len, contents, isymbuf, symtab_hdr, - load_store_relax); + load_store_relax, table); break; case R_NDS32_LO12S0_RELA: case R_NDS32_LO12S1_RELA: + case R_NDS32_LO12S2_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); + contents, isymbuf, symtab_hdr, table); /* It is impossible to delete blank, so just continue. */ continue; @@ -11268,7 +11530,6 @@ nds32_elf_relax_section (bfd *abfd, asection *sec, continue; default: continue; - } if (removed && seq_len - insn_len > 0) @@ -11417,6 +11678,7 @@ bfd_elf32_nds32_set_target_option (struct bfd_link_info *link_info, int relax_fp_as_gp, int eliminate_gc_relocs, FILE * sym_ld_script, + int hyper_relax, int load_store_relax) { struct elf_nds32_link_hash_table *table; @@ -11428,6 +11690,7 @@ bfd_elf32_nds32_set_target_option (struct bfd_link_info *link_info, table->relax_fp_as_gp = relax_fp_as_gp; table->eliminate_gc_relocs = eliminate_gc_relocs; table->sym_ld_script = sym_ld_script; + table->hyper_relax = hyper_relax; table ->load_store_relax = load_store_relax; } @@ -11617,7 +11880,7 @@ nds32_fag_find_base (struct nds32_fag *head, struct nds32_fag **bestpp) static bfd_boolean nds32_fag_mark_relax (struct bfd_link_info *link_info, - bfd *abfd, struct nds32_fag *best_fag, + asection *sec, struct nds32_fag *best_fag, Elf_Internal_Rela *internal_relocs, Elf_Internal_Rela *irelend) { @@ -11625,7 +11888,7 @@ nds32_fag_mark_relax (struct bfd_link_info *link_info, bfd_vma best_fpbase, gp; bfd *output_bfd; - output_bfd = abfd->sections->output_section->owner; + output_bfd = sec->output_section->owner; nds32_elf_final_sda_base (output_bfd, link_info, &gp, FALSE); best_fpbase = best_fag->addr; @@ -11781,7 +12044,7 @@ nds32_relax_fp_as_gp (struct bfd_link_info *link_info, /* Check if it is worth, and FP_BASE is near enough to SDA_BASE. */ if (accu < FAG_THRESHOLD - || !nds32_fag_mark_relax (link_info, abfd, best_fag, + || !nds32_fag_mark_relax (link_info, sec, best_fag, internal_relocs, irelend)) { /* Not worth to do fp-as-gp. */ @@ -11861,8 +12124,6 @@ nds32_fag_remove_unused_fpbase (bfd *abfd, asection *sec, NOTE: Disable fp-as-gp if we encounter ifcall relocations. * R_NDS32_17IFC_PCREL_RELA * R_NDS32_10IFCU_PCREL_RELA - - CASE?????????????? */ symtab_hdr = &elf_tdata (abfd)->symtab_hdr; @@ -12071,6 +12332,27 @@ error_return: free (reloc_vector); return NULL; } + +static bfd_boolean +nds32_elf_is_target_special_symbol (bfd *abfd ATTRIBUTE_UNUSED, asymbol *sym) +{ + if (!sym || !sym->name || sym->name[0] != '$') + return FALSE; + return TRUE; +} + +/* nds32 find maybe function sym. Ignore target special symbol + first, and then go the general function. */ + +static bfd_size_type +nds32_elf_maybe_function_sym (const asymbol *sym, asection *sec, + bfd_vma *code_off) +{ + if (nds32_elf_is_target_special_symbol (NULL, (asymbol *) sym)) + return 0; + + return _bfd_elf_maybe_function_sym (sym, sec, code_off); +} #define ELF_ARCH bfd_arch_nds32 @@ -12114,6 +12396,8 @@ error_return: #define elf_backend_special_sections nds32_elf_special_sections #define bfd_elf32_bfd_get_relocated_section_contents \ nds32_elf_get_relocated_section_contents +#define bfd_elf32_bfd_is_target_special_symbol nds32_elf_is_target_special_symbol +#define elf_backend_maybe_function_sym nds32_elf_maybe_function_sym #define elf_backend_can_gc_sections 1 #define elf_backend_can_refcount 1 diff --git a/bfd/elf32-nds32.h b/bfd/elf32-nds32.h index 6466816241..ecb5b0879c 100644 --- a/bfd/elf32-nds32.h +++ b/bfd/elf32-nds32.h @@ -90,7 +90,8 @@ extern void nds32_insertion_sort extern int nds32_convert_32_to_16 (bfd *, uint32_t, uint16_t *, int *); extern int nds32_convert_16_to_32 (bfd *, uint16_t, uint32_t *); extern void bfd_elf32_nds32_set_target_option (struct bfd_link_info *, - int, int, FILE *, int); + int, int, FILE *, + int, int); #define nds32_elf_hash_table(info) \ (elf_hash_table_id ((struct elf_link_hash_table *) ((info)->hash)) \ @@ -115,6 +116,7 @@ struct elf_nds32_link_hash_table int relax_fp_as_gp; /* --mrelax-omit-fp */ int eliminate_gc_relocs; /* --meliminate-gc-relocs */ FILE *sym_ld_script; /* --mgen-symbol-ld-script= */ + bfd_boolean hyper_relax; /* Relax for symbol not in RW sections. */ /* Disable if linking a dynamically linked executable. */ int load_store_relax; }; diff --git a/ld/ChangeLog b/ld/ChangeLog index ae6a8dc5e4..eabf1c0bb6 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,11 @@ +2018-09-17 Nelson Chu + + * emultempl/nds32elf.em (hyper_relax): New variable. + (nds32_elf_create_output_section_statements): + the parameters of bfd_elf32_nds32_set_target_option + (PARSE_AND_LIST_PROLOGUE, PARSE_AND_LIST_OPTIONS, + PARSE_AND_LIST_ARGS_CASES): Add new option --mhyper-relax. + 2018-09-17 Nelson Chu * emultempl/nds32elf.em (target_optimize, relax_status, relax_round diff --git a/ld/emultempl/nds32elf.em b/ld/emultempl/nds32elf.em index 255b04cbb2..be3e309019 100644 --- a/ld/emultempl/nds32elf.em +++ b/ld/emultempl/nds32elf.em @@ -30,6 +30,7 @@ fragment < */ +static int hyper_relax = 1; /* --mhyper-relax */ /* Disable if linking a dynamically linked executable. */ static int load_store_relax = 1; @@ -51,6 +52,7 @@ nds32_elf_create_output_section_statements (void) relax_fp_as_gp, eliminate_gc_relocs, sym_ld_script, + hyper_relax, load_store_relax); } @@ -152,11 +154,13 @@ PARSE_AND_LIST_PROLOGUE=' #define OPTION_REDUCE_FP_UPDATE (OPTION_BASELINE + 4) #define OPTION_NO_REDUCE_FP_UPDATE (OPTION_BASELINE + 5) #define OPTION_EXPORT_SYMBOLS (OPTION_BASELINE + 6) +#define OPTION_HYPER_RELAX (OPTION_BASELINE + 7) ' PARSE_AND_LIST_LONGOPTS=' { "mfp-as-gp", no_argument, NULL, OPTION_FP_AS_GP}, { "mno-fp-as-gp", no_argument, NULL, OPTION_NO_FP_AS_GP}, { "mexport-symbols", required_argument, NULL, OPTION_EXPORT_SYMBOLS}, + { "mhyper-relax", required_argument, NULL, OPTION_HYPER_RELAX}, /* These are deprecated options. Remove them in the future. */ { "mrelax-reduce-fp-update", no_argument, NULL, OPTION_REDUCE_FP_UPDATE}, { "mrelax-no-reduce-fp-update", no_argument, NULL, OPTION_NO_REDUCE_FP_UPDATE}, @@ -171,6 +175,8 @@ PARSE_AND_LIST_OPTIONS=' --m[no-]fp-as-gp Disable/enable fp-as-gp relaxation\n")); fprintf (file, _("\ --mexport-symbols=FILE Exporting symbols in linker script\n")); + fprintf (file, _("\ + --mhyper-relax=level Adjust relax level (low|medium|high). default: medium\n")); ' PARSE_AND_LIST_ARGS_CASES=' case OPTION_BASELINE: @@ -200,6 +206,20 @@ PARSE_AND_LIST_ARGS_CASES=' einfo (_("%F%P: cannot open map file %s: %E\n"), optarg); } break; + case OPTION_HYPER_RELAX: + if (!optarg) + einfo (_("%P: valid arguments to --mhyper-relax=(low|medium|high)\n")); + + if (strcmp (optarg, "low") == 0) + hyper_relax = 0; + else if (strcmp (optarg, "medium") == 0) + hyper_relax = 1; + else if (strcmp (optarg, "high") == 0) + hyper_relax = 2; + else + einfo (_("%P: valid arguments to --mhyper-relax=(low|medium|high)\n")); + + break; ' LDEMUL_AFTER_OPEN=nds32_elf_after_open LDEMUL_AFTER_PARSE=nds32_elf_after_parse -- 2.17.0