summaryrefslogtreecommitdiff
path: root/ld/ld_layout.c
diff options
context:
space:
mode:
Diffstat (limited to 'ld/ld_layout.c')
-rw-r--r--ld/ld_layout.c1254
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;
+}