contrib/elftoolchain/ld/ld_output.c (854 lines of code) (raw):

/*- * Copyright (c) 2011-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_dynamic.h" #include "ld_ehframe.h" #include "ld_input.h" #include "ld_output.h" #include "ld_layout.h" #include "ld_reloc.h" #include "ld_script.h" #include "ld_strtab.h" #include "ld_symbols.h" ELFTC_VCSID("$Id$"); static void _alloc_input_section_data(struct ld *ld, Elf_Scn *scn, struct ld_input_section *is); static void _alloc_section_data_from_buffer(struct ld *ld, Elf_Scn *scn, struct ld_output_data_buffer *odb); static void _alloc_section_data_for_symtab(struct ld *ld, struct ld_output_section *os, Elf_Scn *scn, struct ld_symbol_table *symtab); static void _alloc_section_data_for_strtab(struct ld *ld, Elf_Scn *scn, struct ld_strtab *strtab); static void _add_to_shstrtab(struct ld *ld, const char *name); static void _copy_and_reloc_input_sections(struct ld *ld); static Elf_Scn *_create_elf_scn(struct ld *ld, struct ld_output *lo, struct ld_output_section *os); static void _create_elf_section(struct ld *ld, struct ld_output_section *os); static void _create_phdr(struct ld *ld); static void _create_symbol_table(struct ld *ld); static uint64_t _find_entry_point(struct ld *ld); static void _produce_reloc_sections(struct ld *ld, struct ld_output *lo); static void _join_and_finalize_dynamic_reloc_sections(struct ld *ld, struct ld_output *lo); static void _join_normal_reloc_sections(struct ld *ld, struct ld_output *lo); static void _update_section_header(struct ld *ld); void ld_output_early_init(struct ld *ld) { struct ld_output *lo; if (ld->ld_output == NULL) { if ((lo = calloc(1, sizeof(*lo))) == NULL) ld_fatal_std(ld, "calloc"); STAILQ_INIT(&lo->lo_oelist); STAILQ_INIT(&lo->lo_oslist); ld->ld_output = lo; } else lo = ld->ld_output; assert(ld->ld_otgt != NULL); lo->lo_ec = elftc_bfd_target_class(ld->ld_otgt); lo->lo_endian = elftc_bfd_target_byteorder(ld->ld_otgt); } void ld_output_init(struct ld *ld) { struct ld_output *lo; const char *fn; GElf_Ehdr eh; lo = ld->ld_output; assert(lo != NULL); if (ld->ld_output_file == NULL) fn = "a.out"; else fn = ld->ld_output_file; lo->lo_fd = open(fn, O_WRONLY | O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO); if (lo->lo_fd < 0) ld_fatal_std(ld, "can not create output file: open %s", fn); if ((lo->lo_elf = elf_begin(lo->lo_fd, ELF_C_WRITE, NULL)) == NULL) ld_fatal(ld, "elf_begin failed: %s", elf_errmsg(-1)); elf_flagelf(lo->lo_elf, ELF_C_SET, ELF_F_LAYOUT); if (gelf_newehdr(lo->lo_elf, lo->lo_ec) == NULL) ld_fatal(ld, "gelf_newehdr failed: %s", elf_errmsg(-1)); if (gelf_getehdr(lo->lo_elf, &eh) == NULL) ld_fatal(ld, "gelf_getehdr failed: %s", elf_errmsg(-1)); eh.e_ident[EI_CLASS] = lo->lo_ec; eh.e_ident[EI_DATA] = lo->lo_endian; eh.e_flags = ld->ld_arch->flags; eh.e_machine = elftc_bfd_target_machine(ld->ld_otgt); if (ld->ld_dso || ld->ld_pie) eh.e_type = ET_DYN; else if (ld->ld_reloc) eh.e_type = ET_REL; else eh.e_type = ET_EXEC; eh.e_version = EV_CURRENT; /* Save updated ELF header. */ if (gelf_update_ehdr(lo->lo_elf, &eh) == 0) ld_fatal(ld, "gelf_update_ehdr failed: %s", elf_errmsg(-1)); } void ld_output_format(struct ld *ld, char *def, char *be, char *le) { ld->ld_otgt_name = def; if ((ld->ld_otgt = elftc_bfd_find_target(def)) == NULL) ld_fatal(ld, "invalid BFD format %s", def); ld->ld_otgt_be_name = be; if ((ld->ld_otgt_be = elftc_bfd_find_target(be)) == NULL) ld_fatal(ld, "invalid BFD format %s", be); ld->ld_otgt_le_name = le; if ((ld->ld_otgt_le = elftc_bfd_find_target(le)) == NULL) ld_fatal(ld, "invalid BFD format %s", le); ld_arch_set_from_target(ld); } struct ld_output_element * ld_output_create_element(struct ld *ld, struct ld_output_element_head *head, enum ld_output_element_type type, void *entry, struct ld_output_element *after) { struct ld_output_element *oe; if ((oe = calloc(1, sizeof(*oe))) == NULL) ld_fatal_std(ld, "calloc"); oe->oe_type = type; oe->oe_entry = entry; if (after == NULL) STAILQ_INSERT_TAIL(head, oe, oe_next); else STAILQ_INSERT_AFTER(head, after, oe, oe_next); return (oe); } struct ld_output_element * ld_output_create_section_element(struct ld *ld, struct ld_output_section *os, enum ld_output_element_type type, void *entry, struct ld_output_element *after) { struct ld_output_element *oe; oe = ld_output_create_element(ld, &os->os_e, type, entry, after); switch (type) { case OET_DATA: case OET_DATA_BUFFER: case OET_SYMTAB: case OET_STRTAB: os->os_empty = 0; break; default: break; } return (oe); } struct ld_output_section * ld_output_alloc_section(struct ld *ld, const char *name, struct ld_output_section *after, struct ld_output_section *ros) { struct ld_output *lo; struct ld_output_section *os; lo = ld->ld_output; if ((os = calloc(1, sizeof(*os))) == NULL) ld_fatal_std(ld, "calloc"); if ((os->os_name = strdup(name)) == NULL) ld_fatal_std(ld, "strdup"); os->os_align = 1; os->os_empty = 1; STAILQ_INIT(&os->os_e); HASH_ADD_KEYPTR(hh, lo->lo_ostbl, os->os_name, strlen(os->os_name), os); if (after == NULL) { STAILQ_INSERT_TAIL(&lo->lo_oslist, os, os_next); os->os_pe = ld_output_create_element(ld, &lo->lo_oelist, OET_OUTPUT_SECTION, os, NULL); } else { STAILQ_INSERT_AFTER(&lo->lo_oslist, after, os, os_next); os->os_pe = ld_output_create_element(ld, &lo->lo_oelist, OET_OUTPUT_SECTION, os, after->os_pe); } if (ros != NULL) ros->os_r = os; return (os); } static Elf_Scn * _create_elf_scn(struct ld *ld, struct ld_output *lo, struct ld_output_section *os) { Elf_Scn *scn; assert(lo->lo_elf != NULL); if ((scn = elf_newscn(lo->lo_elf)) == NULL) ld_fatal(ld, "elf_newscn failed: %s", elf_errmsg(-1)); if (os != NULL) os->os_scn = scn; return (scn); } static void _create_elf_section(struct ld *ld, struct ld_output_section *os) { struct ld_output *lo; struct ld_output_element *oe; struct ld_input_section *is; struct ld_input_section_head *islist; Elf_Data *d; Elf_Scn *scn; lo = ld->ld_output; assert(lo->lo_elf != NULL); /* Create section data. */ scn = NULL; STAILQ_FOREACH(oe, &os->os_e, oe_next) { switch (oe->oe_type) { case OET_DATA: if (scn == NULL) scn = _create_elf_scn(ld, lo, os); /* TODO */ break; case OET_DATA_BUFFER: if (scn == NULL) scn = _create_elf_scn(ld, lo, os); _alloc_section_data_from_buffer(ld, scn, oe->oe_entry); break; case OET_INPUT_SECTION_LIST: islist = oe->oe_islist; STAILQ_FOREACH(is, islist, is_next) { if (scn == NULL) scn = _create_elf_scn(ld, lo, os); if (os->os_type != SHT_NOBITS && !os->os_dynrel) _alloc_input_section_data(ld, scn, is); } if ((ld->ld_reloc || ld->ld_emit_reloc) && os->os_r != NULL) { /* Create Scn for relocation section. */ if (os->os_r->os_scn == NULL) { os->os_r->os_scn = _create_elf_scn(ld, lo, os->os_r); _add_to_shstrtab(ld, os->os_r->os_name); } } break; case OET_KEYWORD: if (scn == NULL) scn = _create_elf_scn(ld, lo, os); /* TODO */ break; case OET_SYMTAB: /* TODO: Check symtab size. */ if (scn == NULL) scn = _create_elf_scn(ld, lo, os); break; case OET_STRTAB: /* TODO: Check strtab size. */ if (scn == NULL) scn = _create_elf_scn(ld, lo, os); _alloc_section_data_for_strtab(ld, scn, oe->oe_entry); break; default: break; } } if (scn == NULL) return; if (os->os_type == SHT_NOBITS) { if ((d = elf_newdata(scn)) == NULL) ld_fatal(ld, "elf_newdata failed: %s", elf_errmsg(-1)); d->d_align = os->os_align; d->d_off = 0; d->d_type = ELF_T_BYTE; d->d_size = os->os_size; d->d_version = EV_CURRENT; d->d_buf = NULL; } _add_to_shstrtab(ld, os->os_name); } static void _alloc_input_section_data(struct ld *ld, Elf_Scn *scn, struct ld_input_section *is) { Elf_Data *d; if (is->is_type == SHT_NOBITS || is->is_size == 0) return; if ((d = elf_newdata(scn)) == NULL) ld_fatal(ld, "elf_newdata failed: %s", elf_errmsg(-1)); is->is_data = d; } static void _alloc_section_data_from_buffer(struct ld *ld, Elf_Scn *scn, struct ld_output_data_buffer *odb) { Elf_Data *d; if ((d = elf_newdata(scn)) == NULL) ld_fatal(ld, "elf_newdata failed: %s", elf_errmsg(-1)); d->d_align = odb->odb_align; d->d_off = odb->odb_off; d->d_type = odb->odb_type; d->d_size = odb->odb_size; d->d_version = EV_CURRENT; d->d_buf = odb->odb_buf; } static void _alloc_section_data_from_reloc_buffer(struct ld *ld, Elf_Scn *scn, void *buf, size_t sz) { Elf_Data *d; if ((d = elf_newdata(scn)) == NULL) ld_fatal(ld, "elf_newdata failed: %s", elf_errmsg(-1)); d->d_align = ld->ld_arch->reloc_is_64bit ? 8 : 4; d->d_off = 0; /* has to be the only data descriptor */ d->d_type = ld->ld_arch->reloc_is_rela ? ELF_T_RELA : ELF_T_REL; d->d_size = sz; d->d_version = EV_CURRENT; d->d_buf = buf; } static void _alloc_section_data_for_symtab(struct ld *ld, struct ld_output_section *os, Elf_Scn *scn, struct ld_symbol_table *symtab) { Elf_Data *d; if (symtab->sy_buf == NULL || symtab->sy_size == 0) return; if ((d = elf_newdata(scn)) == NULL) ld_fatal(ld, "elf_newdata failed: %s", elf_errmsg(-1)); d->d_align = os->os_align; d->d_off = 0; d->d_type = ELF_T_SYM; d->d_size = os->os_entsize * symtab->sy_size; d->d_version = EV_CURRENT; d->d_buf = symtab->sy_buf; } static void _alloc_section_data_for_strtab(struct ld *ld, Elf_Scn *scn, struct ld_strtab *strtab) { Elf_Data *d; void *buf; size_t sz; buf = ld_strtab_getbuf(ld, strtab); sz = ld_strtab_getsize(strtab); if (buf == NULL || sz == 0) return; if ((d = elf_newdata(scn)) == NULL) ld_fatal(ld, "elf_newdata failed: %s", elf_errmsg(-1)); d->d_align = 1; d->d_off = 0; d->d_type = ELF_T_BYTE; d->d_size = sz; d->d_version = EV_CURRENT; d->d_buf = buf; } static void _copy_and_reloc_input_sections(struct ld *ld) { struct ld_input *li; struct ld_input_section *is; Elf_Data *d; int i; STAILQ_FOREACH(li, &ld->ld_lilist, li_next) { ld_input_load(ld, li); for (i = 0; (uint64_t) i < li->li_shnum; i++) { is = &li->li_is[i]; if (is->is_discard || !is->is_need_reloc) continue; d = is->is_data; d->d_align = is->is_align; d->d_off = is->is_reloff; d->d_type = ELF_T_BYTE; d->d_size = is->is_size; d->d_version = EV_CURRENT; /* * Take different actions depending on different types * of input sections: * * For internal input sections, assign the internal * buffer directly to the data descriptor. * For relocation sections, they should be ignored * since they are handled elsewhere. * For other input sections, load the raw data from * input object and preform relocation. */ if (is->is_ibuf != NULL) { d->d_buf = is->is_ibuf; /* .eh_frame section needs relocation */ if (strcmp(is->is_name, ".eh_frame") == 0) ld_reloc_process_input_section(ld, is, d->d_buf); } else if (is->is_reloc == NULL) { d->d_buf = ld_input_get_section_rawdata(ld, is); ld_reloc_process_input_section(ld, is, d->d_buf); } } ld_input_unload(ld, li); } } static void _produce_reloc_sections(struct ld *ld, struct ld_output *lo) { struct ld_output_section *os; void *buf; size_t sz; STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { if (os->os_reloc != NULL) { /* Serialize relocation records. */ buf = ld_reloc_serialize(ld, os, &sz); _alloc_section_data_from_reloc_buffer(ld, os->os_scn, buf, sz); /* * Link dynamic relocation sections to .dynsym * section. */ if (os->os_dynrel) { if ((os->os_link = strdup(".dynsym")) == NULL) ld_fatal_std(ld, "strdup"); } } } } static void _join_and_finalize_dynamic_reloc_sections(struct ld *ld, struct ld_output *lo) { struct ld_output_section *os; struct ld_output_element *oe; struct ld_input_section *is; struct ld_input_section_head *islist; STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { if (!os->os_dynrel) continue; STAILQ_FOREACH(oe, &os->os_e, oe_next) { switch (oe->oe_type) { case OET_INPUT_SECTION_LIST: islist = oe->oe_islist; STAILQ_FOREACH(is, islist, is_next) ld_reloc_join(ld, os, is); break; default: break; } } /* Sort dynamic relocations for the runtime linker. */ if (os->os_reloc != NULL && os->os_dynrel && !os->os_pltrel) ld_reloc_sort(ld, os); /* Finalize relocations. */ ld_reloc_finalize_dynamic(ld, lo, os); } } static void _join_normal_reloc_sections(struct ld *ld, struct ld_output *lo) { struct ld_output_section *os; struct ld_output_element *oe; struct ld_input_section *is; struct ld_input_section_head *islist; STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { if (os->os_r == NULL) continue; STAILQ_FOREACH(oe, &os->os_e, oe_next) { switch (oe->oe_type) { case OET_INPUT_SECTION_LIST: islist = oe->oe_islist; STAILQ_FOREACH(is, islist, is_next) { if (is->is_ris == NULL) continue; ld_reloc_join(ld, os->os_r, is->is_ris); } break; default: break; } } } } void ld_output_create_elf_sections(struct ld *ld) { struct ld_output *lo; struct ld_output_element *oe; lo = ld->ld_output; assert(lo->lo_elf != NULL); STAILQ_FOREACH(oe, &lo->lo_oelist, oe_next) { switch (oe->oe_type) { case OET_OUTPUT_SECTION: _create_elf_section(ld, oe->oe_entry); break; default: break; } } } void ld_output_emit_gnu_stack_section(struct ld *ld) { struct ld_state *ls; struct ld_output *lo; struct ld_output_section *os; ls = &ld->ld_state; lo = ld->ld_output; os = ld_output_alloc_section(ld, ".note.GNU-stack", NULL, NULL); os->os_empty = 0; os->os_addr = 0; os->os_type = SHT_PROGBITS; os->os_align = 1; os->os_entsize = 0; os->os_off = ls->ls_offset; os->os_size = 0; if (ld->ld_stack_exec) os->os_flags = SHF_EXECINSTR; (void) _create_elf_scn(ld, lo, os); _add_to_shstrtab(ld, ".note.GNU-stack"); /* * .note.GNU-stack is an empty section so we don't allocate any * Elf_Data descriptors. */ } static uint64_t _find_entry_point(struct ld *ld) { struct ld_output *lo; struct ld_output_section *os; char entry_symbol[] = "start"; uint64_t entry; lo = ld->ld_output; if (ld->ld_entry != NULL) { if (ld_symbols_get_value(ld, ld->ld_entry, &entry) < 0) ld_fatal(ld, "symbol %s is undefined", ld->ld_entry); return (entry); } if (ld->ld_scp->lds_entry_point != NULL) { if (ld_symbols_get_value(ld, ld->ld_scp->lds_entry_point, &entry) == 0) return (entry); } if (ld_symbols_get_value(ld, entry_symbol, &entry) == 0) return (entry); STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { if (os->os_empty) continue; if (strcmp(os->os_name, ".text") == 0) return (os->os_addr); } return (0); } static void _create_phdr(struct ld *ld) { struct ld_output *lo; struct ld_output_section *os; Elf32_Phdr *p32; Elf64_Phdr *p64; void *phdrs; uint64_t addr, off, align, flags, filesz, memsz, phdr_addr; uint64_t tls_addr, tls_off, tls_align, tls_flags; uint64_t tls_filesz, tls_memsz; unsigned w; int i, new, first, tls; /* TODO: support segments created by linker script command PHDR. */ #define _WRITE_PHDR(T,O,A,FSZ,MSZ,FL,AL) \ do { \ if (lo->lo_ec == ELFCLASS32) { \ p32[i].p_type = (T); \ p32[i].p_offset = (O); \ p32[i].p_vaddr = (A); \ p32[i].p_paddr = (A); \ p32[i].p_filesz = (FSZ); \ p32[i].p_memsz = (MSZ); \ p32[i].p_flags = (FL); \ p32[i].p_align = (AL); \ } else { \ p64[i].p_type = (T); \ p64[i].p_offset = (O); \ p64[i].p_vaddr = (A); \ p64[i].p_paddr = (A); \ p64[i].p_filesz = (FSZ); \ p64[i].p_memsz = (MSZ); \ p64[i].p_flags = (FL); \ p64[i].p_align = (AL); \ } \ } while(0) lo = ld->ld_output; assert(lo->lo_elf != NULL); assert(lo->lo_phdr_num != 0); assert(ld->ld_arch != NULL); if ((phdrs = gelf_newphdr(lo->lo_elf, lo->lo_phdr_num)) == NULL) ld_fatal(ld, "gelf_newphdr failed: %s", elf_errmsg(-1)); p32 = NULL; p64 = NULL; if (lo->lo_ec == ELFCLASS32) p32 = phdrs; else p64 = phdrs; i = -1; /* Calculate the start vma of output object. */ os = STAILQ_FIRST(&lo->lo_oslist); addr = os->os_addr - os->os_off; /* Create PT_PHDR segment for dynamically linked output object */ if (lo->lo_dso_needed > 0 && !ld->ld_dso) { i++; off = gelf_fsize(lo->lo_elf, ELF_T_EHDR, 1, EV_CURRENT); phdr_addr = addr + off; filesz = memsz = gelf_fsize(lo->lo_elf, ELF_T_PHDR, lo->lo_phdr_num, EV_CURRENT); align = lo->lo_ec == ELFCLASS32 ? 4 : 8; flags = PF_R | PF_X; _WRITE_PHDR(PT_PHDR, off, phdr_addr, filesz, memsz, flags, align); } /* Create PT_INTERP segment for dynamically linked output object */ if (lo->lo_interp != NULL) { i++; os = lo->lo_interp; _WRITE_PHDR(PT_INTERP, os->os_off, os->os_addr, os->os_size, os->os_size, PF_R, 1); } /* * Create PT_LOAD segments. */ align = ld->ld_arch->get_max_page_size(ld); new = 1; w = 0; off = filesz = memsz = 0; flags = PF_R; first = 1; tls = 0; tls_off = tls_addr = tls_filesz = tls_memsz = tls_align = 0; tls_flags = PF_R; /* TLS segment is a read-only image */ STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { if (os->os_empty) continue; if ((os->os_flags & SHF_ALLOC) == 0) { new = 1; continue; } if ((os->os_flags & SHF_TLS) != 0) { if (tls < 0) ld_warn(ld, "can not have multiple TLS " "segments"); else { if (tls == 0) { tls = 1; tls_addr = os->os_addr; tls_off = os->os_off; } if (os->os_align > tls_align) tls_align = os->os_align; } } else if (tls > 0) tls = -1; if ((os->os_flags & SHF_WRITE) != w || new) { new = 0; w = os->os_flags & SHF_WRITE; if (!first) _WRITE_PHDR(PT_LOAD, off, addr, filesz, memsz, flags, align); i++; if ((unsigned) i >= lo->lo_phdr_num) ld_fatal(ld, "not enough room for program" " headers"); if (!first) { addr = os->os_addr; off = os->os_off; } first = 0; flags = PF_R; filesz = 0; memsz = 0; } memsz = os->os_addr + os->os_size - addr; if (tls > 0) tls_memsz = memsz; if (os->os_type != SHT_NOBITS) { filesz = memsz; if (tls > 0) tls_filesz = tls_memsz; } if (os->os_flags & SHF_WRITE) flags |= PF_W; if (os->os_flags & SHF_EXECINSTR) flags |= PF_X; } if (i >= 0) _WRITE_PHDR(PT_LOAD, off, addr, filesz, memsz, flags, align); /* * Create PT_DYNAMIC segment. */ if (lo->lo_dynamic != NULL) { i++; os = lo->lo_dynamic; _WRITE_PHDR(PT_DYNAMIC, os->os_off, os->os_addr, os->os_size, os->os_size, PF_R | PF_W, lo->lo_ec == ELFCLASS32 ? 4 : 8); } /* * Create PT_NOTE segment. */ STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { if (os->os_type == SHT_NOTE) { i++; if ((unsigned) i >= lo->lo_phdr_num) ld_fatal(ld, "not enough room for program" " headers"); _WRITE_PHDR(PT_NOTE, os->os_off, os->os_addr, os->os_size, os->os_size, PF_R, os->os_align); break; } } /* * Create PT_TLS segment. */ if (tls != 0) { i++; lo->lo_tls_size = tls_memsz; lo->lo_tls_align = tls_align; lo->lo_tls_addr = tls_addr; _WRITE_PHDR(PT_TLS, tls_off, tls_addr, tls_filesz, tls_memsz, tls_flags, tls_align); } /* * Create PT_GNU_EH_FRAME segment. */ if (ld->ld_ehframe_hdr) { i++; os = lo->lo_ehframe_hdr; assert(os != NULL); _WRITE_PHDR(PT_GNU_EH_FRAME, os->os_off, os->os_addr, os->os_size, os->os_size, PF_R, 4); } /* * Create PT_GNU_STACK segment. */ if (ld->ld_gen_gnustack) { i++; flags = PF_R | PF_W; if (ld->ld_stack_exec) flags |= PF_X; align = (lo->lo_ec == ELFCLASS32) ? 4 : 8; _WRITE_PHDR(PT_GNU_STACK, 0, 0, 0, 0, flags, align); } assert((unsigned) i + 1 == lo->lo_phdr_num); #undef _WRITE_PHDR } void ld_output_create(struct ld *ld) { struct ld_output *lo; GElf_Ehdr eh; lo = ld->ld_output; if (gelf_getehdr(lo->lo_elf, &eh) == NULL) ld_fatal(ld, "gelf_getehdr failed: %s", elf_errmsg(-1)); /* Set program header table offset. */ eh.e_phoff = gelf_fsize(lo->lo_elf, ELF_T_EHDR, 1, EV_CURRENT); if (eh.e_phoff == 0) ld_fatal(ld, "gelf_fsize failed: %s", elf_errmsg(-1)); /* Set section headers table offset. */ eh.e_shoff = lo->lo_shoff; /* Set executable entry point. */ eh.e_entry = _find_entry_point(ld); /* Save updated ELF header. */ if (gelf_update_ehdr(lo->lo_elf, &eh) == 0) ld_fatal(ld, "gelf_update_ehdr failed: %s", elf_errmsg(-1)); /* Allocate space for internal sections. */ ld_input_alloc_internal_section_buffers(ld); /* Finalize PLT and GOT sections. */ if (ld->ld_arch->finalize_got_and_plt) ld->ld_arch->finalize_got_and_plt(ld); /* Join and sort dynamic relocation sections. */ _join_and_finalize_dynamic_reloc_sections(ld, lo); /* Finalize sections for dynamically linked output object. */ ld_dynamic_finalize(ld); /* Finalize dynamic symbol section. */ if (lo->lo_dynsym != NULL) { ld_symbols_finalize_dynsym(ld); _alloc_section_data_for_symtab(ld, lo->lo_dynsym, lo->lo_dynsym->os_scn, ld->ld_dynsym); } /* Generate symbol table. */ _create_symbol_table(ld); /* Copy and relocate input section data to output section. */ _copy_and_reloc_input_sections(ld); /* Finalize .eh_frame_hdr section. */ if (ld->ld_ehframe_hdr) ld_ehframe_finalize_hdr(ld); /* * Join normal relocation sections if the linker is creating a * relocatable object or if option -emit-relocs is specified. */ if (ld->ld_reloc || ld->ld_emit_reloc) _join_normal_reloc_sections(ld, lo); /* Produce relocation entries. */ _produce_reloc_sections(ld, lo); /* Update section headers for the output sections. */ _update_section_header(ld); /* Create program headers. */ if (!ld->ld_reloc) _create_phdr(ld); /* Finally write out the output ELF object. */ if (elf_update(lo->lo_elf, ELF_C_WRITE) < 0) ld_fatal(ld, "elf_update failed: %s", elf_errmsg(-1)); } static void _add_to_shstrtab(struct ld *ld, const char *name) { if (ld->ld_shstrtab == NULL) { ld->ld_shstrtab = ld_strtab_alloc(ld, 1); ld_strtab_insert(ld, ld->ld_shstrtab, ".symtab"); ld_strtab_insert(ld, ld->ld_shstrtab, ".strtab"); ld_strtab_insert(ld, ld->ld_shstrtab, ".shstrtab"); } ld_strtab_insert(ld, ld->ld_shstrtab, name); } static void _update_section_header(struct ld *ld) { struct ld_strtab *st; struct ld_output *lo; struct ld_output_section *os, *_os; GElf_Shdr sh; lo = ld->ld_output; st = ld->ld_shstrtab; assert(st != NULL); STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { if (os->os_scn == NULL) continue; if (gelf_getshdr(os->os_scn, &sh) == NULL) ld_fatal(ld, "gelf_getshdr failed: %s", elf_errmsg(-1)); sh.sh_name = ld_strtab_lookup(st, os->os_name); sh.sh_flags = os->os_flags; sh.sh_addr = os->os_addr; sh.sh_addralign = os->os_align; sh.sh_offset = os->os_off; sh.sh_size = os->os_size; sh.sh_type = os->os_type; sh.sh_entsize = os->os_entsize; /* Update "sh_link" field. */ if (os->os_link != NULL) { if (!strcmp(os->os_link, ".symtab")) sh.sh_link = lo->lo_symtab_shndx; else { HASH_FIND_STR(lo->lo_ostbl, os->os_link, _os); if (_os == NULL) ld_fatal(ld, "Internal: can not find" " link section %s", os->os_link); sh.sh_link = elf_ndxscn(_os->os_scn); } } /* Update "sh_info" field. */ if (os->os_info != NULL) sh.sh_info = elf_ndxscn(os->os_info->os_scn); else sh.sh_info = os->os_info_val; #if 0 printf("name=%s, shname=%#jx, offset=%#jx, size=%#jx, type=%#jx\n", os->os_name, (uint64_t) sh.sh_name, (uint64_t) sh.sh_offset, (uint64_t) sh.sh_size, (uint64_t) sh.sh_type); #endif if (!gelf_update_shdr(os->os_scn, &sh)) ld_fatal(ld, "gelf_update_shdr failed: %s", elf_errmsg(-1)); } } static void _create_symbol_table(struct ld *ld) { struct ld_state *ls; struct ld_strtab *st; struct ld_output *lo; Elf_Scn *scn_symtab, *scn_strtab; Elf_Data *d; GElf_Shdr sh; size_t strndx; ld_symbols_build_symtab(ld); ls = &ld->ld_state; lo = ld->ld_output; st = ld->ld_shstrtab; assert(st != NULL); /* * Create .symtab section. */ scn_symtab = _create_elf_scn(ld, lo, NULL); scn_strtab = _create_elf_scn(ld, lo, NULL); lo->lo_symtab_shndx = elf_ndxscn(scn_symtab); strndx = elf_ndxscn(scn_strtab); if (gelf_getshdr(scn_symtab, &sh) == NULL) ld_fatal(ld, "gelf_getshdr failed: %s", elf_errmsg(-1)); sh.sh_name = ld_strtab_lookup(st, ".symtab"); sh.sh_flags = 0; sh.sh_addr = 0; sh.sh_addralign = (lo->lo_ec == ELFCLASS32) ? 4 : 8; sh.sh_offset = roundup(ls->ls_offset, sh.sh_addralign); sh.sh_entsize = (lo->lo_ec == ELFCLASS32) ? sizeof(Elf32_Sym) : sizeof(Elf64_Sym); sh.sh_size = ld->ld_symtab->sy_size * sh.sh_entsize; sh.sh_type = SHT_SYMTAB; sh.sh_link = strndx; sh.sh_info = ld->ld_symtab->sy_first_nonlocal; if (!gelf_update_shdr(scn_symtab, &sh)) ld_fatal(ld, "gelf_update_shdr failed: %s", elf_errmsg(-1)); if ((d = elf_newdata(scn_symtab)) == NULL) ld_fatal(ld, "elf_newdata failed: %s", elf_errmsg(-1)); d->d_align = sh.sh_addralign; d->d_off = 0; d->d_type = ELF_T_SYM; d->d_size = sh.sh_size; d->d_version = EV_CURRENT; d->d_buf = ld->ld_symtab->sy_buf; ls->ls_offset = sh.sh_offset + sh.sh_size; /* * Create .strtab section. */ ld_output_create_string_table_section(ld, ".strtab", ld->ld_strtab, scn_strtab); } void ld_output_create_string_table_section(struct ld *ld, const char *name, struct ld_strtab *st, Elf_Scn *scn) { struct ld_state *ls; struct ld_output *lo; Elf_Data *d; GElf_Shdr sh; size_t sz; assert(st != NULL && name != NULL); ls = &ld->ld_state; lo = ld->ld_output; if (scn == NULL) scn = _create_elf_scn(ld, lo, NULL); if (strcmp(name, ".shstrtab") == 0) { if (!elf_setshstrndx(lo->lo_elf, elf_ndxscn(scn))) ld_fatal(ld, "elf_setshstrndx failed: %s", elf_errmsg(-1)); } if (gelf_getshdr(scn, &sh) == NULL) ld_fatal(ld, "gelf_getshdr failed: %s", elf_errmsg(-1)); sh.sh_name = ld_strtab_lookup(ld->ld_shstrtab, name); sh.sh_flags = 0; sh.sh_addr = 0; sh.sh_addralign = 1; sh.sh_offset = ls->ls_offset; sh.sh_size = ld_strtab_getsize(st); sh.sh_type = SHT_STRTAB; if (!gelf_update_shdr(scn, &sh)) ld_fatal(ld, "gelf_update_shdr failed: %s", elf_errmsg(-1)); sz = ld_strtab_getsize(st); if ((d = elf_newdata(scn)) == NULL) ld_fatal(ld, "elf_newdata failed: %s", elf_errmsg(-1)); d->d_align = 1; d->d_off = 0; d->d_type = ELF_T_BYTE; d->d_size = sz; d->d_version = EV_CURRENT; d->d_buf = ld_strtab_getbuf(ld, st); ls->ls_offset += sz; }