Home | History | Annotate | Download | only in libasm
      1 /* Finalize operations on the assembler context, free all resources.
      2    Copyright (C) 2002, 2003 Red Hat, Inc.
      3    Written by Ulrich Drepper <drepper (at) redhat.com>, 2002.
      4 
      5    This program is Open Source software; you can redistribute it and/or
      6    modify it under the terms of the Open Software License version 1.0 as
      7    published by the Open Source Initiative.
      8 
      9    You should have received a copy of the Open Software License along
     10    with this program; if not, you may obtain a copy of the Open Software
     11    License version 1.0 from http://www.opensource.org/licenses/osl.php or
     12    by writing the Open Source Initiative c/o Lawrence Rosen, Esq.,
     13    3001 King Ranch Road, Ukiah, CA 95482.   */
     14 
     15 #ifdef HAVE_CONFIG_H
     16 # include <config.h>
     17 #endif
     18 
     19 #include <assert.h>
     20 #include <error.h>
     21 #include <libintl.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include <unistd.h>
     26 #include <sys/stat.h>
     27 
     28 #include <libasmP.h>
     29 #include <libelf.h>
     30 #include <system.h>
     31 
     32 
     33 static int
     34 text_end (AsmCtx_t *ctx)
     35 {
     36   // XXX Does anything have to be done?
     37   return 0;
     38 }
     39 
     40 
     41 static int
     42 binary_end (AsmCtx_t *ctx)
     43 {
     44   void *symtab = NULL;
     45   struct Ebl_Strent *symscn_strent = NULL;
     46   struct Ebl_Strent *strscn_strent = NULL;
     47   struct Ebl_Strent *xndxscn_strent = NULL;
     48   Elf_Scn *shstrscn;
     49   struct Ebl_Strent *shstrscn_strent;
     50   size_t shstrscnndx;
     51   size_t symscnndx = 0;
     52   size_t strscnndx = 0;
     53   size_t xndxscnndx = 0;
     54   Elf_Data *data;
     55   Elf_Data *shstrtabdata;
     56   Elf_Data *strtabdata = NULL;
     57   Elf_Data *xndxdata = NULL;
     58   GElf_Shdr shdr_mem;
     59   GElf_Shdr *shdr;
     60   GElf_Ehdr ehdr_mem;
     61   GElf_Ehdr *ehdr;
     62   AsmScn_t *asmscn;
     63   int result = 0;
     64 
     65   /* Iterate over the created sections and compute the offsets of the
     66      various subsections and fill in the content.  */
     67   for (asmscn = ctx->section_list; asmscn != NULL; asmscn = asmscn->allnext)
     68     {
     69 #if 0
     70       Elf_Scn *scn = elf_getscn (ctx->out.elf, asmscn->data.main.scnndx);
     71 #else
     72       Elf_Scn *scn = asmscn->data.main.scn;
     73 #endif
     74       off_t offset = 0;
     75       AsmScn_t *asmsubscn = asmscn;
     76 
     77       do
     78 	{
     79 	  struct AsmData *content = asmsubscn->content;
     80 	  bool first = true;
     81 
     82 	  offset = ((offset + asmsubscn->max_align - 1)
     83 		    & ~(asmsubscn->max_align - 1));
     84 
     85 	  /* Update the offset for this subsection.  This field now
     86 	     stores the offset of the first by in this subsection.  */
     87 	  asmsubscn->offset = offset;
     88 
     89 	  /* Note that the content list is circular.  */
     90 	  if (content != NULL)
     91 	    do
     92 	      {
     93 		Elf_Data *newdata = elf_newdata (scn);
     94 
     95 		if (newdata == NULL)
     96 		  error (EXIT_FAILURE, 0,
     97 			 _("cannot create section for output file: %s"),
     98 			 elf_errmsg (-1));
     99 
    100 		newdata->d_buf = content->data;
    101 		newdata->d_type = ELF_T_BYTE;
    102 		newdata->d_size = content->len;
    103 		newdata->d_off = offset;
    104 		newdata->d_align = first ? asmsubscn->max_align : 1;
    105 
    106 		offset += content->len;
    107 	      }
    108 	    while ((content = content->next) != asmsubscn->content);
    109 	}
    110       while ((asmsubscn = asmsubscn->subnext) != NULL);
    111     }
    112 
    113 
    114   /* Create the symbol table if necessary.  */
    115   if (ctx->nsymbol_tab > 0)
    116     {
    117       Elf_Scn *symscn;
    118       Elf_Scn *strscn;
    119       AsmSym_t *sym;
    120       int ptr_local;
    121       int ptr_nonlocal;
    122       GElf_Sym syment;
    123       uint32_t *xshndx = NULL;
    124       void *runp;
    125 
    126       /* Create the symbol table and string table section names.  */
    127       symscn_strent = ebl_strtabadd (ctx->section_strtab, ".symtab", 8);
    128       strscn_strent = ebl_strtabadd (ctx->section_strtab, ".strtab", 8);
    129 
    130       /* Create the symbol string table section.  */
    131       strscn = elf_newscn (ctx->out.elf);
    132       strtabdata = elf_newdata (strscn);
    133       shdr = gelf_getshdr (strscn, &shdr_mem);
    134       if (strtabdata == NULL || shdr == NULL)
    135 	error (EXIT_FAILURE, 0, _("cannot create section for output file: %s"),
    136 	       elf_errmsg (-1));
    137       strscnndx = elf_ndxscn (strscn);
    138 
    139       ebl_strtabfinalize (ctx->symbol_strtab, strtabdata);
    140 
    141       shdr->sh_type = SHT_STRTAB;
    142       assert (shdr->sh_entsize == 0);
    143 
    144       (void) gelf_update_shdr (strscn, shdr);
    145 
    146       /* Create the symbol table section.  */
    147       symscn = elf_newscn (ctx->out.elf);
    148       data = elf_newdata (symscn);
    149       shdr = gelf_getshdr (symscn, &shdr_mem);
    150       if (data == NULL || shdr == NULL)
    151 	error (EXIT_FAILURE, 0, _("cannot create section for output file: %s"),
    152 	       elf_errmsg (-1));
    153       symscnndx = elf_ndxscn (symscn);
    154 
    155       /* We know how many symbols there will be in the symbol table.  */
    156       data->d_size = gelf_fsize (ctx->out.elf, ELF_T_SYM,
    157 				 ctx->nsymbol_tab + 1, EV_CURRENT);
    158       symtab = malloc (data->d_size);
    159       if (symtab == NULL)
    160 	return -1;
    161       data->d_buf = symtab;
    162       data->d_type = ELF_T_SYM;
    163       data->d_off = 0;
    164 
    165       /* Clear the first entry.  */
    166       memset (&syment, '\0', sizeof (syment));
    167       (void) gelf_update_sym (data, 0, &syment);
    168 
    169       /* Iterate over the symbol table.  */
    170       runp = NULL;
    171       ptr_local = 1;		/* Start with index 1; zero remains unused.  */
    172       ptr_nonlocal = ctx->nsymbol_tab;
    173       while ((sym = asm_symbol_tab_iterate (&ctx->symbol_tab, &runp)) != NULL)
    174 	if (asm_emit_symbol_p (ebl_string (sym->strent)))
    175 	  {
    176 	    int ptr;
    177 	    Elf32_Word ndx;
    178 	    Elf_Scn *scn;
    179 
    180 	    assert (ptr_local <= ptr_nonlocal);
    181 
    182 	    syment.st_name = ebl_strtaboffset (sym->strent);
    183 	    syment.st_info = GELF_ST_INFO (sym->binding, sym->type);
    184 	    syment.st_other = 0;
    185 	    syment.st_value = sym->scn->offset + sym->offset;
    186 	    syment.st_size = sym->size;
    187 
    188 	    /* Add local symbols at the beginning, the other from
    189 	       the end.  */
    190 	    ptr = sym->binding == STB_LOCAL ? ptr_local++ : ptr_nonlocal--;
    191 
    192 	    /* Determine the section index.  We have to handle the
    193 	       overflow correctly.  */
    194 	    scn = (sym->scn->subsection_id == 0
    195 		   ? sym->scn->data.main.scn
    196 		   : sym->scn->data.up->data.main.scn);
    197 
    198 	    if (unlikely (scn == ASM_ABS_SCN))
    199 	      ndx = SHN_ABS;
    200 	    else if (unlikely (scn == ASM_COM_SCN))
    201 	      ndx = SHN_COMMON;
    202 	    else if (unlikely ((ndx = elf_ndxscn (scn)) >= SHN_LORESERVE))
    203 	      {
    204 		if (unlikely (xshndx == NULL))
    205 		  {
    206 		    /* The extended section index section does not yet
    207 		       exist.  */
    208 		    Elf_Scn *xndxscn;
    209 		    size_t symscnndx = elf_ndxscn (symscn);
    210 
    211 		    xndxscn = elf_newscn (ctx->out.elf);
    212 		    xndxdata = elf_newdata (xndxscn);
    213 		    shdr = gelf_getshdr (xndxscn, &shdr_mem);
    214 		    if (xndxdata == NULL || shdr == NULL)
    215 		      error (EXIT_FAILURE, 0, _("\
    216 cannot create extended section index table: %s"),
    217 			     elf_errmsg (-1));
    218 		    xndxscnndx = elf_ndxscn (xndxscn);
    219 
    220 		    shdr->sh_type = SHT_SYMTAB_SHNDX;
    221 		    shdr->sh_entsize = sizeof (Elf32_Word);
    222 		    shdr->sh_addralign = sizeof (Elf32_Word);
    223 		    shdr->sh_link = symscnndx;
    224 
    225 		    (void) gelf_update_shdr (xndxscn, shdr);
    226 
    227 		    xndxscn_strent = ebl_strtabadd (ctx->section_strtab,
    228 						    ".symtab_shndx", 14);
    229 
    230 		    /* Note that using 'elf32_fsize' instead of
    231 		       'gelf_fsize' here is correct.  */
    232 		    xndxdata->d_size = elf32_fsize (ELF_T_WORD,
    233 						    ctx->nsymbol_tab + 1,
    234 						    EV_CURRENT);
    235 		    xshndx = xndxdata->d_buf = calloc (1, xndxdata->d_size);
    236 		    if (xshndx == NULL)
    237 		      return -1;
    238 		    /* Using ELF_T_WORD here relies on the fact that the
    239 		       32- and 64-bit types are the same size.  */
    240 		    xndxdata->d_type = ELF_T_WORD;
    241 		    xndxdata->d_off = 0;
    242 		  }
    243 
    244 		/* Store the real section index in the extended setion
    245 		   index table.  */
    246 		assert ((size_t) ptr < ctx->nsymbol_tab + 1);
    247 		xshndx[ptr] = ndx;
    248 
    249 		/* And signal that this happened.  */
    250 		ndx = SHN_XINDEX;
    251 	      }
    252 	    syment.st_shndx = ndx;
    253 
    254 	    /* Remember where we put the symbol.  */
    255 	    sym->symidx = ptr;
    256 
    257 	    (void) gelf_update_sym (data, ptr, &syment);
    258 	  }
    259 
    260       assert (ptr_local == ptr_nonlocal + 1);
    261 
    262       shdr->sh_type = SHT_SYMTAB;
    263       shdr->sh_link = strscnndx;
    264       shdr->sh_info = ptr_local;
    265       shdr->sh_entsize = gelf_fsize (ctx->out.elf, ELF_T_SYM, 1, EV_CURRENT);
    266       shdr->sh_addralign = gelf_fsize (ctx->out.elf, ELF_T_ADDR, 1,
    267 				       EV_CURRENT);
    268 
    269       (void) gelf_update_shdr (symscn, shdr);
    270     }
    271 
    272 
    273   /* Create the section header string table section and fill in the
    274      references in the section headers.  */
    275   shstrscn = elf_newscn (ctx->out.elf);
    276   shstrtabdata = elf_newdata (shstrscn);
    277   shdr = gelf_getshdr (shstrscn, &shdr_mem);
    278   if (shstrscn == NULL || shstrtabdata == NULL || shdr == NULL)
    279     error (EXIT_FAILURE, 0, _("cannot create section for output file: %s"),
    280 	   elf_errmsg (-1));
    281 
    282 
    283   /* Add the name of the section header string table.  */
    284   shstrscn_strent = ebl_strtabadd (ctx->section_strtab, ".shstrtab", 10);
    285 
    286   ebl_strtabfinalize (ctx->section_strtab, shstrtabdata);
    287 
    288   shdr->sh_type = SHT_STRTAB;
    289   assert (shdr->sh_entsize == 0);
    290   shdr->sh_name = ebl_strtaboffset (shstrscn_strent);
    291 
    292   (void) gelf_update_shdr (shstrscn, shdr);
    293 
    294 
    295   /* Create the section groups.  */
    296   if (ctx->groups != NULL)
    297     {
    298       AsmScnGrp_t *runp = ctx->groups->next;
    299 
    300       do
    301 	{
    302 	  Elf_Scn *scn;
    303 	  GElf_Shdr shdr_mem;
    304 	  GElf_Shdr *shdr;
    305 	  Elf_Data *data;
    306 	  Elf32_Word *grpdata;
    307 
    308 	  scn = runp->scn;
    309 	  assert (scn != NULL);
    310 	  shdr = gelf_getshdr (scn, &shdr_mem);
    311 	  assert (shdr != NULL);
    312 
    313 	  data = elf_newdata (scn);
    314 	  if (data == NULL)
    315 	    error (EXIT_FAILURE, 0,
    316 		   _("cannot create section group for output file: %s"),
    317 		   elf_errmsg (-1));
    318 
    319 	  /* It is correct to use 'elf32_fsize' instead of 'gelf_fsize'
    320 	     here.  */
    321 	  data->d_size = elf32_fsize (ELF_T_WORD, runp->nmembers + 1,
    322 				      EV_CURRENT);
    323 	  grpdata = data->d_buf = malloc (data->d_size);
    324 	  if (grpdata == NULL)
    325 	    return -1;
    326 	  data->d_type = ELF_T_WORD;
    327 	  data->d_off = 0;
    328 	  data->d_align = elf32_fsize (ELF_T_WORD, 1, EV_CURRENT);
    329 
    330 	  /* The first word of the section is filled with the flag word.  */
    331 	  *grpdata++ = runp->flags;
    332 
    333 	  if (runp->members != NULL)
    334 	    {
    335 	      AsmScn_t *member = runp->members->data.main.next_in_group;
    336 
    337 	      do
    338 		{
    339 		  /* Only sections, not subsections, can be registered
    340 		     as member of a group.  The subsections get
    341 		     automatically included.  */
    342 		  assert (member->subsection_id == 0);
    343 
    344 		  *grpdata++ = elf_ndxscn (member->data.main.scn);
    345 		}
    346 	      while ((member = member->data.main.next_in_group)
    347 		     != runp->members->data.main.next_in_group);
    348 	    }
    349 
    350 	  /* Construct the section header.  */
    351 	  shdr->sh_name = ebl_strtaboffset (runp->strent);
    352 	  shdr->sh_type = SHT_GROUP;
    353 	  shdr->sh_flags = 0;
    354 	  shdr->sh_link = symscnndx;
    355 	  /* If the user did not specify a signature we use the initial
    356 	     empty symbol in the symbol table as the signature.  */
    357 	  shdr->sh_info = (runp->signature != NULL
    358 			   ? runp->signature->symidx : 0);
    359 
    360 	  (void) gelf_update_shdr (scn, shdr);
    361 	}
    362       while ((runp = runp->next) != ctx->groups->next);
    363     }
    364 
    365 
    366   /* Add the name to the symbol section.  */
    367   if (likely (symscnndx != 0))
    368     {
    369       Elf_Scn *scn = elf_getscn (ctx->out.elf, symscnndx);
    370 
    371       shdr = gelf_getshdr (scn, &shdr_mem);
    372 
    373       shdr->sh_name = ebl_strtaboffset (symscn_strent);
    374 
    375       (void) gelf_update_shdr (scn, shdr);
    376 
    377 
    378       /* Add the name to the string section.  */
    379       assert (strscnndx != 0);
    380       scn = elf_getscn (ctx->out.elf, strscnndx);
    381 
    382       shdr = gelf_getshdr (scn, &shdr_mem);
    383 
    384       shdr->sh_name = ebl_strtaboffset (strscn_strent);
    385 
    386       (void) gelf_update_shdr (scn, shdr);
    387 
    388 
    389       /* Add the name to the extended symbol index section.  */
    390       if (xndxscnndx != 0)
    391 	{
    392 	  scn = elf_getscn (ctx->out.elf, xndxscnndx);
    393 
    394 	  shdr = gelf_getshdr (scn, &shdr_mem);
    395 
    396 	  shdr->sh_name = ebl_strtaboffset (xndxscn_strent);
    397 
    398 	  (void) gelf_update_shdr (scn, shdr);
    399 	}
    400     }
    401 
    402 
    403   /* Iterate over the created sections and fill in the names.  */
    404   for (asmscn = ctx->section_list; asmscn != NULL; asmscn = asmscn->allnext)
    405     {
    406       shdr = gelf_getshdr (asmscn->data.main.scn, &shdr_mem);
    407       /* This better should not fail.  */
    408       assert (shdr != NULL);
    409 
    410       shdr->sh_name = ebl_strtaboffset (asmscn->data.main.strent);
    411 
    412       /* We now know the maximum alignment.  */
    413       shdr->sh_addralign = asmscn->max_align;
    414 
    415       (void) gelf_update_shdr (asmscn->data.main.scn, shdr);
    416     }
    417 
    418   /* Put the reference to the section header string table in the ELF
    419      header.  */
    420   ehdr = gelf_getehdr (ctx->out.elf, &ehdr_mem);
    421   assert (ehdr != NULL);
    422 
    423   shstrscnndx = elf_ndxscn (shstrscn);
    424   if (unlikely (shstrscnndx > SHN_HIRESERVE)
    425       || unlikely (shstrscnndx == SHN_XINDEX))
    426     {
    427       /* The index of the section header string sectio is too large.  */
    428       Elf_Scn *scn = elf_getscn (ctx->out.elf, 0);
    429 
    430       /* Get the header for the zeroth section.  */
    431       shdr = gelf_getshdr (scn, &shdr_mem);
    432       /* This better does not fail.  */
    433       assert (shdr != NULL);
    434 
    435       /* The sh_link field of the zeroth section header contains the value.  */
    436       shdr->sh_link = shstrscnndx;
    437 
    438       (void) gelf_update_shdr (scn, shdr);
    439 
    440       /* This is the sign for the overflow.  */
    441       ehdr->e_shstrndx = SHN_XINDEX;
    442     }
    443   else
    444     ehdr->e_shstrndx = elf_ndxscn (shstrscn);
    445 
    446   gelf_update_ehdr (ctx->out.elf, ehdr);
    447 
    448   /* Write out the ELF file.  */
    449   if (unlikely (elf_update (ctx->out.elf, ELF_C_WRITE_MMAP)) < 0)
    450     {
    451       __libasm_seterrno (ASM_E_LIBELF);
    452       result = -1;
    453     }
    454 
    455   /* We do not need the section header and symbol string tables anymore.  */
    456   free (shstrtabdata->d_buf);
    457   if (strtabdata != NULL)
    458     free (strtabdata->d_buf);
    459   /* We might have allocated the extended symbol table index.  */
    460   if (xndxdata != NULL)
    461     free (xndxdata->d_buf);
    462 
    463   /* Free section groups memory.  */
    464   AsmScnGrp_t *scngrp = ctx->groups;
    465   if (scngrp != NULL)
    466     do
    467       free (elf_getdata (scngrp->scn, NULL)->d_buf);
    468     while ((scngrp = scngrp->next) != ctx->groups);
    469 
    470   /* Finalize the ELF handling.  */
    471   if (unlikely (elf_end (ctx->out.elf)) != 0)
    472     {
    473       __libasm_seterrno (ASM_E_LIBELF);
    474       result = -1;
    475     }
    476 
    477   /* Free the temporary resources.  */
    478   free (symtab);
    479 
    480   return result;
    481 }
    482 
    483 
    484 int
    485 asm_end (ctx)
    486      AsmCtx_t *ctx;
    487 {
    488   int result;
    489 
    490   if (ctx == NULL)
    491     /* Something went wrong earlier.  */
    492     return -1;
    493 
    494   result = unlikely (ctx->textp) ? text_end (ctx) : binary_end (ctx);
    495   if (result != 0)
    496     return result;
    497 
    498   /* Make the new file globally readable and user/group-writable.  */
    499   if (fchmod (ctx->fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH) != 0)
    500     {
    501       __libasm_seterrno (ASM_E_CANNOT_CHMOD);
    502       return -1;
    503     }
    504 
    505   /* Rename output file.  */
    506   if (rename (ctx->tmp_fname, ctx->fname) != 0)
    507     {
    508       __libasm_seterrno (ASM_E_CANNOT_RENAME);
    509       return -1;
    510     }
    511 
    512   /* Free the resources.  */
    513   __libasm_finictx (ctx);
    514 
    515   return 0;
    516 }
    517 
    518 
    519 static void
    520 free_section (AsmScn_t *scnp)
    521 {
    522   void *oldp;
    523 
    524   if (scnp->subnext != NULL)
    525     free_section (scnp->subnext);
    526 
    527   struct AsmData *data = scnp->content;
    528   if (data != NULL)
    529     do
    530       {
    531 	oldp = data;
    532 	data = data->next;
    533 	free (oldp);
    534       }
    535     while (oldp != scnp->content);
    536 
    537   free (scnp);
    538 }
    539 
    540 
    541 void
    542 __libasm_finictx (ctx)
    543      AsmCtx_t *ctx;
    544 {
    545   /* Iterate through section table and free individual entries.  */
    546   AsmScn_t *scn = ctx->section_list;
    547   while (scn != NULL)
    548     {
    549       AsmScn_t *oldp = scn;
    550       scn = scn->allnext;
    551       free_section (oldp);
    552     }
    553 
    554   /* Free the resources of the symbol table.  */
    555   void *runp = NULL;
    556   AsmSym_t *sym;
    557   while ((sym = asm_symbol_tab_iterate (&ctx->symbol_tab, &runp)) != NULL)
    558     free (sym);
    559   asm_symbol_tab_free (&ctx->symbol_tab);
    560 
    561 
    562   /* Free section groups.  */
    563   AsmScnGrp_t *scngrp = ctx->groups;
    564   if (scngrp != NULL)
    565     do
    566       {
    567 	AsmScnGrp_t *oldp = scngrp;
    568 
    569 	scngrp = scngrp->next;
    570 	free (oldp);
    571       }
    572     while (scngrp != ctx->groups);
    573 
    574 
    575   if (unlikely (ctx->textp))
    576     {
    577       /* Close the stream.  */
    578       fclose (ctx->out.file);
    579     }
    580   else
    581     {
    582       /* Close the output file.  */
    583       /* XXX We should test for errors here but what would we do if we'd
    584 	 find any.  */
    585       (void) close (ctx->fd);
    586 
    587       /* And the string tables.  */
    588       ebl_strtabfree (ctx->section_strtab);
    589       ebl_strtabfree (ctx->symbol_strtab);
    590     }
    591 
    592   /* Initialize the lock.  */
    593   rwlock_fini (ctx->lock);
    594 
    595   /* Finally free the data structure.   */
    596   free (ctx);
    597 }
    598