freebsd-nq/ld/ld_layout.c
2016-02-10 19:39:36 +00:00

1253 lines
30 KiB
C

/*-
* 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 3276 2015-12-11 21:39:06Z 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;
int sections_cmd_exist;
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;
}