summaryrefslogtreecommitdiff
path: root/ld/ld_script.c
diff options
context:
space:
mode:
authorKai Wang <kaiw@FreeBSD.org>2014-01-15 08:43:20 +0000
committerKai Wang <kaiw@FreeBSD.org>2014-01-15 08:43:20 +0000
commit5265ace0e440a23fb522c516f4ee20f43eaed2b3 (patch)
tree13068447bb61372f7540b168e913b8eb88ef7578 /ld/ld_script.c
Diffstat (limited to 'ld/ld_script.c')
-rw-r--r--ld/ld_script.c746
1 files changed, 746 insertions, 0 deletions
diff --git a/ld/ld_script.c b/ld/ld_script.c
new file mode 100644
index 0000000000000..1e3d104374633
--- /dev/null
+++ b/ld/ld_script.c
@@ -0,0 +1,746 @@
+/*-
+ * 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_exp.h"
+#include "ld_options.h"
+#include "ld_script.h"
+#include "ld_file.h"
+#include "ld_symbols.h"
+
+ELFTC_VCSID("$Id: ld_script.c 2881 2013-01-09 22:46:54Z kaiwang27 $");
+
+static void _input_file_add(struct ld *ld, struct ld_script_input_file *ldif);
+static void _overlay_section_free(void *ptr);
+static struct ld_script_variable *_variable_find(struct ld *ld, char *name);
+
+#define _variable_add(v) \
+ HASH_ADD_KEYPTR(hh, ld->ld_scp->lds_v, (v)->ldv_name, \
+ strlen((v)->ldv_name), (v))
+
+struct ld_script_cmd *
+ld_script_assert(struct ld *ld, struct ld_exp *exp, char *msg)
+{
+ struct ld_script_assert *a;
+
+ if ((a = calloc(1, sizeof(*a))) == NULL)
+ ld_fatal_std(ld, "calloc");
+ a->lda_exp = exp;
+ a->lda_msg = msg;
+
+ return (ld_script_cmd(ld, LSC_ASSERT, a));
+}
+
+struct ld_script_assign *
+ld_script_assign(struct ld *ld, struct ld_exp *var, enum ld_script_assign_op op,
+ struct ld_exp *val, unsigned provide, unsigned hidden)
+{
+ struct ld_script_assign *lda;
+ struct ld_script_variable *ldv;
+
+ if ((lda = calloc(1, sizeof(*lda))) == NULL)
+ ld_fatal_std(ld, "calloc");
+
+ lda->lda_var = var;
+ lda->lda_op = op;
+ lda->lda_val = val;
+ lda->lda_provide = provide;
+
+ if ((ldv = _variable_find(ld, var->le_name)) == NULL) {
+ ldv = calloc(1, sizeof(*ldv));
+ if ((ldv->ldv_name = strdup(var->le_name)) == NULL)
+ ld_fatal_std(ld, "strdup");
+ _variable_add(ldv);
+ if (*var->le_name != '.')
+ ld_symbols_add_variable(ld, ldv, provide, hidden);
+ }
+
+ return (lda);
+}
+
+void
+ld_script_assign_dump(struct ld *ld, struct ld_script_assign *lda)
+{
+
+ printf("%16s", "");
+ printf("0x%016jx ", (uintmax_t) lda->lda_res);
+
+ if (lda->lda_provide)
+ printf("PROVIDE(");
+
+ ld_exp_dump(ld, lda->lda_var);
+
+ switch (lda->lda_op) {
+ case LSAOP_ADD_E:
+ printf(" += ");
+ break;
+ case LSAOP_AND_E:
+ printf(" &= ");
+ break;
+ case LSAOP_DIV_E:
+ printf(" /= ");
+ break;
+ case LSAOP_E:
+ printf(" = ");
+ break;
+ case LSAOP_LSHIFT_E:
+ printf(" <<= ");
+ break;
+ case LSAOP_MUL_E:
+ printf(" *= ");
+ break;
+ case LSAOP_OR_E:
+ printf(" |= ");
+ break;
+ case LSAOP_RSHIFT_E:
+ printf(" >>= ");
+ break;
+ case LSAOP_SUB_E:
+ printf(" -= ");
+ break;
+ default:
+ ld_fatal(ld, "internal: unknown assignment op: %d",
+ lda->lda_op);
+ }
+
+ ld_exp_dump(ld, lda->lda_val);
+
+ if (lda->lda_provide)
+ printf(")");
+
+ printf("\n");
+}
+
+void
+ld_script_assign_free(struct ld_script_assign *lda)
+{
+
+ if (lda == NULL)
+ return;
+ ld_exp_free(lda->lda_var);
+ ld_exp_free(lda->lda_val);
+ free(lda);
+}
+
+void
+ld_script_process_assign(struct ld *ld, struct ld_script_assign *lda)
+{
+ struct ld_state *ls;
+ struct ld_exp *var;
+ struct ld_script_variable *ldv;
+
+ ls = &ld->ld_state;
+ var = lda->lda_var;
+ ldv = _variable_find(ld, var->le_name);
+ assert(ldv != NULL);
+
+ ldv->ldv_val = ld_exp_eval(ld, lda->lda_val);
+ if (*var->le_name == '.') {
+ /*
+ * TODO: location counter is allowed to move backwards
+ * outside output section descriptor, as long as the
+ * move will not cause overlapping LMA's.
+ */
+ if ((uint64_t) ldv->ldv_val < ls->ls_loc_counter)
+ ld_fatal(ld, "cannot move location counter backwards"
+ " from %#jx to %#jx",
+ (uintmax_t) ls->ls_loc_counter,
+ (uintmax_t) ldv->ldv_val);
+ ls->ls_loc_counter = (uint64_t) ldv->ldv_val;
+ }
+ lda->lda_res = ldv->ldv_val;
+}
+
+void
+ld_script_process_entry(struct ld *ld, char *name)
+{
+
+ if (ld->ld_scp->lds_entry_point != NULL) {
+ free(ld->ld_scp->lds_entry_point);
+ ld->ld_scp->lds_entry_point = NULL;
+ }
+
+ ld->ld_scp->lds_entry_point = strdup(name);
+ if (ld->ld_scp->lds_entry_point == NULL)
+ ld_fatal_std(ld, "strdup");
+}
+
+int64_t
+ld_script_variable_value(struct ld *ld, char *name)
+{
+ struct ld_script_variable *ldv;
+ struct ld_state *ls;
+
+ ls = &ld->ld_state;
+ if (*name == '.')
+ return (ls->ls_loc_counter);
+
+ ldv = _variable_find(ld, name);
+ assert(ldv != NULL);
+
+ return (ldv->ldv_val);
+}
+
+struct ld_script_cmd *
+ld_script_cmd(struct ld *ld, enum ld_script_cmd_type type, void *cmd)
+{
+ struct ld_script_cmd *ldc;
+
+ if ((ldc = calloc(1, sizeof(*ldc))) == NULL)
+ ld_fatal_std(ld, "calloc");
+ ldc->ldc_type = type;
+ ldc->ldc_cmd = cmd;
+
+ return (ldc);
+}
+
+void
+ld_script_cmd_insert(struct ld_script_cmd_head *head, struct ld_script_cmd *ldc)
+{
+
+ STAILQ_INSERT_TAIL(head, ldc, ldc_next);
+}
+
+static void
+_overlay_section_free(void *ptr)
+{
+ struct ld_script_cmd *c, *_c;
+ struct ld_script_sections_overlay_section *ldos;
+
+ ldos = ptr;
+ if (ldos == NULL)
+ return;
+ free(ldos->ldos_name);
+ ld_script_list_free(ldos->ldos_phdr, free);
+ ld_exp_free(ldos->ldos_fill);
+ STAILQ_FOREACH_SAFE(c, &ldos->ldos_c, ldc_next, _c) {
+ STAILQ_REMOVE(&ldos->ldos_c, c, ld_script_cmd, ldc_next);
+ ld_script_cmd_free(c);
+ }
+ free(ldos);
+}
+
+void
+ld_script_cmd_free(struct ld_script_cmd *ldc)
+{
+ struct ld_script_cmd *c, *_c;
+ struct ld_script_assert *lda;
+ struct ld_script_sections *ldss;
+ struct ld_script_sections_output *ldso;
+ struct ld_script_sections_output_data *ldod;
+ struct ld_script_sections_output_input *ldoi;
+ struct ld_script_sections_overlay *ldso2;
+
+ switch (ldc->ldc_type) {
+ case LSC_ASSERT:
+ lda = ldc->ldc_cmd;
+ ld_exp_free(lda->lda_exp);
+ free(lda->lda_msg);
+ free(lda);
+ break;
+
+ case LSC_ASSIGN:
+ ld_script_assign_free(ldc->ldc_cmd);
+ break;
+
+ case LSC_ENTRY:
+ free(ldc->ldc_cmd);
+ break;
+
+ case LSC_SECTIONS:
+ ldss = ldc->ldc_cmd;
+ STAILQ_FOREACH_SAFE(c, &ldss->ldss_c, ldc_next, _c) {
+ STAILQ_REMOVE(&ldss->ldss_c, c, ld_script_cmd,
+ ldc_next);
+ ld_script_cmd_free(c);
+ }
+ free(ldss);
+ break;
+
+ case LSC_SECTIONS_OUTPUT:
+ ldso = ldc->ldc_cmd;
+ free(ldso->ldso_name);
+ free(ldso->ldso_type);
+ ld_exp_free(ldso->ldso_vma);
+ ld_exp_free(ldso->ldso_lma);
+ ld_exp_free(ldso->ldso_align);
+ ld_exp_free(ldso->ldso_subalign);
+ free(ldso->ldso_constraint);
+ free(ldso->ldso_region);
+ free(ldso->ldso_lma_region);
+ ld_script_list_free(ldso->ldso_phdr, free);
+ ld_exp_free(ldso->ldso_fill);
+ STAILQ_FOREACH_SAFE(c, &ldso->ldso_c, ldc_next, _c) {
+ STAILQ_REMOVE(&ldso->ldso_c, c, ld_script_cmd,
+ ldc_next);
+ ld_script_cmd_free(c);
+ }
+ free(ldso);
+ break;
+
+ case LSC_SECTIONS_OUTPUT_DATA:
+ ldod = ldc->ldc_cmd;
+ ld_exp_free(ldod->ldod_exp);
+ free(ldod);
+ break;
+
+ case LSC_SECTIONS_OUTPUT_INPUT:
+ ldoi = ldc->ldc_cmd;
+ ld_wildcard_free(ldoi->ldoi_ar);
+ ld_wildcard_free(ldoi->ldoi_file);
+ ld_script_list_free(ldoi->ldoi_exclude, ld_wildcard_free);
+ ld_script_list_free(ldoi->ldoi_sec, ld_wildcard_free);
+ free(ldoi);
+ break;
+
+ case LSC_SECTIONS_OVERLAY:
+ ldso2 = ldc->ldc_cmd;
+ ld_exp_free(ldso2->ldso_vma);
+ ld_exp_free(ldso2->ldso_lma);
+ free(ldso2->ldso_region);
+ ld_script_list_free(ldso2->ldso_phdr, free);
+ ld_exp_free(ldso2->ldso_fill);
+ ld_script_list_free(ldso2->ldso_s, _overlay_section_free);
+ free(ldso2);
+ break;
+
+ default:
+ break;
+ }
+
+ free(ldc);
+}
+
+void
+ld_script_extern(struct ld *ld, struct ld_script_list *list)
+{
+ struct ld_script_list *ldl;
+
+ ldl = list;
+ while (ldl != NULL) {
+ ld_symbols_add_extern(ld, ldl->ldl_entry);
+ ldl = ldl->ldl_next;
+ }
+ ld_script_list_free(list, free);
+}
+
+void
+ld_script_group(struct ld *ld, struct ld_script_list *list)
+{
+ struct ld_script_list *ldl;
+
+ ld->ld_state.ls_group_level++;
+ if (ld->ld_state.ls_group_level > LD_MAX_NESTED_GROUP)
+ ld_fatal(ld, "too many nested archive groups");
+ ldl = list;
+ while (ldl != NULL) {
+ _input_file_add(ld, ldl->ldl_entry);
+ ldl = ldl->ldl_next;
+ }
+ ld->ld_state.ls_group_level--;
+ ld_script_list_free(list, free);
+}
+
+void
+ld_script_init(struct ld *ld)
+{
+
+ ld->ld_scp = calloc(1, sizeof(*ld->ld_scp));
+ if (ld->ld_scp == NULL)
+ ld_fatal_std(ld, "calloc");
+
+ STAILQ_INIT(&ld->ld_scp->lds_a);
+ STAILQ_INIT(&ld->ld_scp->lds_c);
+ STAILQ_INIT(&ld->ld_scp->lds_n);
+ STAILQ_INIT(&ld->ld_scp->lds_p);
+ STAILQ_INIT(&ld->ld_scp->lds_r);
+ STAILQ_INIT(&ld->ld_scp->lds_vn);
+
+ ld_script_parse_internal();
+}
+
+void
+ld_script_cleanup(struct ld *ld)
+{
+ struct ld_script *lds;
+ struct ld_script_phdr *p, *_p;
+ struct ld_script_region *r, *_r;
+ struct ld_script_region_alias *a, *_a;
+ struct ld_script_nocrossref *n, *_n;
+ struct ld_script_cmd *c, *_c;
+ struct ld_script_variable *v, *_v;
+
+ if (ld->ld_scp == NULL)
+ return;
+
+ lds = ld->ld_scp;
+
+ if (lds->lds_entry_point != NULL) {
+ free(lds->lds_entry_point);
+ lds->lds_entry_point = NULL;
+ }
+
+ STAILQ_FOREACH_SAFE(p, &lds->lds_p, ldsp_next, _p) {
+ STAILQ_REMOVE(&lds->lds_p, p, ld_script_phdr, ldsp_next);
+ free(p->ldsp_name);
+ free(p->ldsp_type);
+ ld_exp_free(p->ldsp_addr);
+ free(p);
+ }
+
+ STAILQ_FOREACH_SAFE(r, &lds->lds_r, ldsr_next, _r) {
+ STAILQ_REMOVE(&lds->lds_r, r, ld_script_region, ldsr_next);
+ free(r->ldsr_name);
+ free(r->ldsr_attr);
+ ld_exp_free(r->ldsr_origin);
+ ld_exp_free(r->ldsr_len);
+ free(r);
+ }
+
+ STAILQ_FOREACH_SAFE(a, &lds->lds_a, ldra_next, _a) {
+ STAILQ_REMOVE(&lds->lds_a, a, ld_script_region_alias,
+ ldra_next);
+ free(a->ldra_alias);
+ free(a->ldra_region);
+ free(a);
+ }
+
+ STAILQ_FOREACH_SAFE(n, &lds->lds_n, ldn_next, _n) {
+ STAILQ_REMOVE(&lds->lds_n, n, ld_script_nocrossref, ldn_next);
+ ld_script_list_free(n->ldn_l, free);
+ free(n);
+ }
+
+ STAILQ_FOREACH_SAFE(c, &lds->lds_c, ldc_next, _c) {
+ STAILQ_REMOVE(&lds->lds_c, c, ld_script_cmd, ldc_next);
+ ld_script_cmd_free(c);
+ }
+
+ if (lds->lds_v != NULL) {
+ HASH_ITER(hh, lds->lds_v, v, _v) {
+ HASH_DEL(lds->lds_v, v);
+ free(v->ldv_name);
+ free(v);
+ }
+ lds->lds_v = NULL;
+ }
+}
+
+void
+ld_script_input(struct ld *ld, struct ld_script_list *list)
+{
+ struct ld_script_list *ldl;
+
+ ld->ld_state.ls_search_dir = 1;
+ ldl = list;
+ while (ldl != NULL) {
+ _input_file_add(ld, ldl->ldl_entry);
+ ldl = ldl->ldl_next;
+ }
+ ld->ld_state.ls_search_dir = 0;
+ ld_script_list_free(list, free);
+}
+
+struct ld_script_input_file *
+ld_script_input_file(struct ld *ld, unsigned as_needed, void *in)
+{
+ struct ld_script_input_file *ldif;
+
+ if ((ldif = calloc(1, sizeof(*ldif))) == NULL)
+ ld_fatal_std(ld, "calloc");
+ ldif->ldif_as_needed = as_needed;
+ if (as_needed)
+ ldif->ldif_u.ldif_ldl = in;
+ else
+ ldif->ldif_u.ldif_name = in;
+
+ return (ldif);
+}
+
+struct ld_script_list *
+ld_script_list(struct ld *ld, struct ld_script_list *list, void *entry)
+{
+ struct ld_script_list *ldl;
+
+ if ((ldl = malloc(sizeof(*ldl))) == NULL)
+ ld_fatal_std(ld, "malloc");
+ ldl->ldl_entry = entry;
+ ldl->ldl_next = list;
+
+ return (ldl);
+}
+
+void
+ld_script_list_free(struct ld_script_list *list, void (*_free)(void *ptr))
+{
+ struct ld_script_list *ldl;
+
+ if (list == NULL)
+ return;
+
+ do {
+ ldl = list;
+ list = ldl->ldl_next;
+ if (ldl->ldl_entry)
+ _free(ldl->ldl_entry);
+ free(ldl);
+ } while (list != NULL);
+}
+
+struct ld_script_list *
+ld_script_list_reverse(struct ld_script_list *list)
+{
+ struct ld_script_list *root, *next;
+
+ root = NULL;
+ while (list != NULL) {
+ next = list->ldl_next;
+ list->ldl_next = root;
+ root = list;
+ list = next;
+ }
+
+ return (root);
+}
+
+void
+ld_script_nocrossrefs(struct ld *ld, struct ld_script_list *list)
+{
+ struct ld_script_nocrossref *ldn;
+
+ if ((ldn = calloc(1, sizeof(*ldn))) == NULL)
+ ld_fatal_std(ld, "calloc");
+ ldn->ldn_l = list;
+ STAILQ_INSERT_TAIL(&ld->ld_scp->lds_n, ldn, ldn_next);
+}
+
+struct ld_script_phdr *
+ld_script_phdr(struct ld *ld, char *name, char *type, unsigned filehdr,
+ unsigned phdrs, struct ld_exp *addr, unsigned flags)
+{
+ struct ld_script_phdr *ldsp;
+
+ if ((ldsp = calloc(1, sizeof(*ldsp))) == NULL)
+ ld_fatal_std(ld, "calloc");
+
+ ldsp->ldsp_name = name;
+ ldsp->ldsp_type = type;
+ ldsp->ldsp_filehdr = filehdr;
+ ldsp->ldsp_phdrs = phdrs;
+ ldsp->ldsp_addr = addr;
+ ldsp->ldsp_flags = flags;
+
+ return (ldsp);
+}
+
+struct ld_script_region *
+ld_script_region(struct ld *ld, char *name, char *attr, struct ld_exp *origin,
+ struct ld_exp *len)
+{
+ struct ld_script_region *ldsr;
+
+ if ((ldsr = malloc(sizeof(*ldsr))) == NULL)
+ ld_fatal_std(ld, "malloc");
+
+ ldsr->ldsr_name = name;
+ ldsr->ldsr_attr = attr;
+ ldsr->ldsr_origin = origin;
+ ldsr->ldsr_len = len;
+
+ return (ldsr);
+}
+
+void
+ld_script_region_alias(struct ld *ld, char *alias, char *region)
+{
+ struct ld_script_region_alias *ldra;
+
+ if ((ldra = calloc(1, sizeof(*ldra))) == NULL)
+ ld_fatal_std(ld, "calloc");
+
+ ldra->ldra_alias = alias;
+ ldra->ldra_region = region;
+
+ STAILQ_INSERT_TAIL(&ld->ld_scp->lds_a, ldra, ldra_next);
+}
+
+void
+ld_script_version_add_node(struct ld *ld, char *ver, void *head, char *depend)
+{
+ struct ld_script_version_node *ldvn;
+
+ if ((ldvn = calloc(1, sizeof(*ldvn))) == NULL)
+ ld_fatal_std(ld, "calloc");
+
+ ldvn->ldvn_name = ver;
+ if (ldvn->ldvn_name == NULL) {
+ /*
+ * Version name can be omitted only when this is the only
+ * node in the version script.
+ */
+ if (ld->ld_scp->lds_vn_name_omitted ||
+ !STAILQ_EMPTY(&ld->ld_scp->lds_vn))
+ ld_fatal(ld, "version script can only have one "
+ "version node that is without a version name");
+ ld->ld_scp->lds_vn_name_omitted = 1;
+ }
+
+ ldvn->ldvn_dep = depend;
+ ldvn->ldvn_e = head;
+
+ STAILQ_INSERT_TAIL(&ld->ld_scp->lds_vn, ldvn, ldvn_next);
+}
+
+struct ld_script_version_entry *
+ld_script_version_alloc_entry(struct ld *ld, char *sym, void *extern_block)
+{
+ struct ld_state *ls;
+ struct ld_script_version_entry *ldve;
+ int ignore;
+ char *p;
+
+ ls = &ld->ld_state;
+
+ if ((ldve = calloc(1, sizeof(*ldve))) == NULL)
+ ld_fatal_std(ld, "calloc");
+
+ ldve->ldve_sym = sym;
+ ldve->ldve_local = ls->ls_version_local;
+ ldve->ldve_list = extern_block;
+
+ if (ldve->ldve_sym == NULL)
+ return (ldve);
+
+ ignore = 0;
+ for (p = ldve->ldve_sym; *p != '\0'; p++) {
+ switch (*p) {
+ case '\\':
+ /* Ignore the next char */
+ ignore = 1;
+ break;
+ case '?':
+ case '*':
+ case '[':
+ if (!ignore) {
+ ldve->ldve_glob = 1;
+ goto done;
+ } else
+ ignore = 0;
+ }
+ }
+
+done:
+ return (ldve);
+}
+
+void *
+ld_script_version_link_entry(struct ld *ld,
+ struct ld_script_version_entry_head *head,
+ struct ld_script_version_entry *ldve)
+{
+
+ if (ldve == NULL)
+ return (head);
+
+ if (head == NULL) {
+ if ((head = calloc(1, sizeof(*head))) == NULL)
+ ld_fatal_std(ld, "calloc");
+ STAILQ_INIT(head);
+ }
+
+ if (ldve->ldve_list != NULL) {
+ STAILQ_CONCAT(head, ldve->ldve_list);
+ free(ldve->ldve_list);
+ free(ldve);
+ } else
+ STAILQ_INSERT_TAIL(head, ldve, ldve_next);
+
+ return (head);
+}
+
+void
+ld_script_version_set_lang(struct ld * ld,
+ struct ld_script_version_entry_head *head, char *lang)
+{
+ struct ld_script_version_entry *ldve;
+ enum ld_script_version_lang vl;
+
+ vl = VL_C;
+
+ if (!strcasecmp(lang, "c"))
+ vl = VL_C;
+ else if (!strcasecmp(lang, "c++"))
+ vl = VL_CPP;
+ else if (!strcasecmp(lang, "java"))
+ vl = VL_JAVA;
+ else
+ ld_warn(ld, "unrecognized language `%s' in version script",
+ lang);
+
+ STAILQ_FOREACH(ldve, head, ldve_next) {
+ /* Do not overwrite lang set by inner extern blocks. */
+ if (!ldve->ldve_lang_set) {
+ ldve->ldve_lang = vl;
+ ldve->ldve_lang_set = 1;
+ }
+ }
+}
+
+
+static void
+_input_file_add(struct ld *ld, struct ld_script_input_file *ldif)
+{
+ struct ld_state *ls;
+ struct ld_script_list *ldl;
+ unsigned old_as_needed;
+
+ ls = &ld->ld_state;
+
+ if (!ldif->ldif_as_needed) {
+ ld_file_add(ld, ldif->ldif_u.ldif_name, LFT_UNKNOWN);
+ free(ldif->ldif_u.ldif_name);
+ } else {
+ old_as_needed = ls->ls_as_needed;
+ ls->ls_as_needed = 1;
+ ldl = ldif->ldif_u.ldif_ldl;
+ while (ldl != NULL) {
+ ld_file_add(ld, ldl->ldl_entry, LFT_UNKNOWN);
+ ldl = ldl->ldl_next;
+ }
+ ls->ls_as_needed = old_as_needed;
+ ld_script_list_free(ldif->ldif_u.ldif_ldl, free);
+ }
+}
+
+static struct ld_script_variable *
+_variable_find(struct ld *ld, char *name)
+{
+ struct ld_script_variable *ldv;
+
+ HASH_FIND_STR(ld->ld_scp->lds_v, name, ldv);
+
+ return (ldv);
+}