/* * 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 /*@unused@*/ RCSID("$Id: elf-objfmt.c 2310 2010-03-28 19:28:54Z peter $"); /* 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 #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; 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 (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, 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; iflags |= 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 { /* 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 };