diff options
Diffstat (limited to 'ld/ld_reloc.c')
-rw-r--r-- | ld/ld_reloc.c | 875 |
1 files changed, 875 insertions, 0 deletions
diff --git a/ld/ld_reloc.c b/ld/ld_reloc.c new file mode 100644 index 000000000000..0ca6ec8e4cea --- /dev/null +++ b/ld/ld_reloc.c @@ -0,0 +1,875 @@ +/*- + * Copyright (c) 2012,2013 Kai Wang + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "ld.h" +#include "ld_arch.h" +#include "ld_ehframe.h" +#include "ld_input.h" +#include "ld_output.h" +#include "ld_reloc.h" +#include "ld_script.h" +#include "ld_symbols.h" +#include "ld_utils.h" + +ELFTC_VCSID("$Id: ld_reloc.c 2962 2013-08-25 16:34:57Z kaiwang27 $"); + +static struct ld *_ld; + +/* + * Support routines for relocation handling. + */ + +static int _discard_reloc(struct ld *ld, struct ld_input_section *is, + uint64_t sym, uint64_t off, uint64_t *reloc_adjust); +static void _scan_reloc(struct ld *ld, struct ld_input_section *is, + uint64_t sym, struct ld_reloc_entry *lre); +static void _read_rel(struct ld *ld, struct ld_input_section *is, + Elf_Data *d); +static void _read_rela(struct ld *ld, struct ld_input_section *is, + Elf_Data *d); +static void _add_to_gc_search_list(struct ld_state *ls, + struct ld_input_section *is); +static uint64_t _reloc_addr(struct ld_reloc_entry *lre); +static int _cmp_reloc(struct ld_reloc_entry *a, struct ld_reloc_entry *b); + +void +ld_reloc_load(struct ld *ld) +{ + struct ld_input *li; + struct ld_input_section *is; + Elf *e; + Elf_Scn *scn; + Elf_Data *d; + int elferr, i; + + ld_input_link_objects(ld); + + STAILQ_FOREACH(li, &ld->ld_lilist, li_next) { + + if (li->li_name == NULL || li->li_type == LIT_DSO) + continue; + + ld_input_load(ld, li); + e = li->li_elf; + + for (i = 0; (uint64_t) i < li->li_shnum - 1; i++) { + is = &li->li_is[i]; + + if (is->is_type != SHT_REL && is->is_type != SHT_RELA) + continue; + + if ((scn = elf_getscn(e, is->is_index)) == NULL) { + ld_warn(ld, "%s(%s): elf_getscn failed: %s", + li->li_name, is->is_name, elf_errmsg(-1)); + continue; + } + + (void) elf_errno(); + if ((d = elf_getdata(scn, NULL)) == NULL) { + elferr = elf_errno(); + if (elferr != 0) + ld_warn(ld, "%s(%s): elf_getdata " + "failed: %s", li->li_name, + is->is_name, elf_errmsg(elferr)); + continue; + } + + /* + * Find out the section to which this relocation + * section applies. + */ + if (is->is_info < li->li_shnum) { + is->is_tis = &li->li_is[is->is_info]; + li->li_is[is->is_info].is_ris = is; + } else { + ld_warn(ld, "%s(%s): invalid relocation" + " section", li->li_name, is->is_name); + continue; + } + + /* + * Load and process relocation entries. + */ + if ((is->is_reloc = malloc(sizeof(*is->is_reloc))) == + NULL) + ld_fatal(ld, "malloc"); + STAILQ_INIT(is->is_reloc); + + if (is->is_type == SHT_REL) + _read_rel(ld, is, d); + else + _read_rela(ld, is, d); + + if (!strcmp(is->is_tis->is_name, ".eh_frame")) + ld_ehframe_adjust(ld, is->is_tis); + } + + ld_input_unload(ld, li); + } +} + +void +ld_reloc_deferred_scan(struct ld *ld) +{ + struct ld_input *li; + struct ld_input_section *is; + struct ld_reloc_entry *lre; + int i; + + if (ld->ld_reloc) + return; + + STAILQ_FOREACH(li, &ld->ld_lilist, li_next) { + + if (li->li_name == NULL || li->li_type == LIT_DSO) + continue; + + for (i = 0; (uint64_t) i < li->li_shnum - 1; i++) { + is = &li->li_is[i]; + + if (is->is_type != SHT_REL && is->is_type != SHT_RELA) + continue; + + if (is->is_reloc == NULL) + continue; + + STAILQ_FOREACH(lre, is->is_reloc, lre_next) { + ld->ld_arch->scan_reloc(ld, is->is_tis, lre); + } + } + } +} + +static int +_discard_reloc(struct ld *ld, struct ld_input_section *is, uint64_t sym, + uint64_t off, uint64_t *reloc_adjust) +{ + struct ld_output *lo; + uint8_t *p; + uint64_t length; + uint32_t cie_id; + + lo = ld->ld_output; + assert(lo != NULL); + + /* + * Relocation entry should be discarded if the symbol it refers + * to was discarded. + */ + if (is->is_input->li_symindex[sym] != NULL) + return (0); + + if (strcmp(is->is_tis->is_name, ".eh_frame")) + goto discard_reloc; + + /* + * If we discard a relocation entry for a FDE in the .eh_frame + * section, we need also to remove the FDE entry and adjust the + * relocation offset of the following relocation entries for + * the .eh_frame section. + */ + + assert(is->is_tis->is_ehframe != NULL); + p = is->is_tis->is_ehframe; + p += off - 8; /* XXX extended length unsupported */ + + /* Read CIE/FDE length field. */ + READ_32(p, length); + p += 4; + + /* Check for terminator. (Shouldn't happen) */ + if (length == 0) + goto discard_reloc; + + /* Read CIE ID/Pointer field. */ + READ_32(p, cie_id); + if (cie_id == 0) + goto discard_reloc; /* Shouldn't happen */ + + /* Set CIE ID to 0xFFFFFFFF to mark this FDE to be discarded */ + WRITE_32(p, 0xFFFFFFFF); + + /* Update relocation offset adjustment. */ + *reloc_adjust += length + 4; + + /* Reduce the size of the .eh_frame section. */ + is->is_tis->is_shrink += length + 4; + +discard_reloc: + + /* Reduce the size of the relocation section accordingly */ + is->is_size -= ld->ld_arch->reloc_entsize; + + return (1); +} + +static void +_read_rel(struct ld *ld, struct ld_input_section *is, Elf_Data *d) +{ + struct ld_reloc_entry *lre; + GElf_Rel r; + uint64_t reloc_adjust, sym; + int i, len; + + assert(is->is_reloc != NULL); + + reloc_adjust = 0; + len = d->d_size / is->is_entsize; + for (i = 0; i < len; i++) { + if (gelf_getrel(d, i, &r) != &r) { + ld_warn(ld, "gelf_getrel failed: %s", elf_errmsg(-1)); + continue; + } + sym = GELF_R_SYM(r.r_info); + if (_discard_reloc(ld, is, sym, r.r_offset, &reloc_adjust)) + continue; + if ((lre = calloc(1, sizeof(*lre))) == NULL) + ld_fatal(ld, "calloc"); + assert(r.r_offset >= reloc_adjust); + lre->lre_offset = r.r_offset - reloc_adjust; + lre->lre_type = GELF_R_TYPE(r.r_info); + lre->lre_tis = is->is_tis; + _scan_reloc(ld, is, sym, lre); + STAILQ_INSERT_TAIL(is->is_reloc, lre, lre_next); + is->is_num_reloc++; + } + is->is_tis->is_shrink = reloc_adjust; +} + +static void +_read_rela(struct ld *ld, struct ld_input_section *is, Elf_Data *d) +{ + struct ld_reloc_entry *lre; + GElf_Rela r; + uint64_t reloc_adjust, sym; + int i, len; + + assert(is->is_reloc != NULL); + + reloc_adjust = 0; + len = d->d_size / is->is_entsize; + for (i = 0; i < len; i++) { + if (gelf_getrela(d, i, &r) != &r) { + ld_warn(ld, "gelf_getrel failed: %s", elf_errmsg(-1)); + continue; + } + sym = GELF_R_SYM(r.r_info); + if (_discard_reloc(ld, is, sym, r.r_offset, &reloc_adjust)) + continue; + if ((lre = calloc(1, sizeof(*lre))) == NULL) + ld_fatal(ld, "calloc"); + assert(r.r_offset >= reloc_adjust); + lre->lre_offset = r.r_offset - reloc_adjust; + lre->lre_type = GELF_R_TYPE(r.r_info); + lre->lre_addend = r.r_addend; + lre->lre_tis = is->is_tis; + _scan_reloc(ld, is, sym, lre); + STAILQ_INSERT_TAIL(is->is_reloc, lre, lre_next); + is->is_num_reloc++; + } + is->is_tis->is_shrink = reloc_adjust; +} + +static void +_scan_reloc(struct ld *ld, struct ld_input_section *is, uint64_t sym, + struct ld_reloc_entry *lre) +{ + struct ld_input *li; + + (void) ld; + + li = is->is_input; + + lre->lre_sym = li->li_symindex[sym]; + + if (!ld->ld_reloc && !ld->ld_gc) + ld->ld_arch->scan_reloc(ld, is->is_tis, lre); +} + +static void +_add_to_gc_search_list(struct ld_state *ls, struct ld_input_section *is) +{ + + assert(is != NULL); + + /* Only add allocated sections. */ + if ((is->is_flags & SHF_ALLOC) == 0) + return; + + /* + * Do not add sections that are already exist in the search list, + * or sections that don't have assoicated relocations. + */ + if (is->is_refed || is->is_ris == NULL || is->is_ris->is_reloc == NULL) + return; + + STAILQ_INSERT_TAIL(ls->ls_gc, is, is_gc_next); +} + +void +ld_reloc_gc_sections(struct ld *ld) +{ + struct ld_state *ls; + struct ld_symbol *lsb; + struct ld_input_section *is; + struct ld_reloc_entry *lre; + char *entry; + + /* + * Initialise search list. Initial search list consists of sections + * contains the entry and extern symbols. + */ + ls = &ld->ld_state; + if ((ls->ls_gc = calloc(1, sizeof(*ls->ls_gc))) == NULL) + ld_fatal_std(ld, "calloc"); + STAILQ_INIT(ls->ls_gc); + + /* + * Add the section that contains the entry symbol to the initial + * search list. + */ + entry = ld->ld_entry != NULL ? ld->ld_entry : + ld->ld_scp->lds_entry_point; + if (entry != NULL) { + HASH_FIND_STR(ld->ld_sym, entry, lsb); + if (lsb != NULL && lsb->lsb_is != NULL) + _add_to_gc_search_list(ls, lsb->lsb_is); + } + + /* + * Add sections that contain the symbols specified by command line + * option `-u' (extern symbols) to the initial search list. + */ + if (ld->ld_ext_symbols != NULL) { + STAILQ_FOREACH(lsb, ld->ld_ext_symbols, lsb_next) { + if (lsb->lsb_is != NULL) + _add_to_gc_search_list(ls, lsb->lsb_is); + } + } + + /* + * Breadth-first search for sections referenced by relocations + * assoicated with the initial sections. The search is recusive, + * the relocations assoicated with the found sections are again + * used to search for more referenced sections. + */ + STAILQ_FOREACH(is, ls->ls_gc, is_gc_next) { + assert(is->is_ris != NULL); + STAILQ_FOREACH(lre, is->is_ris->is_reloc, lre_next) { + if (lre->lre_sym == NULL) + continue; + lsb = ld_symbols_ref(lre->lre_sym); + if (lsb->lsb_is != NULL) + _add_to_gc_search_list(ls, lsb->lsb_is); + } + } +} + +void * +ld_reloc_serialize(struct ld *ld, struct ld_output_section *os, size_t *sz) +{ + struct ld_reloc_entry *lre; + struct ld_symbol *lsb; + Elf32_Rel *r32; + Elf64_Rel *r64; + Elf32_Rela *ra32; + Elf64_Rela *ra64; + uint8_t *p; + void *b; + size_t entsize; + uint64_t sym; + unsigned char is_64; + unsigned char is_rela; + + is_64 = ld->ld_arch->reloc_is_64bit; + is_rela = ld->ld_arch->reloc_is_rela; + entsize = ld->ld_arch->reloc_entsize; + + b = malloc(ld->ld_arch->reloc_entsize * os->os_num_reloc); + if (b == NULL) + ld_fatal_std(ld, "malloc"); + + p = b; + STAILQ_FOREACH(lre, os->os_reloc, lre_next) { + if (lre->lre_sym != NULL) { + lsb = ld_symbols_ref(lre->lre_sym); + if (os->os_dynrel) + sym = lsb->lsb_dyn_index; + else + sym = lsb->lsb_out_index; + } else + sym = 0; + + if (is_64 && is_rela) { + ra64 = (Elf64_Rela *) (uintptr_t) p; + ra64->r_offset = lre->lre_offset; + ra64->r_info = ELF64_R_INFO(sym, lre->lre_type); + ra64->r_addend = lre->lre_addend; + } else if (!is_64 && !is_rela) { + r32 = (Elf32_Rel *) (uintptr_t) p; + r32->r_offset = (uint32_t) lre->lre_offset; + r32->r_info = (uint32_t) ELF32_R_INFO(sym, + lre->lre_type); + } else if (!is_64 && is_rela) { + ra32 = (Elf32_Rela *) (uintptr_t) p; + ra32->r_offset = (uint32_t) lre->lre_offset; + ra32->r_info = (uint32_t) ELF32_R_INFO(sym, + lre->lre_type); + ra32->r_addend = (int32_t) lre->lre_addend; + } else if (is_64 && !is_rela) { + r64 = (Elf64_Rel *) (uintptr_t) p; + r64->r_offset = lre->lre_offset; + r64->r_info = ELF64_R_INFO(sym, lre->lre_type); + } + + p += entsize; + } + + *sz = entsize * os->os_num_reloc; + assert((size_t) (p - (uint8_t *) b) == *sz); + + return (b); +} + +void +ld_reloc_create_entry(struct ld *ld, const char *name, + struct ld_input_section *tis, uint64_t type, struct ld_symbol *lsb, + uint64_t offset, int64_t addend) +{ + struct ld_input_section *is; + struct ld_reloc_entry *lre; + int len; + + /* + * List of internal sections to hold dynamic relocations: + * + * .rel.bss contains copy relocations + * .rel.plt contains PLT (*_JMP_SLOT) relocations + * .rel.got contains GOT (*_GLOB_DATA) relocations + * .rel.data.* contains *_RELATIVE and absolute relocations + */ + + is = ld_input_find_internal_section(ld, name); + if (is == NULL) { + is = ld_input_add_internal_section(ld, name); + is->is_dynrel = 1; + is->is_type = ld->ld_arch->reloc_is_rela ? SHT_RELA : SHT_REL; + is->is_align = ld->ld_arch->reloc_is_64bit ? 8 : 4; + is->is_entsize = ld->ld_arch->reloc_entsize; + + len = strlen(name); + if (len > 3 && name[len - 1] == 't' && name[len - 2] == 'l' && + name[len - 3] == 'p') + is->is_pltrel = 1; + } + + if (is->is_reloc == NULL) { + is->is_reloc = calloc(1, sizeof(*is->is_reloc)); + if (is->is_reloc == NULL) + ld_fatal_std(ld, "calloc"); + STAILQ_INIT(is->is_reloc); + } + + if ((lre = malloc(sizeof(*lre))) == NULL) + ld_fatal_std(ld, "calloc"); + + lre->lre_tis = tis; + lre->lre_type = type; + lre->lre_sym = lsb; + lre->lre_offset = offset; + lre->lre_addend = addend; + + STAILQ_INSERT_TAIL(is->is_reloc, lre, lre_next); + is->is_num_reloc++; + is->is_size += ld->ld_arch->reloc_entsize; + + /* Keep track of the total number of *_RELATIVE relocations. */ + if (ld->ld_arch->is_relative_reloc(type)) + ld->ld_state.ls_relative_reloc++; +} + +void +ld_reloc_finalize_dynamic(struct ld *ld, struct ld_output *lo, + struct ld_output_section *os) +{ + struct ld_input_section *is; + struct ld_output_section *_os; + struct ld_reloc_entry *lre; + + if (!os->os_dynrel || os->os_reloc == NULL) + return; + + /* PLT relocation is handled in arch-specified code. */ + if (os->os_pltrel) + return; + + /* + * Set the lo->lo_rel_dyn here so that the DT_* entries needed for + * dynamic relocation will be generated. + * + * Note that besides the PLT relocation section, we can only have one + * dynamic relocation section in the output object. + */ + if (lo->lo_rel_dyn == NULL) + lo->lo_rel_dyn = os; + + STAILQ_FOREACH(lre, os->os_reloc, lre_next) { + /* + * Found out the corresponding output section for the input + * section which the relocation applies to. + */ + is = lre->lre_tis; + assert(is != NULL); + if ((_os = is->is_output) == NULL) + continue; + + /* + * Update the relocation offset to make it point to the + * correct place in the output section. + */ + lre->lre_offset += _os->os_addr + is->is_reloff; + + /* + * Perform arch-specific dynamic relocation + * finalization. + */ + ld->ld_arch->finalize_reloc(ld, is, lre); + } +} + +void +ld_reloc_join(struct ld *ld, struct ld_output_section *os, + struct ld_input_section *is) +{ + + assert(is->is_reloc != NULL); + + if (os->os_reloc == NULL) { + if ((os->os_reloc = malloc(sizeof(*os->os_reloc))) == NULL) + ld_fatal_std(ld, "malloc"); + STAILQ_INIT(os->os_reloc); + } + + STAILQ_CONCAT(os->os_reloc, is->is_reloc); + os->os_num_reloc += is->is_num_reloc; + + is->is_num_reloc = 0; + free(is->is_reloc); + is->is_reloc = NULL; +} + +static uint64_t +_reloc_addr(struct ld_reloc_entry *lre) +{ + + return (lre->lre_tis->is_output->os_addr + lre->lre_tis->is_reloff + + lre->lre_offset); +} + +static int +_cmp_reloc(struct ld_reloc_entry *a, struct ld_reloc_entry *b) +{ + struct ld *ld; + + ld = _ld; + + /* + * Sort dynamic relocation entries to make the runtime linker + * run faster. *_RELATIVE relocations should be sorted to the + * front. Between two *_RELATIVE relocations, the one with + * lower address should appear first. For other relocations + * we sort them by assoicated dynamic symbol index, then + * by relocation type. + */ + + if (ld->ld_arch->is_relative_reloc(a->lre_type) && + !ld->ld_arch->is_relative_reloc(b->lre_type)) + return (-1); + + if (!ld->ld_arch->is_relative_reloc(a->lre_type) && + ld->ld_arch->is_relative_reloc(b->lre_type)) + return (1); + + if (ld->ld_arch->is_relative_reloc(a->lre_type) && + ld->ld_arch->is_relative_reloc(b->lre_type)) { + if (_reloc_addr(a) < _reloc_addr(b)) + return (-1); + else if (_reloc_addr(a) > _reloc_addr(b)) + return (1); + else + return (0); + } + + if (a->lre_sym->lsb_dyn_index < b->lre_sym->lsb_dyn_index) + return (-1); + else if (a->lre_sym->lsb_dyn_index > b->lre_sym->lsb_dyn_index) + return (1); + + if (a->lre_type < b->lre_type) + return (-1); + else if (a->lre_type > b->lre_type) + return (1); + + return (0); +} + +void +ld_reloc_sort(struct ld *ld, struct ld_output_section *os) +{ + + _ld = ld; + + if (os->os_reloc == NULL) + return; + + STAILQ_SORT(os->os_reloc, ld_reloc_entry, lre_next, _cmp_reloc); +} + +int +ld_reloc_require_plt(struct ld *ld, struct ld_reloc_entry *lre) +{ + struct ld_symbol *lsb; + + lsb = ld_symbols_ref(lre->lre_sym); + + /* Only need PLT for functions. */ + if (lsb->lsb_type != STT_FUNC) + return (0); + + /* Create PLT for functions in DSOs. */ + if (ld_symbols_in_dso(lsb)) + return (1); + + /* + * If the linker outputs a DSO, PLT entry is needed if the symbol + * if undefined or it can be overridden. + */ + if (ld->ld_dso && + (lsb->lsb_shndx == SHN_UNDEF || ld_symbols_overridden(ld, lsb))) + return (1); + + /* Otherwise, we do not create PLT entry. */ + return (0); +} + +int +ld_reloc_require_copy_reloc(struct ld *ld, struct ld_reloc_entry *lre) +{ + struct ld_symbol *lsb; + + lsb = ld_symbols_ref(lre->lre_sym); + + /* Functions do not need copy reloc. */ + if (lsb->lsb_type == STT_FUNC) + return (0); + + /* + * If we are generating a normal executable and the symbol is + * defined in a DSO, we need a copy reloc. + */ + if (ld->ld_exec && ld_symbols_in_dso(lsb)) + return (1); + + return (0); +} + +int +ld_reloc_require_glob_dat(struct ld *ld, struct ld_reloc_entry *lre) +{ + struct ld_symbol *lsb; + + lsb = ld_symbols_ref(lre->lre_sym); + + /* + * If the symbol is undefined or if it's defined in a DSO, + * GLOB_DAT relocation is required. + */ + if (lsb->lsb_shndx == SHN_UNDEF || ld_symbols_in_dso(lsb)) + return (1); + + /* + * If the linker creates a DSO and the symbol can be overridden + * GLOB_DAT relocation is required. + */ + if (ld->ld_dso && ld_symbols_overridden(ld, lsb)) + return (1); + + /* + * If the linker creates a DSO and the symbol visibility is + * STV_PROTECTED, GLOB_DAT relocation is required for function + * address comparsion to work. + */ + if (ld->ld_dso && lsb->lsb_other == STV_PROTECTED) + return (1); + + /* + * Otherwise GLOB_DAT relocation is not required, RELATIVE + * relocation can be used instead. + */ + return (0); +} + +int +ld_reloc_require_dynamic_reloc(struct ld *ld, struct ld_reloc_entry *lre) +{ + struct ld_symbol *lsb; + + lsb = ld_symbols_ref(lre->lre_sym); + + /* + * If the symbol is defined in a DSO, we create specific dynamic + * relocations when we create PLT, GOT or copy reloc. + */ + if (ld_symbols_in_dso(lsb)) + return (0); + + /* + * When we are creating a DSO, we create dynamic relocation if + * the symbol is undefined, or if the symbol can be overridden. + */ + if (ld->ld_dso && (lsb->lsb_shndx == SHN_UNDEF || + ld_symbols_overridden(ld, lsb))) + return (1); + + /* + * When we are creating a PIE/DSO (position-independent), if the + * relocation is referencing the absolute address of a symbol, + * we should create dynamic relocation. + */ + if ((ld->ld_pie || ld->ld_dso) && + ld->ld_arch->is_absolute_reloc(lre->lre_type)) + return (1); + + /* Otherwise we do not generate dynamic relocation. */ + return (0); +} + +int +ld_reloc_relative_relax(struct ld *ld, struct ld_reloc_entry *lre) +{ + + struct ld_symbol *lsb; + + lsb = ld_symbols_ref(lre->lre_sym); + + /* + * We only use *_RELATIVE relocation when we create PIE/DSO. + */ + if (!ld->ld_pie && !ld->ld_dso) + return (0); + + /* + * If the symbol is defined in a DSO, we can not relax the + * relocation. + */ + if (ld_symbols_in_dso(lsb)) + return (0); + + /* + * When we are creating a DSO, we can not relax dynamic relocation + * to *_RELATIVE relocation if the symbol is undefined, or if the + * symbol can be overridden. + */ + if (ld->ld_dso && (lsb->lsb_shndx == SHN_UNDEF || + ld_symbols_overridden(ld, lsb))) + return (0); + + /* Otherwise it's ok to use *_RELATIVE. */ + return (1); +} + +void +ld_reloc_process_input_section(struct ld *ld, struct ld_input_section *is, + void *buf) +{ + struct ld_input *li; + struct ld_input_section *ris; + struct ld_output_section *os; + struct ld_reloc_entry *lre; + struct ld_symbol *lsb; + int i; + + if (is->is_type == SHT_REL || is->is_type == SHT_RELA) + return; + + os = is->is_output; + + li = is->is_input; + if (is->is_ris != NULL) + ris = is->is_ris; + else { + ris = NULL; + for (i = 0; (uint64_t) i < li->li_shnum; i++) { + if (li->li_is[i].is_type != SHT_REL && + li->li_is[i].is_type != SHT_RELA) + continue; + if (li->li_is[i].is_info == is->is_index) { + ris = &li->li_is[i]; + break; + } + } + } + + if (ris == NULL) + return; + + assert(ris->is_reloc != NULL); + + STAILQ_FOREACH(lre, ris->is_reloc, lre_next) { + lsb = ld_symbols_ref(lre->lre_sym); + + /* + * Arch-specific relocation handling for non-relocatable + * output object. + */ + if (!ld->ld_reloc) + ld->ld_arch->process_reloc(ld, is, lre, lsb, buf); + + /* + * Arch-specific relocation handling for relocatable output + * object and -emit-relocs option. + * + * Note that for SHT_REL relocation sections, relocation + * addend (in-place) is not adjusted since it will overwrite + * the already applied relocation. + */ + if (ld->ld_reloc || + (ld->ld_emit_reloc && ld->ld_arch->reloc_is_rela)) + ld->ld_arch->adjust_reloc(ld, is, lre, lsb, buf); + + /* + * Update the relocation offset to make it point to the + * correct place in the output section. For -emit-relocs + * option, the section VMA is used. For relocatable output + * object, the section relative offset is added to the + * relocation offset. + */ + if (ld->ld_reloc) + lre->lre_offset += is->is_reloff; + else if (ld->ld_emit_reloc) + lre->lre_offset += os->os_addr + is->is_reloff; + } +} |