888a18208d
This is close to the upcoming 0.7.1 release. From http://svn.code.sf.net/p/elftoolchain/code
1253 lines
30 KiB
C
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;
|
|
}
|