diff options
Diffstat (limited to 'ld/ld_arch.c')
-rw-r--r-- | ld/ld_arch.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/ld/ld_arch.c b/ld/ld_arch.c new file mode 100644 index 0000000000000..33baa132aaab1 --- /dev/null +++ b/ld/ld_arch.c @@ -0,0 +1,209 @@ +/*- + * Copyright (c) 2011,2012 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 "i386.h" +#include "amd64.h" + +ELFTC_VCSID("$Id: ld_arch.c 2515 2012-06-06 23:05:00Z kaiwang27 $"); + +#define LD_DEFAULT_ARCH "amd64" + +static struct ld_arch *_get_arch_from_target(struct ld *ld, char *target); + +void +ld_arch_init(struct ld *ld) +{ + char *end; + char arch[MAX_ARCH_NAME_LEN + 1], target[MAX_TARGET_NAME_LEN + 1]; + size_t len; + + /* + * Register supported architectures. + */ + + i386_register(ld); + amd64_register(ld); + + /* + * Find out default arch for output object. + */ + + if ((end = strrchr(ld->ld_progname, '-')) != NULL && + end != ld->ld_progname) { + len = end - ld->ld_progname + 1; + if (len > MAX_TARGET_NAME_LEN) + return; + strncpy(target, ld->ld_progname, len); + target[len] = '\0'; + ld->ld_arch = _get_arch_from_target(ld, target); + } + + if (ld->ld_arch == NULL) { + snprintf(arch, sizeof(arch), "%s", LD_DEFAULT_ARCH); + ld->ld_arch = ld_arch_find(ld, arch); + if (ld->ld_arch == NULL) + ld_fatal(ld, "Internal: could not determine output" + " object architecture"); + } +} + +void +ld_arch_set(struct ld *ld, char *arch) +{ + + ld->ld_arch = ld_arch_find(ld, arch); + if (ld->ld_arch == NULL) + ld_fatal(ld, "arch '%s' is not supported", arch); +} + +void +ld_arch_set_from_target(struct ld *ld) +{ + + if (ld->ld_otgt != NULL) { + ld->ld_arch = _get_arch_from_target(ld, ld->ld_otgt_name); + if (ld->ld_arch == NULL) + ld_fatal(ld, "target '%s' is not supported", + ld->ld_otgt_name); + } +} + +int +ld_arch_equal(struct ld_arch *a1, struct ld_arch *a2) +{ + + assert(a1 != NULL && a2 != NULL); + + if (a1 == a2) + return (1); + + if (a1->alias == a2 || a2->alias == a1) + return (1); + + if (a1->alias != NULL && a1->alias == a2->alias) + return (1); + + return (0); +} + +void +ld_arch_verify(struct ld *ld, const char *name, int mach) +{ + struct ld_arch *la; + struct ld_state *ls; + + assert(ld->ld_arch != NULL); + ls = &ld->ld_state; + + if ((la = ld_arch_guess_arch_name(ld, mach)) == NULL) + ld_fatal(ld, "%s: ELF object architecture %#x not supported", + name, mach); + + if (!ld_arch_equal(la, ld->ld_arch)) { + ls->ls_arch_conflict = 1; + if (ls->ls_rerun || !ls->ls_first_elf_object) + ld_fatal(ld, "%s: ELF object architecture `%s' " + "conflicts with output object architecture `%s'", + name, la->name, ld->ld_arch->name); + ld->ld_arch = la; + } + + ls->ls_first_elf_object = 0; +} + +struct ld_arch * +ld_arch_guess_arch_name(struct ld *ld, int mach) +{ + char arch[MAX_ARCH_NAME_LEN + 1]; + + /* TODO: we should also consider elf class and endianess. */ + + switch (mach) { + case EM_386: + snprintf(arch, sizeof(arch), "%s", "i386"); + break; + case EM_ARM: + snprintf(arch, sizeof(arch), "%s", "arm"); + break; + case EM_MIPS: + case EM_MIPS_RS3_LE: + snprintf(arch, sizeof(arch), "%s", "mips"); + break; + case EM_PPC: + case EM_PPC64: + snprintf(arch, sizeof(arch), "%s", "ppc"); + break; + case EM_SPARC: + case EM_SPARCV9: + snprintf(arch, sizeof(arch), "%s", "sparc"); + break; + case EM_X86_64: + snprintf(arch, sizeof(arch), "%s", "amd64"); + break; + default: + return (NULL); + } + + return (ld_arch_find(ld, arch)); +} + +static struct ld_arch * +_get_arch_from_target(struct ld *ld, char *target) +{ + struct ld_arch *la; + char *begin, *end, name[MAX_TARGET_NAME_LEN + 1]; + + if ((begin = strchr(target, '-')) == NULL) { + la = ld_arch_find(ld, target); + return (la); + } + la = ld_arch_find(ld, begin + 1); + if (la != NULL) + return (la); + + strncpy(name, begin + 1, sizeof(name) - 1); + name[sizeof(name) - 1] = '\0'; + while ((end = strrchr(name, '-')) != NULL) { + *end = '\0'; + la = ld_arch_find(ld, name); + if (la != NULL) + return (la); + } + + return (NULL); +} + +struct ld_arch * +ld_arch_find(struct ld *ld, char *arch) +{ + struct ld_arch *la; + + HASH_FIND_STR(ld->ld_arch_list, arch, la); + + return (la); +} |