/* Finalize operations on the assembler context, free all resources. Copyright (C) 2002, 2003 Red Hat, Inc. Written by Ulrich Drepper <drepper@redhat.com>, 2002. This program is Open Source software; you can redistribute it and/or modify it under the terms of the Open Software License version 1.0 as published by the Open Source Initiative. You should have received a copy of the Open Software License along with this program; if not, you may obtain a copy of the Open Software License version 1.0 from http://www.opensource.org/licenses/osl.php or by writing the Open Source Initiative c/o Lawrence Rosen, Esq., 3001 King Ranch Road, Ukiah, CA 95482. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <assert.h> #include <error.h> #include <libintl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> #include <libasmP.h> #include <libelf.h> #include <system.h> static int text_end (AsmCtx_t *ctx) { // XXX Does anything have to be done? return 0; } static int binary_end (AsmCtx_t *ctx) { void *symtab = NULL; struct Ebl_Strent *symscn_strent = NULL; struct Ebl_Strent *strscn_strent = NULL; struct Ebl_Strent *xndxscn_strent = NULL; Elf_Scn *shstrscn; struct Ebl_Strent *shstrscn_strent; size_t shstrscnndx; size_t symscnndx = 0; size_t strscnndx = 0; size_t xndxscnndx = 0; Elf_Data *data; Elf_Data *shstrtabdata; Elf_Data *strtabdata = NULL; Elf_Data *xndxdata = NULL; GElf_Shdr shdr_mem; GElf_Shdr *shdr; GElf_Ehdr ehdr_mem; GElf_Ehdr *ehdr; AsmScn_t *asmscn; int result = 0; /* Iterate over the created sections and compute the offsets of the various subsections and fill in the content. */ for (asmscn = ctx->section_list; asmscn != NULL; asmscn = asmscn->allnext) { #if 0 Elf_Scn *scn = elf_getscn (ctx->out.elf, asmscn->data.main.scnndx); #else Elf_Scn *scn = asmscn->data.main.scn; #endif off_t offset = 0; AsmScn_t *asmsubscn = asmscn; do { struct AsmData *content = asmsubscn->content; bool first = true; offset = ((offset + asmsubscn->max_align - 1) & ~(asmsubscn->max_align - 1)); /* Update the offset for this subsection. This field now stores the offset of the first by in this subsection. */ asmsubscn->offset = offset; /* Note that the content list is circular. */ if (content != NULL) do { Elf_Data *newdata = elf_newdata (scn); if (newdata == NULL) error (EXIT_FAILURE, 0, _("cannot create section for output file: %s"), elf_errmsg (-1)); newdata->d_buf = content->data; newdata->d_type = ELF_T_BYTE; newdata->d_size = content->len; newdata->d_off = offset; newdata->d_align = first ? asmsubscn->max_align : 1; offset += content->len; } while ((content = content->next) != asmsubscn->content); } while ((asmsubscn = asmsubscn->subnext) != NULL); } /* Create the symbol table if necessary. */ if (ctx->nsymbol_tab > 0) { Elf_Scn *symscn; Elf_Scn *strscn; AsmSym_t *sym; int ptr_local; int ptr_nonlocal; GElf_Sym syment; uint32_t *xshndx = NULL; void *runp; /* Create the symbol table and string table section names. */ symscn_strent = ebl_strtabadd (ctx->section_strtab, ".symtab", 8); strscn_strent = ebl_strtabadd (ctx->section_strtab, ".strtab", 8); /* Create the symbol string table section. */ strscn = elf_newscn (ctx->out.elf); strtabdata = elf_newdata (strscn); shdr = gelf_getshdr (strscn, &shdr_mem); if (strtabdata == NULL || shdr == NULL) error (EXIT_FAILURE, 0, _("cannot create section for output file: %s"), elf_errmsg (-1)); strscnndx = elf_ndxscn (strscn); ebl_strtabfinalize (ctx->symbol_strtab, strtabdata); shdr->sh_type = SHT_STRTAB; assert (shdr->sh_entsize == 0); (void) gelf_update_shdr (strscn, shdr); /* Create the symbol table section. */ symscn = elf_newscn (ctx->out.elf); data = elf_newdata (symscn); shdr = gelf_getshdr (symscn, &shdr_mem); if (data == NULL || shdr == NULL) error (EXIT_FAILURE, 0, _("cannot create section for output file: %s"), elf_errmsg (-1)); symscnndx = elf_ndxscn (symscn); /* We know how many symbols there will be in the symbol table. */ data->d_size = gelf_fsize (ctx->out.elf, ELF_T_SYM, ctx->nsymbol_tab + 1, EV_CURRENT); symtab = malloc (data->d_size); if (symtab == NULL) return -1; data->d_buf = symtab; data->d_type = ELF_T_SYM; data->d_off = 0; /* Clear the first entry. */ memset (&syment, '\0', sizeof (syment)); (void) gelf_update_sym (data, 0, &syment); /* Iterate over the symbol table. */ runp = NULL; ptr_local = 1; /* Start with index 1; zero remains unused. */ ptr_nonlocal = ctx->nsymbol_tab; while ((sym = asm_symbol_tab_iterate (&ctx->symbol_tab, &runp)) != NULL) if (asm_emit_symbol_p (ebl_string (sym->strent))) { int ptr; Elf32_Word ndx; Elf_Scn *scn; assert (ptr_local <= ptr_nonlocal); syment.st_name = ebl_strtaboffset (sym->strent); syment.st_info = GELF_ST_INFO (sym->binding, sym->type); syment.st_other = 0; syment.st_value = sym->scn->offset + sym->offset; syment.st_size = sym->size; /* Add local symbols at the beginning, the other from the end. */ ptr = sym->binding == STB_LOCAL ? ptr_local++ : ptr_nonlocal--; /* Determine the section index. We have to handle the overflow correctly. */ scn = (sym->scn->subsection_id == 0 ? sym->scn->data.main.scn : sym->scn->data.up->data.main.scn); if (unlikely (scn == ASM_ABS_SCN)) ndx = SHN_ABS; else if (unlikely (scn == ASM_COM_SCN)) ndx = SHN_COMMON; else if (unlikely ((ndx = elf_ndxscn (scn)) >= SHN_LORESERVE)) { if (unlikely (xshndx == NULL)) { /* The extended section index section does not yet exist. */ Elf_Scn *xndxscn; size_t symscnndx = elf_ndxscn (symscn); xndxscn = elf_newscn (ctx->out.elf); xndxdata = elf_newdata (xndxscn); shdr = gelf_getshdr (xndxscn, &shdr_mem); if (xndxdata == NULL || shdr == NULL) error (EXIT_FAILURE, 0, _("\ cannot create extended section index table: %s"), elf_errmsg (-1)); xndxscnndx = elf_ndxscn (xndxscn); shdr->sh_type = SHT_SYMTAB_SHNDX; shdr->sh_entsize = sizeof (Elf32_Word); shdr->sh_addralign = sizeof (Elf32_Word); shdr->sh_link = symscnndx; (void) gelf_update_shdr (xndxscn, shdr); xndxscn_strent = ebl_strtabadd (ctx->section_strtab, ".symtab_shndx", 14); /* Note that using 'elf32_fsize' instead of 'gelf_fsize' here is correct. */ xndxdata->d_size = elf32_fsize (ELF_T_WORD, ctx->nsymbol_tab + 1, EV_CURRENT); xshndx = xndxdata->d_buf = calloc (1, xndxdata->d_size); if (xshndx == NULL) return -1; /* Using ELF_T_WORD here relies on the fact that the 32- and 64-bit types are the same size. */ xndxdata->d_type = ELF_T_WORD; xndxdata->d_off = 0; } /* Store the real section index in the extended setion index table. */ assert ((size_t) ptr < ctx->nsymbol_tab + 1); xshndx[ptr] = ndx; /* And signal that this happened. */ ndx = SHN_XINDEX; } syment.st_shndx = ndx; /* Remember where we put the symbol. */ sym->symidx = ptr; (void) gelf_update_sym (data, ptr, &syment); } assert (ptr_local == ptr_nonlocal + 1); shdr->sh_type = SHT_SYMTAB; shdr->sh_link = strscnndx; shdr->sh_info = ptr_local; shdr->sh_entsize = gelf_fsize (ctx->out.elf, ELF_T_SYM, 1, EV_CURRENT); shdr->sh_addralign = gelf_fsize (ctx->out.elf, ELF_T_ADDR, 1, EV_CURRENT); (void) gelf_update_shdr (symscn, shdr); } /* Create the section header string table section and fill in the references in the section headers. */ shstrscn = elf_newscn (ctx->out.elf); shstrtabdata = elf_newdata (shstrscn); shdr = gelf_getshdr (shstrscn, &shdr_mem); if (shstrscn == NULL || shstrtabdata == NULL || shdr == NULL) error (EXIT_FAILURE, 0, _("cannot create section for output file: %s"), elf_errmsg (-1)); /* Add the name of the section header string table. */ shstrscn_strent = ebl_strtabadd (ctx->section_strtab, ".shstrtab", 10); ebl_strtabfinalize (ctx->section_strtab, shstrtabdata); shdr->sh_type = SHT_STRTAB; assert (shdr->sh_entsize == 0); shdr->sh_name = ebl_strtaboffset (shstrscn_strent); (void) gelf_update_shdr (shstrscn, shdr); /* Create the section groups. */ if (ctx->groups != NULL) { AsmScnGrp_t *runp = ctx->groups->next; do { Elf_Scn *scn; GElf_Shdr shdr_mem; GElf_Shdr *shdr; Elf_Data *data; Elf32_Word *grpdata; scn = runp->scn; assert (scn != NULL); shdr = gelf_getshdr (scn, &shdr_mem); assert (shdr != NULL); data = elf_newdata (scn); if (data == NULL) error (EXIT_FAILURE, 0, _("cannot create section group for output file: %s"), elf_errmsg (-1)); /* It is correct to use 'elf32_fsize' instead of 'gelf_fsize' here. */ data->d_size = elf32_fsize (ELF_T_WORD, runp->nmembers + 1, EV_CURRENT); grpdata = data->d_buf = malloc (data->d_size); if (grpdata == NULL) return -1; data->d_type = ELF_T_WORD; data->d_off = 0; data->d_align = elf32_fsize (ELF_T_WORD, 1, EV_CURRENT); /* The first word of the section is filled with the flag word. */ *grpdata++ = runp->flags; if (runp->members != NULL) { AsmScn_t *member = runp->members->data.main.next_in_group; do { /* Only sections, not subsections, can be registered as member of a group. The subsections get automatically included. */ assert (member->subsection_id == 0); *grpdata++ = elf_ndxscn (member->data.main.scn); } while ((member = member->data.main.next_in_group) != runp->members->data.main.next_in_group); } /* Construct the section header. */ shdr->sh_name = ebl_strtaboffset (runp->strent); shdr->sh_type = SHT_GROUP; shdr->sh_flags = 0; shdr->sh_link = symscnndx; /* If the user did not specify a signature we use the initial empty symbol in the symbol table as the signature. */ shdr->sh_info = (runp->signature != NULL ? runp->signature->symidx : 0); (void) gelf_update_shdr (scn, shdr); } while ((runp = runp->next) != ctx->groups->next); } /* Add the name to the symbol section. */ if (likely (symscnndx != 0)) { Elf_Scn *scn = elf_getscn (ctx->out.elf, symscnndx); shdr = gelf_getshdr (scn, &shdr_mem); shdr->sh_name = ebl_strtaboffset (symscn_strent); (void) gelf_update_shdr (scn, shdr); /* Add the name to the string section. */ assert (strscnndx != 0); scn = elf_getscn (ctx->out.elf, strscnndx); shdr = gelf_getshdr (scn, &shdr_mem); shdr->sh_name = ebl_strtaboffset (strscn_strent); (void) gelf_update_shdr (scn, shdr); /* Add the name to the extended symbol index section. */ if (xndxscnndx != 0) { scn = elf_getscn (ctx->out.elf, xndxscnndx); shdr = gelf_getshdr (scn, &shdr_mem); shdr->sh_name = ebl_strtaboffset (xndxscn_strent); (void) gelf_update_shdr (scn, shdr); } } /* Iterate over the created sections and fill in the names. */ for (asmscn = ctx->section_list; asmscn != NULL; asmscn = asmscn->allnext) { shdr = gelf_getshdr (asmscn->data.main.scn, &shdr_mem); /* This better should not fail. */ assert (shdr != NULL); shdr->sh_name = ebl_strtaboffset (asmscn->data.main.strent); /* We now know the maximum alignment. */ shdr->sh_addralign = asmscn->max_align; (void) gelf_update_shdr (asmscn->data.main.scn, shdr); } /* Put the reference to the section header string table in the ELF header. */ ehdr = gelf_getehdr (ctx->out.elf, &ehdr_mem); assert (ehdr != NULL); shstrscnndx = elf_ndxscn (shstrscn); if (unlikely (shstrscnndx > SHN_HIRESERVE) || unlikely (shstrscnndx == SHN_XINDEX)) { /* The index of the section header string sectio is too large. */ Elf_Scn *scn = elf_getscn (ctx->out.elf, 0); /* Get the header for the zeroth section. */ shdr = gelf_getshdr (scn, &shdr_mem); /* This better does not fail. */ assert (shdr != NULL); /* The sh_link field of the zeroth section header contains the value. */ shdr->sh_link = shstrscnndx; (void) gelf_update_shdr (scn, shdr); /* This is the sign for the overflow. */ ehdr->e_shstrndx = SHN_XINDEX; } else ehdr->e_shstrndx = elf_ndxscn (shstrscn); gelf_update_ehdr (ctx->out.elf, ehdr); /* Write out the ELF file. */ if (unlikely (elf_update (ctx->out.elf, ELF_C_WRITE_MMAP)) < 0) { __libasm_seterrno (ASM_E_LIBELF); result = -1; } /* We do not need the section header and symbol string tables anymore. */ free (shstrtabdata->d_buf); if (strtabdata != NULL) free (strtabdata->d_buf); /* We might have allocated the extended symbol table index. */ if (xndxdata != NULL) free (xndxdata->d_buf); /* Free section groups memory. */ AsmScnGrp_t *scngrp = ctx->groups; if (scngrp != NULL) do free (elf_getdata (scngrp->scn, NULL)->d_buf); while ((scngrp = scngrp->next) != ctx->groups); /* Finalize the ELF handling. */ if (unlikely (elf_end (ctx->out.elf)) != 0) { __libasm_seterrno (ASM_E_LIBELF); result = -1; } /* Free the temporary resources. */ free (symtab); return result; } int asm_end (ctx) AsmCtx_t *ctx; { int result; if (ctx == NULL) /* Something went wrong earlier. */ return -1; result = unlikely (ctx->textp) ? text_end (ctx) : binary_end (ctx); if (result != 0) return result; /* Make the new file globally readable and user/group-writable. */ if (fchmod (ctx->fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) != 0) { __libasm_seterrno (ASM_E_CANNOT_CHMOD); return -1; } /* Rename output file. */ if (rename (ctx->tmp_fname, ctx->fname) != 0) { __libasm_seterrno (ASM_E_CANNOT_RENAME); return -1; } /* Free the resources. */ __libasm_finictx (ctx); return 0; } static void free_section (AsmScn_t *scnp) { void *oldp; if (scnp->subnext != NULL) free_section (scnp->subnext); struct AsmData *data = scnp->content; if (data != NULL) do { oldp = data; data = data->next; free (oldp); } while (oldp != scnp->content); free (scnp); } void __libasm_finictx (ctx) AsmCtx_t *ctx; { /* Iterate through section table and free individual entries. */ AsmScn_t *scn = ctx->section_list; while (scn != NULL) { AsmScn_t *oldp = scn; scn = scn->allnext; free_section (oldp); } /* Free the resources of the symbol table. */ void *runp = NULL; AsmSym_t *sym; while ((sym = asm_symbol_tab_iterate (&ctx->symbol_tab, &runp)) != NULL) free (sym); asm_symbol_tab_free (&ctx->symbol_tab); /* Free section groups. */ AsmScnGrp_t *scngrp = ctx->groups; if (scngrp != NULL) do { AsmScnGrp_t *oldp = scngrp; scngrp = scngrp->next; free (oldp); } while (scngrp != ctx->groups); if (unlikely (ctx->textp)) { /* Close the stream. */ fclose (ctx->out.file); } else { /* Close the output file. */ /* XXX We should test for errors here but what would we do if we'd find any. */ (void) close (ctx->fd); /* And the string tables. */ ebl_strtabfree (ctx->section_strtab); ebl_strtabfree (ctx->symbol_strtab); } /* Initialize the lock. */ rwlock_fini (ctx->lock); /* Finally free the data structure. */ free (ctx); }