diff options
Diffstat (limited to 'ld/ld_layout.c')
-rw-r--r-- | ld/ld_layout.c | 1254 |
1 files changed, 1254 insertions, 0 deletions
diff --git a/ld/ld_layout.c b/ld/ld_layout.c new file mode 100644 index 0000000000000..11711b2d825a8 --- /dev/null +++ b/ld/ld_layout.c @@ -0,0 +1,1254 @@ +/*- + * 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_exp.h" +#include "ld_file.h" +#include "ld_script.h" +#include "ld_input.h" +#include "ld_output.h" +#include "ld_reloc.h" +#include "ld_layout.h" +#include "ld_options.h" +#include "ld_symbols.h" +#include "ld_strtab.h" + +ELFTC_VCSID("$Id: ld_layout.c 2965 2013-09-10 02:46:29Z kaiwang27 $"); + +struct ld_wildcard_match { + char *wm_name; + unsigned wm_no_match; + struct ld_output_section *wm_os; + struct ld_input_section_head *wm_islist; + struct ld_script_sections_output_input *wm_ldoi; + struct ld_wildcard_match *wm_next; + UT_hash_handle hh; +}; + +/* + * Support routines for output section layout. + */ + +static void _calc_offset(struct ld *ld); +static void _calc_output_section_offset(struct ld *ld, + struct ld_output_section *os); +static void _calc_reloc_section_offset(struct ld *ld, struct ld_output *lo); +static void _calc_shdr_offset(struct ld *ld); +static int _check_filename_constraint(struct ld_input *li, + struct ld_script_sections_output_input *ldoi); +static void _insert_input_to_output(struct ld *ld, struct ld_output *lo, + struct ld_output_section *os, struct ld_input_section *is, + struct ld_input_section_head *islist); +static void _layout_input_sections(struct ld *ld, struct ld_input *li); +static void _layout_orphan_section(struct ld *ld, struct ld_input_section *is); +static void _layout_sections(struct ld *ld, struct ld_script_sections *ldss); +static void _parse_output_section_descriptor(struct ld *ld, + struct ld_output_section *os); +static void _prepare_output_section(struct ld *ld, + struct ld_script_sections_output *ldso); +static void _print_section_layout(struct ld *ld, struct ld_output_section *os); +static void _print_wildcard(struct ld_wildcard *lw); +static void _print_wildcard_list(struct ld_script_list *ldl); +static void _record_wildcard_match(struct ld *ld, char *name, + struct ld_output_section *os, struct ld_output_element *oe); +static void _record_wildcard_no_match(struct ld *ld, char *name); +static void _set_output_section_loadable_flag(struct ld_output_section *os); +static int _wildcard_match(struct ld_wildcard *lw, const char *string); +static int _wildcard_list_match(struct ld_script_list *list, + const char *string); + +void +ld_layout_sections(struct ld *ld) +{ + struct ld_output *lo; + struct ld_script *lds; + struct ld_script_cmd *ldc; + struct ld_state *ls; + int sections_cmd_exist; + + ls = &ld->ld_state; + lo = ld->ld_output; + lds = ld->ld_scp; + + sections_cmd_exist = 0; + STAILQ_FOREACH(ldc, &lds->lds_c, ldc_next) { + switch (ldc->ldc_type) { + case LSC_ASSERT: + ld_output_create_element(ld, &lo->lo_oelist, OET_ASSERT, + ldc->ldc_cmd, NULL); + break; + case LSC_ASSIGN: + ld_output_create_element(ld, &lo->lo_oelist, OET_ASSIGN, + ldc->ldc_cmd, NULL); + break; + case LSC_ENTRY: + ld_output_create_element(ld, &lo->lo_oelist, OET_ENTRY, + ldc->ldc_cmd, NULL); + break; + case LSC_SECTIONS: + if (sections_cmd_exist) + ld_fatal(ld, "found multiple SECTIONS commands" + " in the linker script"); + sections_cmd_exist = 1; + _layout_sections(ld, ldc->ldc_cmd); + break; + default: + break; + } + } + + if (!sections_cmd_exist) + _layout_sections(ld, NULL); + + /* Scan and optimize .eh_frame section. */ + ld_ehframe_scan(ld); + + /* Initialise sections for dyanmically linked output object. */ + ld_dynamic_create(ld); + + /* Create ELF sections. */ + ld_output_create_elf_sections(ld); + + /* Calculate section offsets of the output object. */ + _calc_offset(ld); + + /* Calculate symbol values and indices of the output object. */ + ld_symbols_update(ld); + + /* Print out link map if requested. */ + if (ld->ld_print_linkmap) + ld_layout_print_linkmap(ld); +} + +void +ld_layout_print_linkmap(struct ld *ld) +{ + struct ld_input *li; + struct ld_input_section *is; + struct ld_output *lo; + struct ld_output_element *oe; + struct ld_script *lds; + int i; + + lo = ld->ld_output; + assert(lo != NULL); + + /* Print out the list of discarded sections. */ + printf("\nDiscarded input sections:\n\n"); + STAILQ_FOREACH(li, &ld->ld_lilist, li_next) { + for (i = 0; (size_t) i < li->li_shnum; i++) { + is = &li->li_is[i]; + if (is->is_discard) { + printf(" %-20s ", is->is_name); + if (lo->lo_ec == ELFCLASS32) + printf("0x%08jx ", + (uintmax_t) is->is_addr); + else + printf("0x%016jx ", + (uintmax_t) is->is_addr); + printf("0x%jx ", (uintmax_t) is->is_size); + printf("%s\n", ld_input_get_fullname(ld, li)); + } + } + } + + + lds = ld->ld_scp; + if (lds == NULL) + return; + + /* TODO: Dump memory configuration */ + + printf("\nLinker script and memory map\n\n"); + + /* TODO: Dump loaded objects. */ + + STAILQ_FOREACH(oe, &lo->lo_oelist, oe_next) { + + switch (oe->oe_type) { + case OET_ASSERT: + /* TODO */ + break; + case OET_ASSIGN: + ld_script_assign_dump(ld, oe->oe_entry); + break; + case OET_ENTRY: + /* TODO */ + break; + case OET_OUTPUT_SECTION: + _print_section_layout(ld, oe->oe_entry); + break; + default: + break; + } + } +} + +static void +_print_section_layout(struct ld *ld, struct ld_output_section *os) +{ + struct ld_input_section *is; + struct ld_input_section_head *islist; + struct ld_output *lo; + struct ld_output_element *oe; + struct ld_script_sections_output_input *ldoi; + + lo = ld->ld_output; + + if (os->os_empty) + printf("\n%s\n", os->os_name); + else { + printf("\n%-15s", os->os_name); + if (lo->lo_ec == ELFCLASS32) + printf(" 0x%08jx", (uintmax_t) os->os_addr); + else + printf(" 0x%016jx", (uintmax_t) os->os_addr); + printf(" %#10jx\n", (uintmax_t) os->os_size); + } + + STAILQ_FOREACH(oe, &os->os_e, oe_next) { + switch (oe->oe_type) { + case OET_ASSIGN: + ld_script_assign_dump(ld, oe->oe_entry); + break; + case OET_INPUT_SECTION_LIST: + /* + * Print out wildcard patterns and input sections + * matched by these patterns. + */ + ldoi = oe->oe_entry; + if (ldoi == NULL) + break; + putchar(' '); + if (ldoi->ldoi_ar) { + _print_wildcard(ldoi->ldoi_ar); + putchar(':'); + } + _print_wildcard(ldoi->ldoi_file); + putchar('('); + if (ldoi->ldoi_exclude) { + printf("(EXCLUDE_FILE("); + _print_wildcard_list(ldoi->ldoi_exclude); + putchar(')'); + putchar(' '); + } + _print_wildcard_list(ldoi->ldoi_sec); + putchar(')'); + putchar('\n'); + if ((islist = oe->oe_islist) == NULL) + break; + STAILQ_FOREACH(is, islist, is_next) { + if (!strcmp(is->is_name, "COMMON") && + is->is_size == 0) + continue; + printf(" %-14s", is->is_name); + if (lo->lo_ec == ELFCLASS32) + printf(" 0x%08jx", (uintmax_t) + os->os_addr + is->is_reloff); + else + printf(" 0x%016jx", (uintmax_t) + os->os_addr + is->is_reloff); + if (is->is_size == 0) + printf(" %10s", "0x0"); + else + printf(" %#10jx", (uintmax_t) + is->is_size); + printf(" %s\n", ld_input_get_fullname(ld, + is->is_input)); + } + break; + default: + break; + } + } +} + +static void +_print_wildcard(struct ld_wildcard *lw) +{ + + switch (lw->lw_sort) { + case LWS_NONE: + printf("%s", lw->lw_name); + break; + case LWS_NAME: + printf("SORT_BY_NAME(%s)", lw->lw_name); + break; + case LWS_ALIGN: + printf("SORT_BY_ALIGNMENT(%s)", lw->lw_name); + break; + case LWS_NAME_ALIGN: + printf("SORT_BY_NAME(SORT_BY_ALIGNMENT(%s))", lw->lw_name); + break; + case LWS_ALIGN_NAME: + printf("SORT_BY_ALIGNMENT(SORT_BY_NAME(%s))", lw->lw_name); + break; + default: + break; + } +} + +static void +_print_wildcard_list(struct ld_script_list *ldl) +{ + + _print_wildcard(ldl->ldl_entry); + if (ldl->ldl_next != NULL) { + putchar(' '); + _print_wildcard_list(ldl->ldl_next); + } +} + +off_t +ld_layout_calc_header_size(struct ld *ld) +{ + struct ld_script_phdr *ldsp; + struct ld_output *lo; + struct ld_output_section *os; + off_t header_size; + unsigned ec, w, num_phdrs; + int new, tls; + + lo = ld->ld_output; + assert(lo != NULL); + + header_size = 0; + + ec = elftc_bfd_target_class(ld->ld_otgt); + + if (ec == ELFCLASS32) + header_size += sizeof(Elf32_Ehdr); + else + header_size += sizeof(Elf64_Ehdr); + + /* Do not generate segments for relocatable output. */ + if (ld->ld_reloc) { + lo->lo_phdr_num = 0; + return (header_size); + } + + if (!STAILQ_EMPTY(&ld->ld_scp->lds_p)) { + num_phdrs = 0; + STAILQ_FOREACH(ldsp, &ld->ld_scp->lds_p, ldsp_next) + num_phdrs++; + } else { + if (lo->lo_phdr_num > 0) + num_phdrs = lo->lo_phdr_num; + else { + num_phdrs = 0; + new = 1; + tls = 0; + w = 0; + 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_WRITE) != w || new) { + new = 0; + num_phdrs++; + w = os->os_flags & SHF_WRITE; + } + + if ((os->os_flags & SHF_TLS) != 0 && !tls) { + tls = 1; + num_phdrs++; + } + } + + /* + * PT_PHDR and PT_DYNAMIC for dynamic linking. But + * do not create PT_PHDR for shared libraries. + */ + if (lo->lo_dso_needed > 0) { + num_phdrs++; + if (!ld->ld_dso) + num_phdrs++; + } + + if (lo->lo_interp != NULL) + num_phdrs++; + + if (lo->lo_phdr_note) + num_phdrs++; + + if (ld->ld_ehframe_hdr) + num_phdrs++; + + if (ld->ld_gen_gnustack) + num_phdrs++; + } + } + + if (ec == ELFCLASS32) + header_size += num_phdrs * sizeof(Elf32_Phdr); + else + header_size += num_phdrs * sizeof(Elf64_Phdr); + + lo->lo_phdr_num = num_phdrs; + + return (header_size); +} + +static void +_layout_sections(struct ld *ld, struct ld_script_sections *ldss) +{ + struct ld_input *li; + struct ld_output *lo; + struct ld_script_cmd *ldc; + + lo = ld->ld_output; + + /* + * Process commands inside the SECTIONS command and create + * output elements. + */ + STAILQ_FOREACH(ldc, &ldss->ldss_c, ldc_next) { + switch (ldc->ldc_type) { + case LSC_ASSERT: + ld_output_create_element(ld, &lo->lo_oelist, + OET_ASSIGN, ldc->ldc_cmd, NULL); + case LSC_ASSIGN: + ld_output_create_element(ld, &lo->lo_oelist, + OET_ASSIGN, ldc->ldc_cmd, NULL); + break; + case LSC_ENTRY: + ld_output_create_element(ld, &lo->lo_oelist, + OET_ENTRY, ldc->ldc_cmd, NULL); + break; + case LSC_SECTIONS_OUTPUT: + _prepare_output_section(ld, ldc->ldc_cmd); + break; + case LSC_SECTIONS_OVERLAY: + /* TODO */ + break; + default: + break; + } + } + + /* Lay out each input object. */ + STAILQ_FOREACH(li, &ld->ld_lilist, li_next) { + + /* Only lay out relocatable input objects. */ + if (li->li_type != LIT_RELOCATABLE) + continue; + + /* Lay out sections for the input object. */ + _layout_input_sections(ld, li); + } +} + +static void +_prepare_output_section(struct ld *ld, struct ld_script_sections_output *ldso) +{ + struct ld_script_cmd *ldc; + struct ld_input_section_head *islist; + struct ld_output *lo; + struct ld_output_section *os; + struct ld_output_element *oe; + + lo = ld->ld_output; + + HASH_FIND_STR(lo->lo_ostbl, ldso->ldso_name, os); + if (os != NULL) + return; + + os = ld_output_alloc_section(ld, ldso->ldso_name, NULL, NULL); + os->os_ldso = ldso; + _set_output_section_loadable_flag(os); + + STAILQ_FOREACH(ldc, &ldso->ldso_c, ldc_next) { + switch (ldc->ldc_type) { + case LSC_ASSERT: + oe = ld_output_create_section_element(ld, os, + OET_ASSERT, ldc->ldc_cmd, NULL); + break; + case LSC_ASSIGN: + oe = ld_output_create_section_element(ld, os, + OET_ASSIGN, ldc->ldc_cmd, NULL); + break; + case LSC_SECTIONS_OUTPUT_DATA: + oe = ld_output_create_section_element(ld, os, + OET_DATA, ldc->ldc_cmd, NULL); + break; + case LSC_SECTIONS_OUTPUT_INPUT: + islist = calloc(1, sizeof(*islist)); + if (islist == NULL) + ld_fatal_std(ld, "calloc"); + STAILQ_INIT(islist); + oe = ld_output_create_section_element(ld, os, + OET_INPUT_SECTION_LIST, ldc->ldc_cmd, NULL); + oe->oe_islist = islist; + break; + case LSC_SECTIONS_OUTPUT_KEYWORD: + ld_output_create_section_element(ld, os, + OET_KEYWORD, ldc->ldc_cmd, NULL); + break; + default: + ld_fatal(ld, "internal: invalid output section " + "command: %d", ldc->ldc_type); + } + } +} + +static int +_wildcard_match(struct ld_wildcard *lw, const char *string) +{ + + return (fnmatch(lw->lw_name, string, 0) == 0); +} + +static int +_wildcard_list_match(struct ld_script_list *list, const char *string) +{ + struct ld_script_list *ldl; + + for (ldl = list; ldl != NULL; ldl = ldl->ldl_next) + if (_wildcard_match(ldl->ldl_entry, string)) + return (1); + + return (0); +} + +static void +_set_output_section_loadable_flag(struct ld_output_section *os) +{ + struct ld_script_sections_output *ldso; + struct ld_exp *le; + + if ((ldso = os->os_ldso) == NULL) + return; + + if (ldso->ldso_vma == NULL) + os->os_flags |= SHF_ALLOC; + else { + le = ldso->ldso_vma; + if (le->le_op != LEOP_CONSTANT || le->le_val != 0) + os->os_flags |= SHF_ALLOC; + } + + if (ldso->ldso_type != NULL && strcmp(ldso->ldso_type, "NOLOAD") == 0) + os->os_flags &= ~SHF_ALLOC; +} + +static int +_check_filename_constraint(struct ld_input *li, + struct ld_script_sections_output_input *ldoi) +{ + struct ld_file *lf; + + /* Internal sections always suffice any constraint. */ + if (li->li_name == NULL) + return (1); + + lf = li->li_file; + + if (ldoi->ldoi_ar != NULL && li->li_lam != NULL && + !_wildcard_match(ldoi->ldoi_ar, lf->lf_name)) + return (0); + + assert(ldoi->ldoi_file != NULL); + if (!_wildcard_match(ldoi->ldoi_file, li->li_name)) + return (0); + + if (ldoi->ldoi_exclude != NULL && + _wildcard_list_match(ldoi->ldoi_exclude, li->li_name)) + return (0); + + return (1); +} + +static void +_record_wildcard_match(struct ld *ld, char *name, struct ld_output_section *os, + struct ld_output_element *oe) +{ + struct ld_wildcard_match *wm, *_wm; + + assert(name != NULL && os != NULL); + assert(oe != NULL && oe->oe_type == OET_INPUT_SECTION_LIST); + + HASH_FIND_STR(ld->ld_wm, name, wm); + + /* Create a new wildcard match. */ + if (wm == NULL) { + if ((wm = calloc(1, sizeof(*wm))) == NULL) + ld_fatal_std(ld, "calloc"); + if ((wm->wm_name = strdup(name)) == NULL) + ld_fatal_std(ld, "strdup"); + wm->wm_os = os; + wm->wm_islist = oe->oe_islist; + wm->wm_ldoi = oe->oe_entry; + wm->wm_next = NULL; + HASH_ADD_KEYPTR(hh, ld->ld_wm, wm->wm_name, + strlen(wm->wm_name), wm); + return; + } + + /* + * Wildcard match already exist, compare the "ldoi" to check + * if this is a new wildcard match with a different file/archive + * constraint. If so, Insert it to the tail of the wildcard match + * list. + */ + do { + if (oe->oe_entry == (void *) wm->wm_ldoi) + return; + } while (wm->wm_next != NULL && (wm = wm->wm_next)); + + if ((_wm = calloc(1, sizeof(*_wm))) == NULL) + ld_fatal_std(ld, "calloc"); + _wm->wm_os = os; + _wm->wm_islist = oe->oe_islist; + _wm->wm_ldoi = oe->oe_entry; + _wm->wm_next = NULL; + wm->wm_next = _wm; +} + +static void +_record_wildcard_no_match(struct ld *ld, char *name) +{ + struct ld_wildcard_match *wm; + + assert(name != NULL); + + HASH_FIND_STR(ld->ld_wm, name, wm); + + /* + * Unfortunately this section is an orphan section because + * it doesn't satisfy the file/archive constraint but does + * match certain section name wildcard. We can not record this. + */ + if (wm != NULL) + return; + + /* Create the wildcard "no-match" for the orphan. */ + if ((wm = calloc(1, sizeof(*wm))) == NULL) + ld_fatal_std(ld, "calloc"); + if ((wm->wm_name = strdup(name)) == NULL) + ld_fatal_std(ld, "strdup"); + wm->wm_no_match = 1; +} + +static void +_layout_input_sections(struct ld *ld, struct ld_input *li) +{ + struct ld_input_section *is; + struct ld_output *lo; + struct ld_output_section *os; + struct ld_output_element *oe; + struct ld_wildcard_match *wm; + struct ld_script_sections_output_input *ldoi; + int i; + + lo = ld->ld_output; + + for (i = 0; (size_t) i < li->li_shnum; i++) { + + is = &li->li_is[i]; + + if (is->is_type == SHT_NULL) + continue; + + /* Ignore discarded section groups. */ + if (is->is_discard) + continue; + + if (strcmp(is->is_name, ".shstrtab") == 0 || + strcmp(is->is_name, ".symtab") == 0 || + strcmp(is->is_name, ".strtab") == 0) + continue; + + /* Search the wildcard match table for a quick match. */ + HASH_FIND_STR(ld->ld_wm, is->is_name, wm); + if (wm != NULL) { + if (wm->wm_no_match) { + /* + * We found a "no-match". This is certainly + * an orphan section. + */ + _layout_orphan_section(ld, is); + continue; + } + } else + goto full_search; + + /* There is a match! Verify file/archive constraint. */ + while (wm != NULL) { + ldoi = wm->wm_ldoi; + + if (!_check_filename_constraint(li, ldoi)) + goto next_wm; + + if (strcmp(wm->wm_os->os_name, "/DISCARD/") == 0) { + is->is_discard = 1; + break; + } + + /* + * File/archive constraint satisfied. Insert the + * this section to the input section list of the + * output section element. + */ + _insert_input_to_output(ld, lo, wm->wm_os, is, + wm->wm_islist); + break; + + next_wm: + wm = wm->wm_next; + } + + if (wm != NULL) + continue; + + full_search: + + /* + * Otherwise, we have to do a full search for the section + * name in all the wildcard list. + */ + STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { + STAILQ_FOREACH(oe, &os->os_e, oe_next) { + + if (oe->oe_type != OET_INPUT_SECTION_LIST) + continue; + + /* + * Skip output sections created for orphan + * input sections. They don't have wildcard + * list. + */ + if ((ldoi = oe->oe_entry) == NULL) + continue; + + /* Check if the section name match wildcard */ + assert(ldoi->ldoi_sec != NULL); + if (!_wildcard_list_match(ldoi->ldoi_sec, + is->is_name)) + continue; + + /* + * Record this wildcard match to speed up + * wildcard match for sections with the same + * name. + */ + _record_wildcard_match(ld, is->is_name, os, + oe); + + /* Check file/archive constraint. */ + if (!_check_filename_constraint(li, ldoi)) { + continue; + } + + /* Check if we should discard the section. */ + if (strcmp(os->os_name, "/DISCARD/") == 0) { + is->is_discard = 1; + goto next_input_section; + } + + /* Match! Insert to the input section list. */ + _insert_input_to_output(ld, lo, os, is, + oe->oe_islist); + goto next_input_section; + } + } + + /* + * We found an orphan section. Record this so we can quickly + * identify other orphan sections with the same name. + */ + _record_wildcard_no_match(ld, is->is_name); + + /* Lay out the orphan section. */ + _layout_orphan_section(ld, is); + + next_input_section: + ; + } +} + +static void +_layout_orphan_section(struct ld *ld, struct ld_input_section *is) +{ + struct ld_input_section_head *islist; + struct ld_output *lo; + struct ld_output_element *oe; + struct ld_output_section *os, *_os; + + /* + * Layout the input sections that are not listed in the output + * section descriptor in the linker script. + */ + + lo = ld->ld_output; + + if (is->is_discard) + return; + + if (strcmp(is->is_name, ".shstrtab") == 0 || + strcmp(is->is_name, ".symtab") == 0 || + strcmp(is->is_name, ".strtab") == 0) + return; + + if ((is->is_type == SHT_REL || is->is_type == SHT_RELA) && + !is->is_dynrel) + return; + + /* + * When garbage collection is enabled (option `-gc-sections' + * specified), remove sections that are not used. + */ + if (ld->ld_gc) { + if ((is->is_flags & SHF_ALLOC) != 0 && !is->is_refed) { + if (ld->ld_gc_print) + ld_info(ld, "Remove unused ection `%s' in " + "file %s", is->is_name, + ld_input_get_fullname(ld, is->is_input)); + return; + } + } + + HASH_FIND_STR(lo->lo_ostbl, is->is_name, os); + if (os != NULL) { + oe = STAILQ_FIRST(&os->os_e); + assert(oe != NULL && + oe->oe_type == OET_INPUT_SECTION_LIST); + _insert_input_to_output(ld, lo, os, is, oe->oe_islist); + return; + } + + /* + * Create a new output secton and put it in a proper place, + * based on the section flag. + */ + _os = ld_layout_insert_output_section(ld, is->is_name, + is->is_flags); + + if ((islist = calloc(1, sizeof(*islist))) == NULL) + ld_fatal_std(ld, "calloc"); + STAILQ_INIT(islist); + + oe = ld_output_create_section_element(ld, _os, OET_INPUT_SECTION_LIST, + NULL, NULL); + oe->oe_islist = islist; + _insert_input_to_output(ld, lo, _os, is, oe->oe_islist); +} + +struct ld_output_section * +ld_layout_insert_output_section(struct ld *ld, const char *name, + uint64_t flags) +{ + struct ld_output *lo; + struct ld_output_section *os, *_os; + + lo = ld->ld_output; + assert(lo != NULL); + + STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { + if ((os->os_flags & SHF_ALLOC) != (flags & SHF_ALLOC)) + continue; + + if (os->os_flags == flags) { + _os = STAILQ_NEXT(os, os_next); + if (_os == NULL || _os->os_flags != flags) + break; + } + + _os = STAILQ_NEXT(os, os_next); + if (_os == NULL && + (_os->os_flags & SHF_ALLOC) != (flags & SHF_ALLOC)) + break; + } + + _os = ld_output_alloc_section(ld, name, os, NULL); + _os->os_flags |= flags & SHF_ALLOC; + + return (_os); +} + +static void +_insert_input_to_output(struct ld *ld, struct ld_output *lo, + struct ld_output_section *os, struct ld_input_section *is, + struct ld_input_section_head *islist) +{ + struct ld_output_section *_os; + char *name; + int len; + + /* + * Relocation sections is handled separately. + */ + if ((is->is_type == SHT_REL || is->is_type == SHT_RELA) && + !is->is_dynrel) + return; + + os->os_empty = 0; + + os->os_flags |= is->is_flags & (SHF_EXECINSTR | SHF_WRITE | SHF_TLS); + os->os_dynrel |= is->is_dynrel; + os->os_pltrel |= is->is_pltrel; + + if (!is->is_dynrel && !is->is_pltrel && is->is_type != SHT_NOBITS && + is->is_size != 0) + is->is_need_reloc = 1; + + if (is->is_align > os->os_align) + os->os_align = is->is_align; + + /* + * The entsize of the output section is determined by the + * input sections it contains. If all the input sections has + * the same entsize, the output section will also have that + * entsize. If any input section has a different entsize, + * the entsize for output section is set to 0, meaning that + * it has variable entry sizes. + */ + if (!os->os_entsize_set) { + os->os_entsize = is->is_entsize; + os->os_entsize_set = 1; + } else if (os->os_entsize != is->is_entsize) + os->os_entsize = 0; + + if (os->os_type == SHT_NULL) + os->os_type = is->is_type; + if (is->is_type == SHT_NOTE) + lo->lo_phdr_note = 1; + + is->is_output = os; + + STAILQ_INSERT_TAIL(islist, is, is_next); + + /* + * Lay out relocation section for this input section if the linker + * creates relocatable output object or if -emit-relocs option is + * sepcified. + */ + if ((ld->ld_reloc || ld->ld_emit_reloc) && is->is_ris != NULL && + is->is_ris->is_num_reloc > 0) { + if (os->os_r == NULL) { + /* + * Create relocation section for output sections. + */ + if (ld->ld_arch->reloc_is_rela) { + len = strlen(os->os_name) + 6; + if ((name = malloc(len)) == NULL) + ld_fatal_std(ld, "malloc"); + snprintf(name, len, ".rela%s", os->os_name); + } else { + len = strlen(os->os_name) + 5; + if ((name = malloc(len)) == NULL) + ld_fatal_std(ld, "malloc"); + snprintf(name, len, ".rel%s", os->os_name); + } + _os = ld_output_alloc_section(ld, name, NULL, os); + _os->os_rel = 1; + + /* + * Fill in entry size, alignment and type for output + * relocation sections. + */ + _os->os_entsize = ld->ld_arch->reloc_entsize; + _os->os_type = ld->ld_arch->reloc_is_rela ? SHT_RELA : + SHT_REL; + _os->os_align = ld->ld_arch->reloc_is_64bit ? 8 : 4; + + /* Setup sh_link and sh_info. */ + if ((_os->os_link = strdup(".symtab")) == NULL) + ld_fatal_std(ld, "strdup"); + _os->os_info = os; + + /* Relocation sections are not allocated in memory. */ + _os->os_addr = 0; + } else + _os = os->os_r; + + _os->os_size += is->is_ris->is_num_reloc * _os->os_entsize; + } + +} + +static void +_parse_output_section_descriptor(struct ld *ld, struct ld_output_section *os) +{ + struct ld_script_sections_output *ldso; + + if ((ldso = os->os_ldso) == NULL) + return; + + if (ldso->ldso_vma != NULL) + os->os_addr = ld_exp_eval(ld, ldso->ldso_vma); + + if (ldso->ldso_lma != NULL) + os->os_lma = ld_exp_eval(ld, ldso->ldso_lma); + + if (ldso->ldso_align != NULL) + os->os_align = ld_exp_eval(ld, ldso->ldso_align); + + /* TODO: handle other output section parameters. */ +} + +static void +_calc_offset(struct ld *ld) +{ + struct ld_state *ls; + struct ld_output *lo; + struct ld_output_element *oe; + + ls = &ld->ld_state; + lo = ld->ld_output; + ls->ls_loc_counter = 0; + ls->ls_offset = ld_layout_calc_header_size(ld); + ls->ls_first_output_sec = 1; + + STAILQ_FOREACH(oe, &lo->lo_oelist, oe_next) { + switch (oe->oe_type) { + case OET_ASSERT: + /* TODO */ + break; + case OET_ASSIGN: + ld_script_process_assign(ld, oe->oe_entry); + break; + case OET_ENTRY: + ld_script_process_entry(ld, oe->oe_entry); + break; + case OET_OUTPUT_SECTION: + _parse_output_section_descriptor(ld, oe->oe_entry); + _calc_output_section_offset(ld, oe->oe_entry); + break; + default: + break; + } + } + + /* Emit .note.GNU-stack section for reloctable output object. */ + if (ld->ld_gen_gnustack && ld->ld_reloc) + ld_output_emit_gnu_stack_section(ld); + + /* Lay out section header table after normal input sections. */ + _calc_shdr_offset(ld); + + /* Create .shstrtab section and put it after section header table. */ + ld_output_create_string_table_section(ld, ".shstrtab", + ld->ld_shstrtab, NULL); + + /* Lay out relocation sections. */ + if (ld->ld_reloc || ld->ld_emit_reloc) + _calc_reloc_section_offset(ld, lo); +} + +static void +_calc_output_section_offset(struct ld *ld, struct ld_output_section *os) +{ + struct ld_state *ls; + struct ld_output_element *oe; + struct ld_output_data_buffer *odb; + struct ld_input_section *is; + struct ld_input_section_head *islist; + struct ld_symbol_table *sy; + struct ld_strtab *st; + uint64_t addr; + + /* Relocation sections are handled separately. */ + if (os->os_rel) + return; + + ls = &ld->ld_state; + + /* + * Position independent output object should have VMA from 0. + * So if we are building a DSO or PIE, and this output section is + * the first one, we should set current VMA to SIZEOF_HEADERS + * and ignore all the previous assignments to the location counter. + */ + if ((ld->ld_dso || ld->ld_pie) && ls->ls_first_output_sec) { + ls->ls_loc_counter = ld_layout_calc_header_size(ld); + if (!os->os_empty) + ls->ls_first_output_sec = 0; + } + + /* + * Location counter stores the end VMA offset of the previous output + * section. We use that value as the base VMA offset for this output + * section. + */ + addr = ls->ls_loc_counter; + + /* + * Location counter when refered inside an output section descriptor, + * is an offset relative to the start of the section. + */ + ls->ls_loc_counter = 0; + + STAILQ_FOREACH(oe, &os->os_e, oe_next) { + switch (oe->oe_type) { + case OET_ASSERT: + /* TODO */ + break; + case OET_ASSIGN: + ld_script_process_assign(ld, oe->oe_entry); + break; + case OET_DATA: + /* TODO */ + break; + case OET_DATA_BUFFER: + odb = oe->oe_entry; + odb->odb_off = roundup(ls->ls_loc_counter, + odb->odb_align); + ls->ls_loc_counter = odb->odb_off + odb->odb_size; + break; + case OET_ENTRY: + ld_script_process_entry(ld, oe->oe_entry); + break; + case OET_INPUT_SECTION_LIST: + islist = oe->oe_islist; + STAILQ_FOREACH(is, islist, is_next) { + if (is->is_size == 0) + continue; + is->is_reloff = roundup(ls->ls_loc_counter, + is->is_align); +#if 0 + printf("\t%s(%s): %#jx,%#jx(%#jx)\n", + is->is_input->li_name, + is->is_name, is->is_reloff, + is->is_size, is->is_align); +#endif + ls->ls_loc_counter = is->is_reloff + + is->is_size; + } + break; + case OET_KEYWORD: + /* TODO */ + break; + case OET_SYMTAB: + assert(ls->ls_loc_counter == 0); + sy = oe->oe_entry; + ls->ls_loc_counter = sy->sy_size * os->os_entsize; + break; + case OET_STRTAB: + assert(ls->ls_loc_counter == 0); + st = oe->oe_entry; + ls->ls_loc_counter = ld_strtab_getsize(st); + break; + default: + break; + } + } + + /* + * Properly align section vma and offset to the required section + * alignment. + */ + + if ((os->os_flags & SHF_ALLOC) != 0 && !ld->ld_reloc) { + if (os->os_ldso == NULL || os->os_ldso->ldso_vma == NULL) + os->os_addr = roundup(addr, os->os_align); + } else + os->os_addr = 0; + + os->os_off = roundup(ls->ls_offset, os->os_align); + os->os_size = ls->ls_loc_counter; + +#if 0 + printf("layout output section %s: (off:%#jx,size:%#jx) " + "vma:%#jx,align:%#jx\n", os->os_name, os->os_off, os->os_size, + os->os_addr, os->os_align); +#endif + + /* + * Calculate the file offset for the next output section. Note that + * only sections with type other than SHT_NOBITS consume file space. + */ + ls->ls_offset = os->os_off; + if (os->os_type != SHT_NOBITS) + ls->ls_offset += os->os_size; + + /* Reset location counter to the current VMA. */ + if (os->os_flags & SHF_ALLOC) { + ls->ls_loc_counter = os->os_addr; + /* + * Do not allocate VMA for TLS .tbss sections. TLS sections + * are only used as an initialization image and .tbss section + * will not be allocated in memory. + */ + if (os->os_type != SHT_NOBITS || (os->os_flags & SHF_TLS) == 0) + ls->ls_loc_counter += os->os_size; + } +} + +static void +_calc_reloc_section_offset(struct ld *ld, struct ld_output *lo) +{ + struct ld_state *ls; + struct ld_output_section *os, *_os; + + ls = &ld->ld_state; + + STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { + if (os->os_r != NULL) { + _os = os->os_r; + _os->os_off = roundup(ls->ls_offset, _os->os_align); + ls->ls_offset = _os->os_off + _os->os_size; + } + } +} + +static void +_calc_shdr_offset(struct ld *ld) +{ + struct ld_state *ls; + struct ld_output *lo; + struct ld_output_section *os; + uint64_t shoff; + int n; + + ls = &ld->ld_state; + lo = ld->ld_output; + + if (lo->lo_ec == ELFCLASS32) + shoff = roundup(ls->ls_offset, 4); + else + shoff = roundup(ls->ls_offset, 8); + + ls->ls_offset = shoff; + + n = 0; + STAILQ_FOREACH(os, &lo->lo_oslist, os_next) { + if (os->os_scn != NULL) + n++; + } + + /* TODO: n + 2 if ld(1) will not create symbol table. */ + ls->ls_offset += gelf_fsize(lo->lo_elf, ELF_T_SHDR, n + 4, EV_CURRENT); + + lo->lo_shoff = shoff; +} |