blob: e05fa5fc1de98f903f78d8fb5edc159c1cf708ea [file] [log] [blame]
/*
* ELF object format
*
* Copyright (C) 2003-2007 Michael Urman
*
* 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 OTHER 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 OTHER 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 <util.h>
/*@unused@*/ RCSID("$Id: elf-objfmt.c,v 1.1.1.1 2012/03/29 17:21:02 uid42307 Exp $");
/* Notes
*
* elf-objfmt uses the "linking" view of an ELF file:
* ELF header, an optional program header table, several sections,
* and a section header table
*
* The ELF header tells us some overall program information,
* where to find the PHT (if it exists) with phnum and phentsize,
* and where to find the SHT with shnum and shentsize
*
* The PHT doesn't seem to be generated by NASM for elftest.asm
*
* The SHT
*
* Each Section is spatially disjoint, and has exactly one SHT entry.
*/
#include <libyasm.h>
#include "elf.h"
#include "elf-machine.h"
typedef struct yasm_objfmt_elf {
yasm_objfmt_base objfmt; /* base structure */
elf_symtab_head* elf_symtab; /* symbol table of indexed syms */
elf_strtab_head* shstrtab; /* section name strtab */
elf_strtab_head* strtab; /* strtab entries */
elf_strtab_entry *file_strtab_entry;/* .file symbol associated string */
yasm_symrec *dotdotsym; /* ..sym symbol */
} yasm_objfmt_elf;
typedef struct {
yasm_objfmt_elf *objfmt_elf;
yasm_errwarns *errwarns;
FILE *f;
elf_secthead *shead;
yasm_section *sect;
yasm_object *object;
unsigned long sindex;
} elf_objfmt_output_info;
typedef struct {
yasm_object *object;
yasm_objfmt_elf *objfmt_elf;
yasm_errwarns *errwarns;
int local_names;
} build_symtab_info;
yasm_objfmt_module yasm_elf_LTX_objfmt;
yasm_objfmt_module yasm_elf32_LTX_objfmt;
yasm_objfmt_module yasm_elf64_LTX_objfmt;
static elf_symtab_entry *
elf_objfmt_symtab_append(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym,
elf_section_index sectidx, elf_symbol_binding bind,
elf_symbol_type type, elf_symbol_vis vis,
yasm_expr *size, elf_address *value)
{
elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data);
if (!entry) {
elf_strtab_entry *name =
elf_strtab_append_str(objfmt_elf->strtab,
yasm_symrec_get_name(sym));
entry = elf_symtab_entry_create(name, sym);
yasm_symrec_add_data(sym, &elf_symrec_data, entry);
}
/* Only append to table if not already appended */
if (!elf_sym_in_table(entry))
elf_symtab_append_entry(objfmt_elf->elf_symtab, entry);
elf_symtab_set_nonzero(entry, NULL, sectidx, bind, type, size, value);
elf_sym_set_visibility(entry, vis);
return entry;
}
static elf_symtab_entry *
build_extern(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym)
{
yasm_valparamhead *objext_valparams =
yasm_symrec_get_objext_valparams(sym);
if (objext_valparams) {
yasm_valparam *vp = yasm_vps_first(objext_valparams);
for (; vp; vp = yasm_vps_next(vp)) {
if (yasm_vp_string(vp))
yasm_error_set(YASM_ERROR_TYPE,
N_("unrecognized symbol type `%s'"),
yasm_vp_string(vp));
}
}
return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_GLOBAL, 0,
STV_DEFAULT, NULL, NULL);
}
struct elf_build_global_data {
yasm_expr *size;
unsigned long type; /* elf_symbol_type */
elf_symbol_vis vis;
unsigned int vis_overrides;
};
static int
elf_global_helper_valparam(void *obj, yasm_valparam *vp, unsigned long line,
void *d)
{
struct elf_build_global_data *data = (struct elf_build_global_data *)d;
const char *s;
if (!vp->val && (s = yasm_vp_id(vp))) {
yasm_error_set(YASM_ERROR_TYPE, N_("unrecognized symbol type `%s'"),
s);
return -1;
} else if (!vp->val && vp->type == YASM_PARAM_EXPR && !data->size) {
data->size = yasm_expr_copy(vp->param.e);
return 0;
} else
return yasm_dir_helper_valparam_warn(obj, vp, line, d);
}
static int
elf_global_helper_vis(void *obj, yasm_valparam *vp, unsigned long line,
void *d, uintptr_t vis)
{
struct elf_build_global_data *data = (struct elf_build_global_data *)d;
data->vis = vis;
data->vis_overrides++;
return 0;
}
static elf_symtab_entry *
build_global(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym)
{
yasm_valparamhead *objext_valparams =
yasm_symrec_get_objext_valparams(sym);
struct elf_build_global_data data;
static const yasm_dir_help help[] = {
{ "function", 0, yasm_dir_helper_flag_set,
offsetof(struct elf_build_global_data, type), STT_FUNC },
{ "data", 0, yasm_dir_helper_flag_set,
offsetof(struct elf_build_global_data, type), STT_OBJECT },
{ "object", 0, yasm_dir_helper_flag_set,
offsetof(struct elf_build_global_data, type), STT_OBJECT },
{ "internal", 0, elf_global_helper_vis, 0, STV_INTERNAL },
{ "hidden", 0, elf_global_helper_vis, 0, STV_HIDDEN },
{ "protected", 0, elf_global_helper_vis, 0, STV_PROTECTED },
};
data.size = NULL;
data.type = 0;
data.vis = STV_DEFAULT;
data.vis_overrides = 0;
if (objext_valparams)
yasm_dir_helper(sym, yasm_vps_first(objext_valparams),
yasm_symrec_get_decl_line(sym), help, NELEMS(help),
&data, elf_global_helper_valparam);
if (data.vis_overrides > 1) {
yasm_warn_set(YASM_WARN_GENERAL,
N_("More than one symbol visibility provided; using last"));
}
return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_GLOBAL,
data.type, data.vis, data.size, NULL);
}
static /*@null@*/ elf_symtab_entry *
build_common(yasm_objfmt_elf *objfmt_elf, yasm_symrec *sym, yasm_object *object)
{
yasm_expr **size = yasm_symrec_get_common_size(sym);
yasm_valparamhead *objext_valparams =
yasm_symrec_get_objext_valparams(sym);
unsigned long addralign = 0;
if (objext_valparams) {
yasm_valparam *vp = yasm_vps_first(objext_valparams);
for (; vp; vp = yasm_vps_next(vp)) {
if (!vp->val) {
/*@only@*/ /*@null@*/ yasm_expr *align_expr;
/*@dependent@*/ /*@null@*/ const yasm_intnum *align_intn;
if (!(align_expr = yasm_vp_expr(vp, object->symtab,
yasm_symrec_get_def_line(sym)))
|| !(align_intn = yasm_expr_get_intnum(&align_expr, 0))) {
yasm_error_set(YASM_ERROR_VALUE,
N_("alignment constraint is not an integer"));
if (align_expr)
yasm_expr_destroy(align_expr);
return NULL;
}
addralign = yasm_intnum_get_uint(align_intn);
yasm_expr_destroy(align_expr);
/* Alignments must be a power of two. */
if (!is_exp2(addralign)) {
yasm_error_set(YASM_ERROR_VALUE,
N_("alignment constraint is not a power of two"));
return NULL;
}
} else
yasm_warn_set(YASM_WARN_GENERAL,
N_("Unrecognized qualifier `%s'"), vp->val);
}
}
return elf_objfmt_symtab_append(objfmt_elf, sym, SHN_COMMON, STB_GLOBAL,
0, STV_DEFAULT, *size, &addralign);
}
static int
elf_objfmt_build_symtab(yasm_symrec *sym, /*@null@*/ void *d)
{
build_symtab_info *info = (build_symtab_info *)d;
yasm_sym_vis vis = yasm_symrec_get_visibility(sym);
yasm_sym_status status = yasm_symrec_get_status(sym);
elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data);
elf_address value=0;
yasm_section *sect=NULL;
yasm_bytecode *precbc=NULL;
assert(info != NULL);
if (vis & YASM_SYM_EXTERN) {
entry = build_extern(info->objfmt_elf, sym);
yasm_errwarn_propagate(info->errwarns,
yasm_symrec_get_decl_line(sym));
return 0;
}
if (vis & YASM_SYM_COMMON) {
entry = build_common(info->objfmt_elf, sym, info->object);
yasm_errwarn_propagate(info->errwarns,
yasm_symrec_get_decl_line(sym));
/* If the COMMON variable was actually defined, fall through. */
if (!(status & YASM_SYM_DEFINED))
return 0;
}
/* Ignore any undefined at this point. */
if (!(status & YASM_SYM_DEFINED))
return 0;
if (!yasm_symrec_get_label(sym, &precbc)) {
if (!yasm_symrec_get_equ(sym) && !yasm_symrec_is_abs(sym))
return 0;
precbc = NULL;
}
if (precbc)
sect = yasm_bc_get_section(precbc);
if (entry && elf_sym_in_table(entry))
;
else if (vis & YASM_SYM_GLOBAL) {
entry = build_global(info->objfmt_elf, sym);
yasm_errwarn_propagate(info->errwarns, yasm_symrec_get_decl_line(sym));
} else {
int is_sect = 0;
/* Locals (except when debugging) do not need to be
* in the symbol table, unless they're a section.
*/
if (sect &&
strcmp(yasm_symrec_get_name(sym), yasm_section_get_name(sect))==0)
is_sect = 1;
#if 0
/* FIXME: to enable this we must have handling in place for special
* symbols.
*/
if (!info->local_names && !is_sect)
return 0;
#else
if (yasm_symrec_get_equ(sym) && !yasm_symrec_is_abs(sym))
return 0;
#endif
entry = yasm_symrec_get_data(sym, &elf_symrec_data);
if (!entry) {
elf_strtab_entry *name = !info->local_names || is_sect ? NULL :
elf_strtab_append_str(info->objfmt_elf->strtab,
yasm_symrec_get_name(sym));
entry = elf_symtab_entry_create(name, sym);
yasm_symrec_add_data(sym, &elf_symrec_data, entry);
}
if (!elf_sym_in_table(entry))
elf_symtab_insert_local_sym(info->objfmt_elf->elf_symtab, entry);
elf_symtab_set_nonzero(entry, sect, 0, STB_LOCAL,
is_sect ? STT_SECTION : 0, NULL, 0);
if (is_sect)
return 0;
}
if (precbc)
value = yasm_bc_next_offset(precbc);
elf_symtab_set_nonzero(entry, sect, 0, 0, 0, NULL, &value);
return 0;
}
static yasm_objfmt *
elf_objfmt_create_common(yasm_object *object, yasm_objfmt_module *module,
int bits_pref,
const elf_machine_handler **elf_march_out)
{
yasm_objfmt_elf *objfmt_elf = yasm_xmalloc(sizeof(yasm_objfmt_elf));
yasm_symrec *filesym;
elf_symtab_entry *entry;
const elf_machine_handler *elf_march;
objfmt_elf->objfmt.module = module;
elf_march = elf_set_arch(object->arch, object->symtab, bits_pref);
if (!elf_march) {
yasm_xfree(objfmt_elf);
return NULL;
}
if (elf_march_out)
*elf_march_out = elf_march;
objfmt_elf->shstrtab = elf_strtab_create();
objfmt_elf->strtab = elf_strtab_create();
objfmt_elf->elf_symtab = elf_symtab_create();
/* FIXME: misuse of NULL bytecode here; it works, but only barely. */
filesym = yasm_symtab_define_label(object->symtab, ".file", NULL, 0, 0);
/* Put in current input filename; we'll replace it in output() */
objfmt_elf->file_strtab_entry =
elf_strtab_append_str(objfmt_elf->strtab, object->src_filename);
entry = elf_symtab_entry_create(objfmt_elf->file_strtab_entry, filesym);
yasm_symrec_add_data(filesym, &elf_symrec_data, entry);
elf_symtab_set_nonzero(entry, NULL, SHN_ABS, STB_LOCAL, STT_FILE, NULL,
NULL);
elf_symtab_append_entry(objfmt_elf->elf_symtab, entry);
/* FIXME: misuse of NULL bytecode */
objfmt_elf->dotdotsym =
yasm_symtab_define_label(object->symtab, "..sym", NULL, 0, 0);
return (yasm_objfmt *)objfmt_elf;
}
static yasm_objfmt *
elf_objfmt_create(yasm_object *object)
{
const elf_machine_handler *elf_march;
yasm_objfmt *objfmt;
yasm_objfmt_elf *objfmt_elf;
objfmt = elf_objfmt_create_common(object, &yasm_elf_LTX_objfmt, 0,
&elf_march);
if (objfmt) {
objfmt_elf = (yasm_objfmt_elf *)objfmt;
/* Figure out which bitness of object format to use */
if (elf_march->bits == 32)
objfmt_elf->objfmt.module = &yasm_elf32_LTX_objfmt;
else if (elf_march->bits == 64)
objfmt_elf->objfmt.module = &yasm_elf64_LTX_objfmt;
}
return objfmt;
}
static yasm_objfmt *
elf32_objfmt_create(yasm_object *object)
{
return elf_objfmt_create_common(object, &yasm_elf32_LTX_objfmt, 32, NULL);
}
static yasm_objfmt *
elf64_objfmt_create(yasm_object *object)
{
return elf_objfmt_create_common(object, &yasm_elf64_LTX_objfmt, 64, NULL);
}
static long
elf_objfmt_output_align(FILE *f, unsigned int align)
{
long pos;
unsigned long delta;
if (!is_exp2(align))
yasm_internal_error("requested alignment not a power of two");
pos = ftell(f);
if (pos == -1) {
yasm_error_set(YASM_ERROR_IO,
N_("could not get file position on output file"));
return -1;
}
delta = align - (pos & (align-1));
if (delta != align) {
pos += delta;
if (fseek(f, pos, SEEK_SET) < 0) {
yasm_error_set(YASM_ERROR_IO,
N_("could not set file position on output file"));
return -1;
}
}
return pos;
}
static int
elf_objfmt_output_reloc(yasm_symrec *sym, yasm_bytecode *bc,
unsigned char *buf, unsigned int destsize,
unsigned int valsize, int warn, void *d)
{
elf_reloc_entry *reloc;
elf_objfmt_output_info *info = d;
yasm_intnum *zero;
int retval;
reloc = elf_reloc_entry_create(sym, NULL,
yasm_intnum_create_uint(bc->offset), 0, valsize);
if (reloc == NULL) {
yasm_error_set(YASM_ERROR_TYPE, N_("elf: invalid relocation size"));
return 1;
}
/* allocate .rel[a] sections on a need-basis */
elf_secthead_append_reloc(info->sect, info->shead, reloc);
zero = yasm_intnum_create_uint(0);
elf_handle_reloc_addend(zero, reloc);
retval = yasm_arch_intnum_tobytes(info->object->arch, zero, buf, destsize,
valsize, 0, bc, warn);
yasm_intnum_destroy(zero);
return retval;
}
static int
elf_objfmt_output_value(yasm_value *value, unsigned char *buf,
unsigned int destsize, unsigned long offset,
yasm_bytecode *bc, int warn, /*@null@*/ void *d)
{
/*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d;
/*@dependent@*/ /*@null@*/ yasm_intnum *intn;
unsigned long intn_val;
/*@null@*/ elf_reloc_entry *reloc = NULL;
int retval;
unsigned int valsize = value->size;
if (info == NULL)
yasm_internal_error("null info struct");
if (value->abs)
value->abs = yasm_expr_simplify(value->abs, 1);
/* Try to output constant and PC-relative section-local first.
* Note this does NOT output any value with a SEG, WRT, external,
* cross-section, or non-PC-relative reference (those are handled below).
*/
switch (yasm_value_output_basic(value, buf, destsize, bc, warn,
info->object->arch)) {
case -1:
return 1;
case 0:
break;
default:
return 0;
}
/* Handle other expressions, with relocation if necessary */
if (value->seg_of || value->section_rel || value->rshift > 0) {
yasm_error_set(YASM_ERROR_TOO_COMPLEX,
N_("elf: relocation too complex"));
return 1;
}
intn_val = 0;
if (value->rel) {
yasm_sym_vis vis = yasm_symrec_get_visibility(value->rel);
/*@dependent@*/ /*@null@*/ yasm_symrec *sym = value->rel;
/*@dependent@*/ /*@null@*/ yasm_symrec *wrt = value->wrt;
if (wrt == info->objfmt_elf->dotdotsym)
wrt = NULL;
else if (wrt && elf_is_wrt_sym_relative(wrt))
;
else if (wrt && elf_is_wrt_pos_adjusted(wrt))
intn_val = offset + bc->offset;
else if (vis == YASM_SYM_LOCAL) {
yasm_bytecode *sym_precbc;
/* Local symbols need relocation to their section's start, and
* add in the offset of the bytecode (within the target section)
* into the abs portion.
*
* This is only done if the symbol is relocated against the
* section instead of the symbol itself.
*/
if (yasm_symrec_get_label(sym, &sym_precbc)) {
/* Relocate to section start */
yasm_section *sym_sect = yasm_bc_get_section(sym_precbc);
/*@null@*/ elf_secthead *sym_shead;
sym_shead = yasm_section_get_data(sym_sect, &elf_section_data);
assert(sym_shead != NULL);
sym = elf_secthead_get_sym(sym_shead);
intn_val = yasm_bc_next_offset(sym_precbc);
}
}
/* For PC-relative, need to add offset of expression within bc. */
if (value->curpos_rel)
intn_val += offset;
reloc = elf_reloc_entry_create(sym, wrt,
yasm_intnum_create_uint(bc->offset + offset), value->curpos_rel,
valsize);
if (reloc == NULL) {
yasm_error_set(YASM_ERROR_TYPE,
N_("elf: invalid relocation (WRT or size)"));
return 1;
}
/* allocate .rel[a] sections on a need-basis */
elf_secthead_append_reloc(info->sect, info->shead, reloc);
}
intn = yasm_intnum_create_uint(intn_val);
if (value->abs) {
yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0);
if (!intn2) {
yasm_error_set(YASM_ERROR_TOO_COMPLEX,
N_("elf: relocation too complex"));
yasm_intnum_destroy(intn);
return 1;
}
yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2);
}
if (reloc)
elf_handle_reloc_addend(intn, reloc);
retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize,
valsize, 0, bc, warn);
yasm_intnum_destroy(intn);
return retval;
}
static int
elf_objfmt_output_bytecode(yasm_bytecode *bc, /*@null@*/ void *d)
{
/*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d;
unsigned char buf[256];
/*@null@*/ /*@only@*/ unsigned char *bigbuf;
unsigned long size = 256;
int gap;
if (info == NULL)
yasm_internal_error("null info struct");
bigbuf = yasm_bc_tobytes(bc, buf, &size, &gap, info,
elf_objfmt_output_value, elf_objfmt_output_reloc);
/* Don't bother doing anything else if size ended up being 0. */
if (size == 0) {
if (bigbuf)
yasm_xfree(bigbuf);
return 0;
}
else {
yasm_intnum *bcsize = yasm_intnum_create_uint(size);
elf_secthead_add_size(info->shead, bcsize);
yasm_intnum_destroy(bcsize);
}
/* Warn that gaps are converted to 0 and write out the 0's. */
if (gap) {
unsigned long left;
yasm_warn_set(YASM_WARN_UNINIT_CONTENTS,
N_("uninitialized space declared in code/data section: zeroing"));
/* Write out in chunks */
memset(buf, 0, 256);
left = size;
while (left > 256) {
fwrite(buf, 256, 1, info->f);
left -= 256;
}
fwrite(buf, left, 1, info->f);
} else {
/* Output buf (or bigbuf if non-NULL) to file */
fwrite(bigbuf ? bigbuf : buf, (size_t)size, 1, info->f);
}
/* If bigbuf was allocated, free it */
if (bigbuf)
yasm_xfree(bigbuf);
return 0;
}
static int
elf_objfmt_create_dbg_secthead(yasm_section *sect, /*@null@*/ void *d)
{
/*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d;
elf_secthead *shead;
elf_section_type type=SHT_PROGBITS;
elf_size entsize=0;
const char *sectname;
/*@dependent@*/ yasm_symrec *sym;
elf_strtab_entry *name;
shead = yasm_section_get_data(sect, &elf_section_data);
if (shead)
return 0; /* only create new secthead if missing */
sectname = yasm_section_get_name(sect);
name = elf_strtab_append_str(info->objfmt_elf->shstrtab, sectname);
if (yasm__strcasecmp(sectname, ".stab")==0) {
entsize = 12;
} else if (yasm__strcasecmp(sectname, ".stabstr")==0) {
type = SHT_STRTAB;
} else if (yasm__strncasecmp(sectname, ".debug_", 7)==0) {
;
} else
yasm_internal_error(N_("Unrecognized section without data"));
shead = elf_secthead_create(name, type, 0, 0, 0);
elf_secthead_set_entsize(shead, entsize);
sym = yasm_symtab_define_label(info->object->symtab, sectname,
yasm_section_bcs_first(sect), 1, 0);
elf_secthead_set_sym(shead, sym);
yasm_section_add_data(sect, &elf_section_data, shead);
return 0;
}
static int
elf_objfmt_output_section(yasm_section *sect, /*@null@*/ void *d)
{
/*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d;
/*@dependent@*/ /*@null@*/ elf_secthead *shead;
long pos;
char *relname;
const char *sectname;
if (info == NULL)
yasm_internal_error("null info struct");
shead = yasm_section_get_data(sect, &elf_section_data);
if (shead == NULL)
yasm_internal_error("no associated data");
if (elf_secthead_get_align(shead) == 0)
elf_secthead_set_align(shead, yasm_section_get_align(sect));
/* don't output header-only sections */
if ((elf_secthead_get_type(shead) & SHT_NOBITS) == SHT_NOBITS)
{
yasm_bytecode *last = yasm_section_bcs_last(sect);
if (last) {
yasm_intnum *sectsize;
sectsize = yasm_intnum_create_uint(yasm_bc_next_offset(last));
elf_secthead_add_size(shead, sectsize);
yasm_intnum_destroy(sectsize);
}
elf_secthead_set_index(shead, ++info->sindex);
return 0;
}
if ((pos = ftell(info->f)) == -1) {
yasm_error_set(YASM_ERROR_IO,
N_("couldn't read position on output stream"));
yasm_errwarn_propagate(info->errwarns, 0);
}
pos = elf_secthead_set_file_offset(shead, pos);
if (fseek(info->f, pos, SEEK_SET) < 0) {
yasm_error_set(YASM_ERROR_IO, N_("couldn't seek on output stream"));
yasm_errwarn_propagate(info->errwarns, 0);
}
info->sect = sect;
info->shead = shead;
yasm_section_bcs_traverse(sect, info->errwarns, info,
elf_objfmt_output_bytecode);
elf_secthead_set_index(shead, ++info->sindex);
/* No relocations to output? Go on to next section */
if (elf_secthead_write_relocs_to_file(info->f, sect, shead,
info->errwarns) == 0)
return 0;
elf_secthead_set_rel_index(shead, ++info->sindex);
/* name the relocation section .rel[a].foo */
sectname = yasm_section_get_name(sect);
relname = elf_secthead_name_reloc_section(sectname);
elf_secthead_set_rel_name(shead,
elf_strtab_append_str(info->objfmt_elf->shstrtab, relname));
yasm_xfree(relname);
return 0;
}
static int
elf_objfmt_output_secthead(yasm_section *sect, /*@null@*/ void *d)
{
/*@null@*/ elf_objfmt_output_info *info = (elf_objfmt_output_info *)d;
/*@dependent@*/ /*@null@*/ elf_secthead *shead;
if (info == NULL)
yasm_internal_error("null info struct");
shead = yasm_section_get_data(sect, &elf_section_data);
if (shead == NULL)
yasm_internal_error("no section header attached to section");
if(elf_secthead_write_to_file(info->f, shead, info->sindex+1))
info->sindex++;
/* output strtab headers here? */
/* relocation entries for .foo are stored in section .rel[a].foo */
if(elf_secthead_write_rel_to_file(info->f, 3, sect, shead,
info->sindex+1))
info->sindex++;
return 0;
}
static void
elf_objfmt_output(yasm_object *object, FILE *f, int all_syms,
yasm_errwarns *errwarns)
{
yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt;
elf_objfmt_output_info info;
build_symtab_info buildsym_info;
long pos;
unsigned long elf_shead_addr;
elf_secthead *esdn;
unsigned long elf_strtab_offset, elf_shstrtab_offset, elf_symtab_offset;
unsigned long elf_strtab_size, elf_shstrtab_size, elf_symtab_size;
elf_strtab_entry *elf_strtab_name, *elf_shstrtab_name, *elf_symtab_name;
unsigned long elf_symtab_nlocal;
info.object = object;
info.objfmt_elf = objfmt_elf;
info.errwarns = errwarns;
info.f = f;
/* Update filename strtab */
elf_strtab_entry_set_str(objfmt_elf->file_strtab_entry,
object->src_filename);
/* Allocate space for Ehdr by seeking forward */
if (fseek(f, (long)(elf_proghead_get_size()), SEEK_SET) < 0) {
yasm_error_set(YASM_ERROR_IO, N_("could not seek on output file"));
yasm_errwarn_propagate(errwarns, 0);
return;
}
/* Create missing section headers */
if (yasm_object_sections_traverse(object, &info,
elf_objfmt_create_dbg_secthead))
return;
/* add all (local) syms to symtab because relocation needs a symtab index
* if all_syms, register them by name. if not, use strtab entry 0 */
buildsym_info.object = object;
buildsym_info.objfmt_elf = objfmt_elf;
buildsym_info.errwarns = errwarns;
buildsym_info.local_names = all_syms;
yasm_symtab_traverse(object->symtab, &buildsym_info,
elf_objfmt_build_symtab);
elf_symtab_nlocal = elf_symtab_assign_indices(objfmt_elf->elf_symtab);
/* output known sections - includes reloc sections which aren't in yasm's
* list. Assign indices as we go. */
info.sindex = 3;
if (yasm_object_sections_traverse(object, &info,
elf_objfmt_output_section))
return;
/* add final sections to the shstrtab */
elf_strtab_name = elf_strtab_append_str(objfmt_elf->shstrtab, ".strtab");
elf_symtab_name = elf_strtab_append_str(objfmt_elf->shstrtab, ".symtab");
elf_shstrtab_name = elf_strtab_append_str(objfmt_elf->shstrtab,
".shstrtab");
/* output .shstrtab */
if ((pos = elf_objfmt_output_align(f, 4)) == -1) {
yasm_errwarn_propagate(errwarns, 0);
return;
}
elf_shstrtab_offset = (unsigned long) pos;
elf_shstrtab_size = elf_strtab_output_to_file(f, objfmt_elf->shstrtab);
/* output .strtab */
if ((pos = elf_objfmt_output_align(f, 4)) == -1) {
yasm_errwarn_propagate(errwarns, 0);
return;
}
elf_strtab_offset = (unsigned long) pos;
elf_strtab_size = elf_strtab_output_to_file(f, objfmt_elf->strtab);
/* output .symtab - last section so all others have indexes */
if ((pos = elf_objfmt_output_align(f, 4)) == -1) {
yasm_errwarn_propagate(errwarns, 0);
return;
}
elf_symtab_offset = (unsigned long) pos;
elf_symtab_size = elf_symtab_write_to_file(f, objfmt_elf->elf_symtab,
errwarns);
/* output section header table */
if ((pos = elf_objfmt_output_align(f, 16)) == -1) {
yasm_errwarn_propagate(errwarns, 0);
return;
}
elf_shead_addr = (unsigned long) pos;
/* stabs debugging support */
if (strcmp(yasm_dbgfmt_keyword(object->dbgfmt), "stabs")==0) {
yasm_section *stabsect = yasm_object_find_general(object, ".stab");
yasm_section *stabstrsect =
yasm_object_find_general(object, ".stabstr");
if (stabsect && stabstrsect) {
elf_secthead *stab =
yasm_section_get_data(stabsect, &elf_section_data);
elf_secthead *stabstr =
yasm_section_get_data(stabstrsect, &elf_section_data);
if (stab && stabstr) {
elf_secthead_set_link(stab, elf_secthead_get_index(stabstr));
}
else
yasm_internal_error(N_("missing .stab or .stabstr section/data"));
}
}
/* output dummy section header - 0 */
info.sindex = 0;
esdn = elf_secthead_create(NULL, SHT_NULL, 0, 0, 0);
elf_secthead_set_index(esdn, 0);
elf_secthead_write_to_file(f, esdn, 0);
elf_secthead_destroy(esdn);
esdn = elf_secthead_create(elf_shstrtab_name, SHT_STRTAB, 0,
elf_shstrtab_offset, elf_shstrtab_size);
elf_secthead_set_index(esdn, 1);
elf_secthead_write_to_file(f, esdn, 1);
elf_secthead_destroy(esdn);
esdn = elf_secthead_create(elf_strtab_name, SHT_STRTAB, 0,
elf_strtab_offset, elf_strtab_size);
elf_secthead_set_index(esdn, 2);
elf_secthead_write_to_file(f, esdn, 2);
elf_secthead_destroy(esdn);
esdn = elf_secthead_create(elf_symtab_name, SHT_SYMTAB, 0,
elf_symtab_offset, elf_symtab_size);
elf_secthead_set_index(esdn, 3);
elf_secthead_set_info(esdn, elf_symtab_nlocal);
elf_secthead_set_link(esdn, 2); /* for .strtab, which is index 2 */
elf_secthead_write_to_file(f, esdn, 3);
elf_secthead_destroy(esdn);
info.sindex = 3;
/* output remaining section headers */
yasm_object_sections_traverse(object, &info, elf_objfmt_output_secthead);
/* output Ehdr */
if (fseek(f, 0, SEEK_SET) < 0) {
yasm_error_set(YASM_ERROR_IO, N_("could not seek on output file"));
yasm_errwarn_propagate(errwarns, 0);
return;
}
elf_proghead_write_to_file(f, elf_shead_addr, info.sindex+1, 1);
}
static void
elf_objfmt_destroy(yasm_objfmt *objfmt)
{
yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)objfmt;
elf_symtab_destroy(objfmt_elf->elf_symtab);
elf_strtab_destroy(objfmt_elf->shstrtab);
elf_strtab_destroy(objfmt_elf->strtab);
yasm_xfree(objfmt);
}
static elf_secthead *
elf_objfmt_init_new_section(yasm_object *object, yasm_section *sect,
const char *sectname, unsigned long type,
unsigned long flags, unsigned long line)
{
yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt;
elf_secthead *esd;
yasm_symrec *sym;
elf_strtab_entry *name = elf_strtab_append_str(objfmt_elf->shstrtab,
sectname);
esd = elf_secthead_create(name, type, flags, 0, 0);
yasm_section_add_data(sect, &elf_section_data, esd);
sym = yasm_symtab_define_label(object->symtab, sectname,
yasm_section_bcs_first(sect), 1, line);
elf_secthead_set_sym(esd, sym);
return esd;
}
static yasm_section *
elf_objfmt_add_default_section(yasm_object *object)
{
yasm_section *retval;
int isnew;
elf_secthead *esd;
retval = yasm_object_get_general(object, ".text", 16, 1, 0, &isnew, 0);
esd = elf_objfmt_init_new_section(object, retval, ".text", SHT_PROGBITS,
SHF_ALLOC + SHF_EXECINSTR, 0);
yasm_section_set_default(retval, 1);
return retval;
}
struct elf_section_switch_data {
/*@only@*/ /*@null@*/ yasm_intnum *align_intn;
unsigned long flags;
unsigned long type;
int gasflags;
};
/* GAS-style flags */
static int
elf_helper_gasflags(void *obj, yasm_valparam *vp, unsigned long line, void *d,
/*@unused@*/ uintptr_t arg)
{
struct elf_section_switch_data *data = (struct elf_section_switch_data *)d;
const char *s = yasm_vp_string(vp);
size_t i;
if (!s) {
yasm_error_set(YASM_ERROR_VALUE,
N_("non-string section attribute"));
return -1;
}
data->flags = 0;
for (i=0; i<strlen(s); i++) {
switch (s[i]) {
case 'a':
data->flags |= SHF_ALLOC;
break;
case 'w':
data->flags |= SHF_WRITE;
break;
case 'x':
data->flags |= SHF_EXECINSTR;
break;
case 'M':
data->flags |= SHF_MERGE;
break;
case 'S':
data->flags |= SHF_STRINGS;
break;
case 'G':
data->flags |= SHF_GROUP;
break;
case 'T':
data->flags |= SHF_TLS;
break;
default:
yasm_warn_set(YASM_WARN_GENERAL,
N_("unrecognized section attribute: `%c'"),
s[i]);
}
}
data->gasflags = 1;
return 0;
}
static /*@observer@*/ /*@null@*/ yasm_section *
elf_objfmt_section_switch(yasm_object *object, yasm_valparamhead *valparams,
/*@null@*/ yasm_valparamhead *objext_valparams,
unsigned long line)
{
yasm_valparam *vp;
yasm_section *retval;
int isnew;
unsigned long align = 4;
int flags_override = 0;
const char *sectname;
int resonly = 0;
struct elf_section_switch_data data;
static const yasm_dir_help help[] = {
{ "alloc", 0, yasm_dir_helper_flag_or,
offsetof(struct elf_section_switch_data, flags), SHF_ALLOC },
{ "exec", 0, yasm_dir_helper_flag_or,
offsetof(struct elf_section_switch_data, flags), SHF_EXECINSTR },
{ "write", 0, yasm_dir_helper_flag_or,
offsetof(struct elf_section_switch_data, flags), SHF_WRITE },
{ "tls", 0, yasm_dir_helper_flag_or,
offsetof(struct elf_section_switch_data, flags), SHF_TLS },
{ "progbits", 0, yasm_dir_helper_flag_set,
offsetof(struct elf_section_switch_data, type), SHT_PROGBITS },
{ "noalloc", 0, yasm_dir_helper_flag_and,
offsetof(struct elf_section_switch_data, flags), SHF_ALLOC },
{ "noexec", 0, yasm_dir_helper_flag_and,
offsetof(struct elf_section_switch_data, flags), SHF_EXECINSTR },
{ "nowrite", 0, yasm_dir_helper_flag_and,
offsetof(struct elf_section_switch_data, flags), SHF_WRITE },
{ "notls", 0, yasm_dir_helper_flag_and,
offsetof(struct elf_section_switch_data, flags), SHF_TLS },
{ "noprogbits", 0, yasm_dir_helper_flag_set,
offsetof(struct elf_section_switch_data, type), SHT_NOBITS },
{ "nobits", 0, yasm_dir_helper_flag_set,
offsetof(struct elf_section_switch_data, type), SHT_NOBITS },
{ "gasflags", 1, elf_helper_gasflags, 0, 0 },
{ "align", 1, yasm_dir_helper_intn,
offsetof(struct elf_section_switch_data, align_intn), 0 }
};
/*@only@*/ /*@null@*/ yasm_expr *merge_expr = NULL;
/*@dependent@*/ /*@null@*/ const yasm_intnum *merge_intn = NULL;
elf_secthead *esd;
data.align_intn = NULL;
data.flags = SHF_ALLOC;
data.type = SHT_PROGBITS;
data.gasflags = 0;
vp = yasm_vps_first(valparams);
sectname = yasm_vp_string(vp);
if (!sectname)
return NULL;
vp = yasm_vps_next(vp);
if (strcmp(sectname, ".bss") == 0) {
data.type = SHT_NOBITS;
data.flags = SHF_ALLOC + SHF_WRITE;
resonly = 1;
} else if (strcmp(sectname, ".data") == 0) {
data.type = SHT_PROGBITS;
data.flags = SHF_ALLOC + SHF_WRITE;
} else if (strcmp(sectname, ".tdata") == 0) {
data.type = SHT_PROGBITS;
data.flags = SHF_ALLOC + SHF_WRITE + SHF_TLS;
} else if (strcmp(sectname, ".rodata") == 0) {
data.type = SHT_PROGBITS;
data.flags = SHF_ALLOC;
} else if (strcmp(sectname, ".text") == 0) {
align = 16;
data.type = SHT_PROGBITS;
data.flags = SHF_ALLOC + SHF_EXECINSTR;
} else if (strcmp(sectname, ".comment") == 0) {
align = 0;
data.type = SHT_PROGBITS;
data.flags = 0;
} else {
/* Default to code */
align = 1;
}
flags_override = yasm_dir_helper(object, vp, line, help, NELEMS(help),
&data, yasm_dir_helper_valparam_warn);
if (flags_override < 0)
return NULL; /* error occurred */
if (data.align_intn) {
align = yasm_intnum_get_uint(data.align_intn);
yasm_intnum_destroy(data.align_intn);
/* Alignments must be a power of two. */
if (!is_exp2(align)) {
yasm_error_set(YASM_ERROR_VALUE,
N_("argument to `%s' is not a power of two"),
"align");
return NULL;
}
}
/* Handle merge entity size */
if (data.flags & SHF_MERGE) {
if (objext_valparams && (vp = yasm_vps_first(objext_valparams))
&& !vp->val) {
if (!(merge_expr = yasm_vp_expr(vp, object->symtab, line)) ||
!(merge_intn = yasm_expr_get_intnum(&merge_expr, 0)))
yasm_warn_set(YASM_WARN_GENERAL,
N_("invalid merge entity size"));
} else {
yasm_warn_set(YASM_WARN_GENERAL,
N_("entity size for SHF_MERGE not specified"));
data.flags &= ~SHF_MERGE;
}
}
retval = yasm_object_get_general(object, sectname, align,
(data.flags & SHF_EXECINSTR) != 0,
resonly, &isnew, line);
if (isnew)
esd = elf_objfmt_init_new_section(object, retval, sectname, data.type,
data.flags, line);
else
esd = yasm_section_get_data(retval, &elf_section_data);
if (isnew || yasm_section_is_default(retval)) {
yasm_section_set_default(retval, 0);
elf_secthead_set_typeflags(esd, data.type, data.flags);
if (merge_intn)
elf_secthead_set_entsize(esd, yasm_intnum_get_uint(merge_intn));
yasm_section_set_align(retval, align, line);
} else if (flags_override && !data.gasflags)
yasm_warn_set(YASM_WARN_GENERAL,
N_("section flags ignored on section redeclaration"));
if (merge_expr)
yasm_expr_destroy(merge_expr);
return retval;
}
static /*@observer@*/ /*@null@*/ yasm_symrec *
elf_objfmt_get_special_sym(yasm_object *object, const char *name,
const char *parser)
{
if (yasm__strcasecmp(name, "sym") == 0) {
yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt;
return objfmt_elf->dotdotsym;
}
return elf_get_special_sym(name, parser);
}
static void
dir_type(yasm_object *object, yasm_valparamhead *valparams,
yasm_valparamhead *objext_valparams, unsigned long line)
{
yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt;
yasm_valparam *vp = yasm_vps_first(valparams);
const char *symname = yasm_vp_id(vp);
/* Get symbol elf data */
yasm_symrec *sym = yasm_symtab_use(object->symtab, symname, line);
elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data);
/*@null@*/ const char *type;
/* Create entry if necessary */
if (!entry) {
entry = elf_symtab_entry_create(
elf_strtab_append_str(objfmt_elf->strtab, symname), sym);
yasm_symrec_add_data(sym, &elf_symrec_data, entry);
}
/* Pull new type from param */
vp = yasm_vps_next(vp);
if (vp && !vp->val && (type = yasm_vp_id(vp))) {
if (yasm__strcasecmp(type, "function") == 0)
elf_sym_set_type(entry, STT_FUNC);
else if (yasm__strcasecmp(type, "object") == 0)
elf_sym_set_type(entry, STT_OBJECT);
else if (yasm__strcasecmp(type, "tls_object") == 0)
elf_sym_set_type(entry, STT_TLS);
else if (yasm__strcasecmp(type, "notype") == 0)
elf_sym_set_type(entry, STT_NOTYPE);
else
yasm_warn_set(YASM_WARN_GENERAL,
N_("unrecognized symbol type `%s'"), type);
} else
yasm_error_set(YASM_ERROR_SYNTAX, N_("no type specified"));
}
static void
dir_size(yasm_object *object, yasm_valparamhead *valparams,
yasm_valparamhead *objext_valparams, unsigned long line)
{
yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt;
yasm_valparam *vp = yasm_vps_first(valparams);
const char *symname = yasm_vp_id(vp);
/* Get symbol elf data */
yasm_symrec *sym = yasm_symtab_use(object->symtab, symname, line);
elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data);
/*@only@*/ /*@null@*/ yasm_expr *size;
/* Create entry if necessary */
if (!entry) {
entry = elf_symtab_entry_create(
elf_strtab_append_str(objfmt_elf->strtab, symname), sym);
yasm_symrec_add_data(sym, &elf_symrec_data, entry);
}
/* Pull new size from param */
vp = yasm_vps_next(vp);
if (vp && !vp->val && (size = yasm_vp_expr(vp, object->symtab, line)))
elf_sym_set_size(entry, size);
else
yasm_error_set(YASM_ERROR_SYNTAX, N_("no size specified"));
}
static void
dir_weak(yasm_object *object, yasm_valparamhead *valparams,
yasm_valparamhead *objext_valparams, unsigned long line)
{
yasm_objfmt_elf *objfmt_elf = (yasm_objfmt_elf *)object->objfmt;
yasm_valparam *vp = yasm_vps_first(valparams);
const char *symname = yasm_vp_id(vp);
yasm_symrec *sym = yasm_symtab_declare(object->symtab, symname,
YASM_SYM_GLOBAL, line);
elf_objfmt_symtab_append(objfmt_elf, sym, SHN_UNDEF, STB_WEAK, 0,
STV_DEFAULT, NULL, NULL);
}
static void
dir_ident(yasm_object *object, yasm_valparamhead *valparams,
yasm_valparamhead *objext_valparams, unsigned long line)
{
yasm_valparamhead sect_vps;
yasm_datavalhead dvs;
yasm_section *comment;
yasm_valparam *vp;
yasm_valparam *vp2;
/* Accept, but do nothing with empty ident */
if (!valparams)
return;
vp = yasm_vps_first(valparams);
if (!vp)
return;
/* Put ident data into .comment section */
yasm_vps_initialize(&sect_vps);
vp2 = yasm_vp_create_string(NULL, yasm__xstrdup(".comment"));
yasm_vps_append(&sect_vps, vp2);
comment = elf_objfmt_section_switch(object, &sect_vps, NULL, line);
yasm_vps_delete(&sect_vps);
/* To match GAS output, if the comment section is empty, put an
* initial 0 byte in the section.
*/
if (yasm_section_bcs_first(comment) == yasm_section_bcs_last(comment)) {
yasm_dvs_initialize(&dvs);
yasm_dvs_append(&dvs, yasm_dv_create_expr(
yasm_expr_create_ident(
yasm_expr_int(yasm_intnum_create_uint(0)), line)));
yasm_section_bcs_append(comment,
yasm_bc_create_data(&dvs, 1, 0, object->arch, line));
}
yasm_dvs_initialize(&dvs);
do {
const char *s = yasm_vp_string(vp);
if (!s) {
yasm_error_set(YASM_ERROR_VALUE,
N_(".comment requires string parameters"));
yasm_dvs_delete(&dvs);
return;
}
yasm_dvs_append(&dvs,
yasm_dv_create_string(yasm__xstrdup(s), strlen(s)));
} while ((vp = yasm_vps_next(vp)));
yasm_section_bcs_append(comment,
yasm_bc_create_data(&dvs, 1, 1, object->arch, line));
}
/* Define valid debug formats to use with this object format */
static const char *elf_objfmt_dbgfmt_keywords[] = {
"null",
"stabs",
"dwarf2",
NULL
};
static const yasm_directive elf_objfmt_directives[] = {
{ ".type", "gas", dir_type, YASM_DIR_ID_REQUIRED },
{ ".size", "gas", dir_size, YASM_DIR_ID_REQUIRED },
{ ".weak", "gas", dir_weak, YASM_DIR_ID_REQUIRED },
{ ".ident", "gas", dir_ident, YASM_DIR_ANY },
{ "type", "nasm", dir_type, YASM_DIR_ID_REQUIRED },
{ "size", "nasm", dir_size, YASM_DIR_ID_REQUIRED },
{ "weak", "nasm", dir_weak, YASM_DIR_ID_REQUIRED },
{ "ident", "nasm", dir_ident, YASM_DIR_ANY },
{ NULL, NULL, NULL, 0 }
};
/* Define objfmt structure -- see objfmt.h for details */
yasm_objfmt_module yasm_elf_LTX_objfmt = {
"ELF",
"elf",
"o",
32,
elf_objfmt_dbgfmt_keywords,
"null",
elf_objfmt_directives,
elf_objfmt_create,
elf_objfmt_output,
elf_objfmt_destroy,
elf_objfmt_add_default_section,
elf_objfmt_section_switch,
elf_objfmt_get_special_sym
};
yasm_objfmt_module yasm_elf32_LTX_objfmt = {
"ELF (32-bit)",
"elf32",
"o",
32,
elf_objfmt_dbgfmt_keywords,
"null",
elf_objfmt_directives,
elf32_objfmt_create,
elf_objfmt_output,
elf_objfmt_destroy,
elf_objfmt_add_default_section,
elf_objfmt_section_switch,
elf_objfmt_get_special_sym
};
yasm_objfmt_module yasm_elf64_LTX_objfmt = {
"ELF (64-bit)",
"elf64",
"o",
64,
elf_objfmt_dbgfmt_keywords,
"null",
elf_objfmt_directives,
elf64_objfmt_create,
elf_objfmt_output,
elf_objfmt_destroy,
elf_objfmt_add_default_section,
elf_objfmt_section_switch,
elf_objfmt_get_special_sym
};