mirror of https://github.com/yasm/yasm.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1397 lines
47 KiB
1397 lines
47 KiB
/* |
|
* 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> |
|
|
|
/* 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; |
|
yasm_symrec *GOT_sym; |
|
} 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; |
|
yasm_objfmt_module yasm_elfx32_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, |
|
yasm_object *object) |
|
{ |
|
elf_symtab_entry *entry = yasm_symrec_get_data(sym, &elf_symrec_data); |
|
|
|
if (!entry) { |
|
/*@only@*/ char *symname = yasm_symrec_get_global_name(sym, object); |
|
elf_strtab_entry *name = |
|
elf_strtab_append_str(objfmt_elf->strtab, symname); |
|
yasm_xfree(symname); |
|
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_object *object) |
|
{ |
|
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, object); |
|
} |
|
|
|
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_object *object) |
|
{ |
|
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, |
|
object); |
|
} |
|
|
|
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, object); |
|
} |
|
|
|
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, info->object); |
|
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, info->object); |
|
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) { |
|
/*@only@*/ char *symname = |
|
yasm_symrec_get_global_name(sym, info->object); |
|
elf_strtab_entry *name = !info->local_names || is_sect ? NULL : |
|
elf_strtab_append_str(info->objfmt_elf->strtab, symname); |
|
yasm_xfree(symname); |
|
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 (strcmp (elf_march->machine, "x32") == 0) |
|
objfmt_elf->objfmt.module = &yasm_elfx32_LTX_objfmt; |
|
else 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 yasm_objfmt * |
|
elfx32_objfmt_create(yasm_object *object) |
|
{ |
|
return elf_objfmt_create_common(object, &yasm_elfx32_LTX_objfmt, 32, 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, 0); |
|
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, 0); |
|
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; |
|
|
|
/* Check for _GLOBAL_OFFSET_TABLE_ symbol reference */ |
|
reloc = elf_reloc_entry_create(sym, wrt, |
|
yasm_intnum_create_uint(bc->offset + offset), value->curpos_rel, |
|
valsize, sym == info->GOT_sym); |
|
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, offset); |
|
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_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; |
|
info.GOT_sym = yasm_symtab_get(object->symtab, "_GLOBAL_OFFSET_TABLE_"); |
|
|
|
/* 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; |
|
} |
|
|
|
/* 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 void |
|
elf_objfmt_init_new_section(yasm_section *sect, unsigned long line) |
|
{ |
|
yasm_object *object = yasm_section_get_object(sect); |
|
const char *sectname = yasm_section_get_name(sect); |
|
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); |
|
|
|
elf_section_type type=SHT_PROGBITS; |
|
elf_size entsize=0; |
|
|
|
if (yasm__strcasecmp(sectname, ".stab")==0) { |
|
entsize = 12; |
|
} else if (yasm__strcasecmp(sectname, ".stabstr")==0) { |
|
type = SHT_STRTAB; |
|
} |
|
|
|
esd = elf_secthead_create(name, type, 0, 0, 0); |
|
elf_secthead_set_entsize(esd, entsize); |
|
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); |
|
} |
|
|
|
static yasm_section * |
|
elf_objfmt_add_default_section(yasm_object *object) |
|
{ |
|
yasm_section *retval; |
|
int isnew; |
|
|
|
retval = yasm_object_get_general(object, ".text", 16, 1, 0, &isnew, 0); |
|
if (isnew) |
|
{ |
|
elf_secthead *esd = yasm_section_get_data(retval, &elf_section_data); |
|
elf_secthead_set_typeflags(esd, SHT_PROGBITS, |
|
SHF_ALLOC + SHF_EXECINSTR); |
|
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; |
|
int stdsect; |
|
}; |
|
|
|
/* 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; |
|
} |
|
|
|
if (data->stdsect && strlen(s) == 0) { |
|
data->gasflags = 1; |
|
return 0; |
|
} |
|
|
|
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; |
|
data.stdsect = 1; |
|
|
|
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 if (strcmp(sectname, ".note.gnu.property") == 0) { |
|
align = 8; |
|
data.type = SHT_NOTE; |
|
data.flags = 0; |
|
} else { |
|
/* Default to code */ |
|
align = 1; |
|
data.stdsect = 0; |
|
} |
|
|
|
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); |
|
|
|
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, object); |
|
} |
|
|
|
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(§_vps); |
|
vp2 = yasm_vp_create_string(NULL, yasm__xstrdup(".comment")); |
|
yasm_vps_append(§_vps, vp2); |
|
comment = elf_objfmt_section_switch(object, §_vps, NULL, line); |
|
yasm_vps_delete(§_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 } |
|
}; |
|
|
|
static const char *elf_nasm_stdmac[] = { |
|
"%imacro type 1+.nolist", |
|
"[type %1]", |
|
"%endmacro", |
|
"%imacro size 1+.nolist", |
|
"[size %1]", |
|
"%endmacro", |
|
"%imacro weak 1+.nolist", |
|
"[weak %1]", |
|
"%endmacro", |
|
NULL |
|
}; |
|
|
|
static const yasm_stdmac elf_objfmt_stdmacs[] = { |
|
{ "nasm", "nasm", elf_nasm_stdmac }, |
|
{ NULL, NULL, NULL } |
|
}; |
|
|
|
/* Define objfmt structure -- see objfmt.h for details */ |
|
yasm_objfmt_module yasm_elf_LTX_objfmt = { |
|
"ELF", |
|
"elf", |
|
"o", |
|
32, |
|
0, |
|
elf_objfmt_dbgfmt_keywords, |
|
"null", |
|
elf_objfmt_directives, |
|
elf_objfmt_stdmacs, |
|
elf_objfmt_create, |
|
elf_objfmt_output, |
|
elf_objfmt_destroy, |
|
elf_objfmt_add_default_section, |
|
elf_objfmt_init_new_section, |
|
elf_objfmt_section_switch, |
|
elf_objfmt_get_special_sym |
|
}; |
|
|
|
yasm_objfmt_module yasm_elf32_LTX_objfmt = { |
|
"ELF (32-bit)", |
|
"elf32", |
|
"o", |
|
32, |
|
0, |
|
elf_objfmt_dbgfmt_keywords, |
|
"null", |
|
elf_objfmt_directives, |
|
elf_objfmt_stdmacs, |
|
elf32_objfmt_create, |
|
elf_objfmt_output, |
|
elf_objfmt_destroy, |
|
elf_objfmt_add_default_section, |
|
elf_objfmt_init_new_section, |
|
elf_objfmt_section_switch, |
|
elf_objfmt_get_special_sym |
|
}; |
|
|
|
yasm_objfmt_module yasm_elf64_LTX_objfmt = { |
|
"ELF (64-bit)", |
|
"elf64", |
|
"o", |
|
64, |
|
0, |
|
elf_objfmt_dbgfmt_keywords, |
|
"null", |
|
elf_objfmt_directives, |
|
elf_objfmt_stdmacs, |
|
elf64_objfmt_create, |
|
elf_objfmt_output, |
|
elf_objfmt_destroy, |
|
elf_objfmt_add_default_section, |
|
elf_objfmt_init_new_section, |
|
elf_objfmt_section_switch, |
|
elf_objfmt_get_special_sym |
|
}; |
|
|
|
yasm_objfmt_module yasm_elfx32_LTX_objfmt = { |
|
"ELF (x32)", |
|
"elfx32", |
|
"o", |
|
64, |
|
0, |
|
elf_objfmt_dbgfmt_keywords, |
|
"null", |
|
elf_objfmt_directives, |
|
elf_objfmt_stdmacs, |
|
elfx32_objfmt_create, |
|
elf_objfmt_output, |
|
elf_objfmt_destroy, |
|
elf_objfmt_add_default_section, |
|
elf_objfmt_init_new_section, |
|
elf_objfmt_section_switch, |
|
elf_objfmt_get_special_sym |
|
};
|
|
|