X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=bfd%2Felfnn-riscv.c;h=6be209e23989c62f85dcaaef11ae6ab05fba7b67;hb=595d3787e9cbedbceb6182f873a4774707c0e74f;hp=bb114e59c762e3550a7ff5403c775c14ed4cef1c;hpb=a9f5a5517fb7df640f0fcd4cb0f9961818b6505b;p=deliverable%2Fbinutils-gdb.git diff --git a/bfd/elfnn-riscv.c b/bfd/elfnn-riscv.c index bb114e59c7..6be209e239 100644 --- a/bfd/elfnn-riscv.c +++ b/bfd/elfnn-riscv.c @@ -310,13 +310,13 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) (bed->dynamic_sec_flags | SEC_READONLY)); if (s == NULL - || ! bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->srelgot = s; s = s_got = bfd_make_section_anyway_with_flags (abfd, ".got", flags); if (s == NULL - || !bfd_set_section_alignment (abfd, s, bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->sgot = s; @@ -327,8 +327,7 @@ riscv_elf_create_got_section (bfd *abfd, struct bfd_link_info *info) { s = bfd_make_section_anyway_with_flags (abfd, ".got.plt", flags); if (s == NULL - || !bfd_set_section_alignment (abfd, s, - bed->s->log_file_align)) + || !bfd_set_section_alignment (s, bed->s->log_file_align)) return FALSE; htab->sgotplt = s; @@ -373,9 +372,23 @@ riscv_elf_create_dynamic_sections (bfd *dynobj, if (!bfd_link_pic (info)) { + /* Technically, this section doesn't have contents. It is used as the + target of TLS copy relocs, to copy TLS data from shared libraries into + the executable. However, if we don't mark it as loadable, then it + matches the IS_TBSS test in ldlang.c, and there is no run-time address + space allocated for it even though it has SEC_ALLOC. That test is + correct for .tbss, but not correct for this section. There is also + a second problem that having a section with no contents can only work + if it comes after all sections with contents in the same segment, + but the linker script does not guarantee that. This is just mixed in + with other .tdata.* sections. We can fix both problems by lying and + saying that there are contents. This section is expected to be small + so this should not cause a significant extra program startup cost. */ htab->sdyntdata = bfd_make_section_anyway_with_flags (dynobj, ".tdata.dyn", (SEC_ALLOC | SEC_THREAD_LOCAL + | SEC_LOAD | SEC_DATA + | SEC_HAS_CONTENTS | SEC_LINKER_CREATED)); } @@ -1482,9 +1495,21 @@ perform_relocation (const reloc_howto_type *howto, break; case R_RISCV_RVC_LUI: - if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value))) + if (RISCV_CONST_HIGH_PART (value) == 0) + { + /* Linker relaxation can convert an address equal to or greater than + 0x800 to slightly below 0x800. C.LUI does not accept zero as a + valid immediate. We can fix this by converting it to a C.LI. */ + bfd_vma insn = bfd_get (howto->bitsize, input_bfd, + contents + rel->r_offset); + insn = (insn & ~MATCH_C_LUI) | MATCH_C_LI; + bfd_put (howto->bitsize, input_bfd, insn, contents + rel->r_offset); + value = ENCODE_RVC_IMM (0); + } + else if (!VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value))) return bfd_reloc_overflow; - value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)); + else + value = ENCODE_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (value)); break; case R_RISCV_32: @@ -1762,6 +1787,7 @@ riscv_elf_relocate_section (bfd *output_bfd, int r_type = ELFNN_R_TYPE (rel->r_info), tls_type; reloc_howto_type *howto = riscv_elf_rtype_to_howto (input_bfd, r_type); const char *msg = NULL; + char *msg_buf = NULL; bfd_boolean resolved_to_zero; if (howto == NULL @@ -1814,7 +1840,7 @@ riscv_elf_relocate_section (bfd *output_bfd, name = (bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name)); if (name == NULL || *name == '\0') - name = bfd_section_name (input_bfd, sec); + name = bfd_section_name (sec); } resolved_to_zero = (h != NULL @@ -2063,6 +2089,7 @@ riscv_elf_relocate_section (bfd *output_bfd, || (h != NULL && h->type == STT_SECTION)) && rel->r_addend) { + msg = _("%pcrel_lo section symbol with an addend"); r = bfd_reloc_dangerous; break; } @@ -2277,21 +2304,42 @@ riscv_elf_relocate_section (bfd *output_bfd, && _bfd_elf_section_offset (output_bfd, info, input_section, rel->r_offset) != (bfd_vma) -1) { - (*_bfd_error_handler) - (_("%pB(%pA+%#" PRIx64 "): " - "unresolvable %s relocation against symbol `%s'"), - input_bfd, - input_section, - (uint64_t) rel->r_offset, - howto->name, - h->root.root.string); - continue; + switch (r_type) + { + case R_RISCV_CALL: + case R_RISCV_JAL: + case R_RISCV_RVC_JUMP: + if (asprintf (&msg_buf, + _("%%X%%P: relocation %s against `%s' can " + "not be used when making a shared object; " + "recompile with -fPIC\n"), + howto->name, + h->root.root.string) == -1) + msg_buf = NULL; + break; + + default: + if (asprintf (&msg_buf, + _("%%X%%P: unresolvable %s relocation against " + "symbol `%s'\n"), + howto->name, + h->root.root.string) == -1) + msg_buf = NULL; + break; + } + + msg = msg_buf; + r = bfd_reloc_notsupported; } if (r == bfd_reloc_ok) r = perform_relocation (howto, rel, relocation, input_section, input_bfd, contents); + /* We should have already detected the error and set message before. + If the error message isn't set since the linker runs out of memory + or we don't set it before, then we should set the default message + with the "internal error" string here. */ switch (r) { case bfd_reloc_ok: @@ -2310,17 +2358,21 @@ riscv_elf_relocate_section (bfd *output_bfd, break; case bfd_reloc_outofrange: - msg = _("%X%P: internal error: out of range error\n"); + if (msg == NULL) + msg = _("%X%P: internal error: out of range error\n"); break; case bfd_reloc_notsupported: - msg = _("%X%P: internal error: unsupported relocation error\n"); + if (msg == NULL) + msg = _("%X%P: internal error: unsupported relocation error\n"); break; case bfd_reloc_dangerous: + /* The error message should already be set. */ + if (msg == NULL) + msg = _("dangerous relocation error"); info->callbacks->reloc_dangerous - (info, "%pcrel_lo section symbol with an addend", input_bfd, - input_section, rel->r_offset); + (info, msg, input_bfd, input_section, rel->r_offset); break; default: @@ -2328,9 +2380,14 @@ riscv_elf_relocate_section (bfd *output_bfd, break; } - if (msg) + /* Do not report error message for the dangerous relocation again. */ + if (msg && r != bfd_reloc_dangerous) info->callbacks->einfo (msg); + /* Free the unused `msg_buf` if needed. */ + if (msg_buf) + free (msg_buf); + /* We already reported the error via a callback, so don't try to report it again by returning false. That leads to spurious errors. */ ret = TRUE; @@ -3086,8 +3143,7 @@ static bfd_boolean _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) { bfd *obfd = info->output_bfd; - flagword new_flags = elf_elfheader (ibfd)->e_flags; - flagword old_flags = elf_elfheader (obfd)->e_flags; + flagword new_flags, old_flags; if (!is_riscv_elf (ibfd) || !is_riscv_elf (obfd)) return TRUE; @@ -3107,6 +3163,9 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) if (!riscv_merge_attributes (ibfd, info)) return FALSE; + new_flags = elf_elfheader (ibfd)->e_flags; + old_flags = elf_elfheader (obfd)->e_flags; + if (! elf_flags_init (obfd)) { elf_flags_init (obfd) = TRUE; @@ -3114,6 +3173,34 @@ _bfd_riscv_elf_merge_private_bfd_data (bfd *ibfd, struct bfd_link_info *info) return TRUE; } + /* Check to see if the input BFD actually contains any sections. If not, + its flags may not have been initialized either, but it cannot actually + cause any incompatibility. Do not short-circuit dynamic objects; their + section list may be emptied by elf_link_add_object_symbols. + + Also check to see if there are no code sections in the input. In this + case, there is no need to check for code specific flags. */ + if (!(ibfd->flags & DYNAMIC)) + { + bfd_boolean null_input_bfd = TRUE; + bfd_boolean only_data_sections = TRUE; + asection *sec; + + for (sec = ibfd->sections; sec != NULL; sec = sec->next) + { + if ((bfd_section_flags (sec) + & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) + == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) + only_data_sections = FALSE; + + null_input_bfd = FALSE; + break; + } + + if (null_input_bfd || only_data_sections) + return TRUE; + } + /* Disallow linking different float ABIs. */ if ((old_flags ^ new_flags) & EF_RISCV_FLOAT_ABI) { @@ -3261,6 +3348,7 @@ struct riscv_pcgp_hi_reloc bfd_vma hi_addr; unsigned hi_sym; asection *sym_sec; + bfd_boolean undefined_weak; riscv_pcgp_hi_reloc *next; }; @@ -3319,7 +3407,8 @@ riscv_free_pcgp_relocs (riscv_pcgp_relocs *p, static bfd_boolean riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off, bfd_vma hi_addend, bfd_vma hi_addr, - unsigned hi_sym, asection *sym_sec) + unsigned hi_sym, asection *sym_sec, + bfd_boolean undefined_weak) { riscv_pcgp_hi_reloc *new = bfd_malloc (sizeof(*new)); if (!new) @@ -3329,6 +3418,7 @@ riscv_record_pcgp_hi_reloc (riscv_pcgp_relocs *p, bfd_vma hi_sec_off, new->hi_addr = hi_addr; new->hi_sym = hi_sym; new->sym_sec = sym_sec; + new->undefined_weak = undefined_weak; new->next = p->hi; p->hi = new; return TRUE; @@ -3381,7 +3471,8 @@ typedef bfd_boolean (*relax_func_t) (bfd *, asection *, asection *, struct bfd_link_info *, Elf_Internal_Rela *, bfd_vma, bfd_vma, bfd_vma, bfd_boolean *, - riscv_pcgp_relocs *); + riscv_pcgp_relocs *, + bfd_boolean undefined_weak); /* Relax AUIPC + JALR into JAL. */ @@ -3393,7 +3484,8 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, bfd_vma max_alignment, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_signed_vma foff = symval - (sec_addr (sec) + rel->r_offset); @@ -3416,9 +3508,12 @@ _bfd_riscv_relax_call (bfd *abfd, asection *sec, asection *sym_sec, auipc = bfd_get_32 (abfd, contents + rel->r_offset); jalr = bfd_get_32 (abfd, contents + rel->r_offset + 4); rd = (jalr >> OP_SH_RD) & OP_MASK_RD; - rvc = rvc && VALID_RVC_J_IMM (foff) && ARCH_SIZE == 32; + rvc = rvc && VALID_RVC_J_IMM (foff); - if (rvc && (rd == 0 || rd == X_RA)) + /* C.J exists on RV32 and RV64, but C.JAL is RV32-only. */ + rvc = rvc && (rd == 0 || (rd == X_RA && ARCH_SIZE == 32)); + + if (rvc) { /* Relax to C.J[AL] rd, addr. */ r_type = R_RISCV_RVC_JUMP; @@ -3478,46 +3573,61 @@ _bfd_riscv_relax_lui (bfd *abfd, bfd_vma max_alignment, bfd_vma reserve_size, bfd_boolean *again, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_vma gp = riscv_global_pointer_value (link_info); int use_rvc = elf_elfheader (abfd)->e_flags & EF_RISCV_RVC; - /* Mergeable symbols and code might later move out of range. */ - if (sym_sec->flags & (SEC_MERGE | SEC_CODE)) - return TRUE; - BFD_ASSERT (rel->r_offset + 4 <= sec->size); if (gp) { - /* If gp and the symbol are in the same output section, then - consider only that section's alignment. */ + /* If gp and the symbol are in the same output section, which is not the + abs section, then consider only that output section's alignment. */ struct bfd_link_hash_entry *h = bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE); - if (h->u.def.section->output_section == sym_sec->output_section) + if (h->u.def.section->output_section == sym_sec->output_section + && sym_sec->output_section != bfd_abs_section_ptr) max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power; } /* Is the reference in range of x0 or gp? Valid gp range conservatively because of alignment issue. */ - if (VALID_ITYPE_IMM (symval) - || (symval >= gp - && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) - || (symval < gp - && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))) + if (undefined_weak + || (VALID_ITYPE_IMM (symval) + || (symval >= gp + && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) + || (symval < gp + && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))) { unsigned sym = ELFNN_R_SYM (rel->r_info); switch (ELFNN_R_TYPE (rel->r_info)) { case R_RISCV_LO12_I: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); + if (undefined_weak) + { + /* Change the RS1 to zero. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + } + else + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); return TRUE; case R_RISCV_LO12_S: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); + if (undefined_weak) + { + /* Change the RS1 to zero. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + } + else + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); return TRUE; case R_RISCV_HI20: @@ -3533,11 +3643,16 @@ _bfd_riscv_relax_lui (bfd *abfd, } /* Can we relax LUI to C.LUI? Alignment might move the section forward; - account for this assuming page alignment at worst. */ + account for this assuming page alignment at worst. In the presence of + RELRO segment the linker aligns it by one page size, therefore sections + after the segment can be moved more than one page. */ + if (use_rvc && ELFNN_R_TYPE (rel->r_info) == R_RISCV_HI20 && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval)) - && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval + ELF_MAXPAGESIZE))) + && VALID_RVC_LUI_IMM (RISCV_CONST_HIGH_PART (symval) + + (link_info->relro ? 2 * ELF_MAXPAGESIZE + : ELF_MAXPAGESIZE))) { /* Replace LUI with C.LUI if legal (i.e., rd != x0 and rd != x2/sp). */ bfd_vma lui = bfd_get_32 (abfd, contents + rel->r_offset); @@ -3571,7 +3686,8 @@ _bfd_riscv_relax_tls_le (bfd *abfd, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again, - riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *prcel_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { /* See if this symbol is in range of tp. */ if (RISCV_CONST_HIGH_PART (tpoff (link_info, symval)) != 0) @@ -3611,7 +3727,8 @@ _bfd_riscv_relax_align (bfd *abfd, asection *sec, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *pcrel_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_vma alignment = 1, pos; @@ -3669,8 +3786,10 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, bfd_vma max_alignment, bfd_vma reserve_size, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcgp_relocs) + riscv_pcgp_relocs *pcgp_relocs, + bfd_boolean undefined_weak) { + bfd_byte *contents = elf_section_data (sec)->this_hdr.contents; bfd_vma gp = riscv_global_pointer_value (link_info); BFD_ASSERT (rel->r_offset + 4 <= sec->size); @@ -3700,12 +3819,19 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, hi_reloc = *hi; symval = hi_reloc.hi_addr; sym_sec = hi_reloc.sym_sec; + + /* We can not know whether the undefined weak symbol is referenced + according to the information of R_RISCV_PCREL_LO12_I/S. Therefore, + we have to record the 'undefined_weak' flag when handling the + corresponding R_RISCV_HI20 reloc in riscv_record_pcgp_hi_reloc. */ + undefined_weak = hi_reloc.undefined_weak; } break; case R_RISCV_PCREL_HI20: /* Mergeable symbols and code might later move out of range. */ - if (sym_sec->flags & (SEC_MERGE | SEC_CODE)) + if (! undefined_weak + && sym_sec->flags & (SEC_MERGE | SEC_CODE)) return TRUE; /* If the cooresponding lo relocation has already been seen then it's not @@ -3721,33 +3847,62 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, if (gp) { - /* If gp and the symbol are in the same output section, then - consider only that section's alignment. */ + /* If gp and the symbol are in the same output section, which is not the + abs section, then consider only that output section's alignment. */ struct bfd_link_hash_entry *h = - bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, TRUE); - if (h->u.def.section->output_section == sym_sec->output_section) + bfd_link_hash_lookup (link_info->hash, RISCV_GP_SYMBOL, FALSE, FALSE, + TRUE); + if (h->u.def.section->output_section == sym_sec->output_section + && sym_sec->output_section != bfd_abs_section_ptr) max_alignment = (bfd_vma) 1 << sym_sec->output_section->alignment_power; } /* Is the reference in range of x0 or gp? Valid gp range conservatively because of alignment issue. */ - if (VALID_ITYPE_IMM (symval) - || (symval >= gp - && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) - || (symval < gp - && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size))) + if (undefined_weak + || (VALID_ITYPE_IMM (symval) + || (symval >= gp + && VALID_ITYPE_IMM (symval - gp + max_alignment + reserve_size)) + || (symval < gp + && VALID_ITYPE_IMM (symval - gp - max_alignment - reserve_size)))) { unsigned sym = hi_reloc.hi_sym; switch (ELFNN_R_TYPE (rel->r_info)) { case R_RISCV_PCREL_LO12_I: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); - rel->r_addend += hi_reloc.hi_addend; + if (undefined_weak) + { + /* Change the RS1 to zero, and then modify the relocation + type to R_RISCV_LO12_I. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_I); + rel->r_addend = hi_reloc.hi_addend; + } + else + { + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_I); + rel->r_addend += hi_reloc.hi_addend; + } return TRUE; case R_RISCV_PCREL_LO12_S: - rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); - rel->r_addend += hi_reloc.hi_addend; + if (undefined_weak) + { + /* Change the RS1 to zero, and then modify the relocation + type to R_RISCV_LO12_S. */ + bfd_vma insn = bfd_get_32 (abfd, contents + rel->r_offset); + insn &= ~(OP_MASK_RS1 << OP_SH_RS1); + bfd_put_32 (abfd, insn, contents + rel->r_offset); + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_LO12_S); + rel->r_addend = hi_reloc.hi_addend; + } + else + { + rel->r_info = ELFNN_R_INFO (sym, R_RISCV_GPREL_S); + rel->r_addend += hi_reloc.hi_addend; + } return TRUE; case R_RISCV_PCREL_HI20: @@ -3756,7 +3911,8 @@ _bfd_riscv_relax_pc (bfd *abfd ATTRIBUTE_UNUSED, rel->r_addend, symval, ELFNN_R_SYM(rel->r_info), - sym_sec); + sym_sec, + undefined_weak); /* We can delete the unnecessary AUIPC and reloc. */ rel->r_info = ELFNN_R_INFO (0, R_RISCV_DELETE); rel->r_addend = 4; @@ -3782,7 +3938,8 @@ _bfd_riscv_relax_delete (bfd *abfd, bfd_vma max_alignment ATTRIBUTE_UNUSED, bfd_vma reserve_size ATTRIBUTE_UNUSED, bfd_boolean *again ATTRIBUTE_UNUSED, - riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED) + riscv_pcgp_relocs *pcgp_relocs ATTRIBUTE_UNUSED, + bfd_boolean undefined_weak ATTRIBUTE_UNUSED) { if (!riscv_relax_delete_bytes(abfd, sec, rel->r_offset, rel->r_addend, link_info)) @@ -3848,6 +4005,8 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, relax_func_t relax_func; int type = ELFNN_R_TYPE (rel->r_info); bfd_vma symval; + char symtype; + bfd_boolean undefined_weak = FALSE; relax_func = NULL; if (info->relax_pass == 0) @@ -3913,7 +4072,7 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, ? 0 : isym->st_size - rel->r_addend; if (isym->st_shndx == SHN_UNDEF) - sym_sec = sec, symval = sec_addr (sec) + rel->r_offset; + sym_sec = sec, symval = rel->r_offset; else { BFD_ASSERT (isym->st_shndx < elf_numsections (abfd)); @@ -3926,8 +4085,9 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, if (sec_addr (sym_sec) == 0) continue; #endif - symval = sec_addr (sym_sec) + isym->st_value; + symval = isym->st_value; } + symtype = ELF_ST_TYPE (isym->st_info); } else { @@ -3941,26 +4101,89 @@ _bfd_riscv_relax_section (bfd *abfd, asection *sec, || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; + if (h->root.type == bfd_link_hash_undefweak + && (relax_func == _bfd_riscv_relax_lui + || relax_func == _bfd_riscv_relax_pc)) + { + /* For the lui and auipc relaxations, since the symbol + value of an undefined weak symbol is always be zero, + we can optimize the patterns into a single LI/MV/ADDI + instruction. + + Note that, creating shared libraries and pie output may + break the rule above. Fortunately, since we do not relax + pc relocs when creating shared libraries and pie output, + and the absolute address access for R_RISCV_HI20 isn't + allowed when "-fPIC" is set, the problem of creating shared + libraries can not happen currently. Once we support the + auipc relaxations when creating shared libraries, then we will + need the more rigorous checking for this optimization. */ + undefined_weak = TRUE; + } + if (h->plt.offset != MINUS_ONE) - symval = sec_addr (htab->elf.splt) + h->plt.offset; + { + sym_sec = htab->elf.splt; + symval = h->plt.offset; + } + else if (undefined_weak) + { + symval = 0; + sym_sec = bfd_und_section_ptr; + } else if (h->root.u.def.section->output_section == NULL || (h->root.type != bfd_link_hash_defined && h->root.type != bfd_link_hash_defweak)) continue; else - symval = sec_addr (h->root.u.def.section) + h->root.u.def.value; + { + symval = h->root.u.def.value; + sym_sec = h->root.u.def.section; + } if (h->type != STT_FUNC) reserve_size = (h->size - rel->r_addend) > h->size ? 0 : h->size - rel->r_addend; - sym_sec = h->root.u.def.section; + symtype = h->type; } - symval += rel->r_addend; + if (sym_sec->sec_info_type == SEC_INFO_TYPE_MERGE + && (sym_sec->flags & SEC_MERGE)) + { + /* At this stage in linking, no SEC_MERGE symbol has been + adjusted, so all references to such symbols need to be + passed through _bfd_merged_section_offset. (Later, in + relocate_section, all SEC_MERGE symbols *except* for + section symbols have been adjusted.) + + gas may reduce relocations against symbols in SEC_MERGE + sections to a relocation against the section symbol when + the original addend was zero. When the reloc is against + a section symbol we should include the addend in the + offset passed to _bfd_merged_section_offset, since the + location of interest is the original symbol. On the + other hand, an access to "sym+addend" where "sym" is not + a section symbol should not include the addend; Such an + access is presumed to be an offset from "sym"; The + location of interest is just "sym". */ + if (symtype == STT_SECTION) + symval += rel->r_addend; + + symval = _bfd_merged_section_offset (abfd, &sym_sec, + elf_section_data (sym_sec)->sec_info, + symval); + + if (symtype != STT_SECTION) + symval += rel->r_addend; + } + else + symval += rel->r_addend; + + symval += sec_addr (sym_sec); if (!relax_func (abfd, sec, sym_sec, info, rel, symval, max_alignment, reserve_size, again, - &pcgp_relocs)) + &pcgp_relocs, undefined_weak)) goto fail; } @@ -3975,7 +4198,7 @@ fail: } #if ARCH_SIZE == 32 -# define PRSTATUS_SIZE 0 /* FIXME */ +# define PRSTATUS_SIZE 204 # define PRSTATUS_OFFSET_PR_CURSIG 12 # define PRSTATUS_OFFSET_PR_PID 24 # define PRSTATUS_OFFSET_PR_REG 72