• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Finalize operations on the assembler context, free all resources.
2    Copyright (C) 2002, 2003 Red Hat, Inc.
3    Written by Ulrich Drepper <drepper@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
text_end(AsmCtx_t * ctx)34 text_end (AsmCtx_t *ctx)
35 {
36   // XXX Does anything have to be done?
37   return 0;
38 }
39 
40 
41 static int
binary_end(AsmCtx_t * ctx)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
asm_end(ctx)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
free_section(AsmScn_t * scnp)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
__libasm_finictx(ctx)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