1 /* simple-object-coff.c -- routines to manipulate XCOFF object files.
2    Copyright (C) 2013-2018 Free Software Foundation, Inc.
3    Written by Ian Lance Taylor, Google and David Edelsohn, IBM.
4 
5 This program is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by the
7 Free Software Foundation; either version 2, or (at your option) any
8 later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 51 Franklin Street - Fifth Floor,
18 Boston, MA 02110-1301, USA.  */
19 
20 #include "config.h"
21 #include "libiberty.h"
22 #include "simple-object.h"
23 
24 #include <errno.h>
25 #include <stddef.h>
26 
27 #ifdef HAVE_STDLIB_H
28 #include <stdlib.h>
29 #endif
30 
31 #ifdef HAVE_STDINT_H
32 #include <stdint.h>
33 #endif
34 
35 #ifdef HAVE_STRING_H
36 #include <string.h>
37 #endif
38 
39 #ifdef HAVE_INTTYPES_H
40 #include <inttypes.h>
41 #endif
42 
43 #include "simple-object-common.h"
44 
45 /* XCOFF structures and constants.  */
46 
47 /* XCOFF file header.  */
48 
49 struct external_filehdr
50 {
51   unsigned char f_magic[2];	/* magic number			*/
52   unsigned char f_nscns[2];	/* number of sections		*/
53   unsigned char f_timdat[4];	/* time & date stamp		*/
54   union
55   {
56     struct
57     {
58       unsigned char f_symptr[4];	/* file pointer to symtab	*/
59       unsigned char f_nsyms[4];	/* number of symtab entries	*/
60       unsigned char f_opthdr[2];	/* sizeof(optional hdr)		*/
61       unsigned char f_flags[2];	/* flags			*/
62     } xcoff32;
63     struct
64     {
65       unsigned char f_symptr[8];	/* file pointer to symtab	*/
66       unsigned char f_opthdr[2];	/* sizeof(optional hdr)		*/
67       unsigned char f_flags[2];	/* flags			*/
68       unsigned char f_nsyms[4];	/* number of symtab entries	*/
69     } xcoff64;
70   } u;
71 };
72 
73 /* Bits for filehdr f_flags field.  */
74 
75 #define F_EXEC			(0x0002)
76 
77 /* The known values of f_magic in an XCOFF file header.  */
78 
79 #define U802WRMAGIC 0730        /* Writeable text segments.  */
80 #define U802ROMAGIC 0735        /* Readonly sharable text segments.  */
81 #define U802TOCMAGIC 0737       /* Readonly text segments and TOC.  */
82 #define U803XTOCMAGIC 0757      /* Aix 4.3 64-bit XCOFF.  */
83 #define U64_TOCMAGIC 0767       /* AIX 5+ 64-bit XCOFF.  */
84 
85 /* XCOFF section header.  */
86 
87 struct external_scnhdr
88 {
89   unsigned char s_name[8];	/* section name				*/
90   union
91   {
92     struct
93     {
94       unsigned char s_paddr[4];	/* physical address, aliased s_nlib 	*/
95       unsigned char s_vaddr[4];	/* virtual address			*/
96       unsigned char s_size[4];	/* section size				*/
97       unsigned char s_scnptr[4];	/* file ptr to raw data for section */
98       unsigned char s_relptr[4];	/* file ptr to relocation	*/
99       unsigned char s_lnnoptr[4];	/* file ptr to line numbers	*/
100       unsigned char s_nreloc[2];	/* number of relocation entries	*/
101       unsigned char s_nlnno[2];	/* number of line number entries	*/
102       unsigned char s_flags[4];	/* flags				*/
103     } xcoff32;
104     struct
105     {
106       unsigned char s_paddr[8];	/* physical address, aliased s_nlib 	*/
107       unsigned char s_vaddr[8];	/* virtual address			*/
108       unsigned char s_size[8];	/* section size				*/
109       unsigned char s_scnptr[8];	/* file ptr to raw data for section */
110       unsigned char s_relptr[8];	/* file ptr to relocation	*/
111       unsigned char s_lnnoptr[8];	/* file ptr to line numbers	*/
112       unsigned char s_nreloc[4];	/* number of relocation entries	*/
113       unsigned char s_nlnno[4];	/* number of line number entries	*/
114       unsigned char s_flags[4];	/* flags				*/
115     } xcoff64;
116   } u;
117 };
118 
119 #define SCNHSZ32	(40)
120 #define SCNHSZ64	(68)
121 
122 /* The length of the s_name field in struct external_scnhdr.  */
123 
124 #define SCNNMLEN	(8)
125 
126 /* Bits for scnhdr s_flags field.  */
127 
128 #define STYP_DATA			0x40
129 
130 /* XCOFF symbol table entry.  */
131 
132 
133 #define N_SYMNMLEN	(8)	/* # characters in a symbol name	*/
134 
135 /* The format of an XCOFF symbol-table entry.  */
136 struct external_syment
137 {
138   union {
139     struct {
140       union {
141 /* The name of the symbol.  There is an implicit null character
142    after the end of the array.  */
143 	char n_name[N_SYMNMLEN];
144 	struct {
145 	  /* If n_zeroes is zero, n_offset is the offset the name from
146 	     the start of the string table.  */
147 	  unsigned char n_zeroes[4];
148 	  unsigned char n_offset[4];
149 	} n;
150       } n;
151 
152       /* The symbol's value.  */
153       unsigned char n_value[4];
154     } xcoff32;
155     struct {
156       /* The symbol's value.  */
157       unsigned char n_value[8];
158 
159       /* The offset of the symbol from the start of the string table.  */
160       unsigned char n_offset[4];
161     } xcoff64;
162   } u;
163 
164   /* The number of the section to which this symbol belongs.  */
165   unsigned char n_scnum[2];
166 
167   /* The type of symbol.  (It can be interpreted as an n_lang
168      and an n_cpu byte, but we don't care about that here.)  */
169   unsigned char n_type[2];
170 
171   /* The class of symbol (a C_* value).  */
172   unsigned char n_sclass[1];
173 
174   /* The number of auxiliary symbols attached to this entry.  */
175   unsigned char n_numaux[1];
176 };
177 
178 #define SYMESZ		(18)
179 
180 /* Length allowed for filename in aux sym format 4.  */
181 
182 #define FILNMLEN	(14)
183 
184 /* Omits x_sym and other unused variants.  */
185 
186 union external_auxent
187 {
188   /* Aux sym format 4: file.  */
189   union
190   {
191     char x_fname[FILNMLEN];
192     struct
193     {
194       unsigned char x_zeroes[4];
195       unsigned char x_offset[4];
196       unsigned char x_pad[FILNMLEN-8];
197       unsigned char x_ftype;
198     } _x;
199   } x_file;
200   /* Aux sym format 5: section.  */
201   struct
202   {
203     unsigned char x_scnlen[4];		/* section length		*/
204     unsigned char x_nreloc[2];		/* # relocation entries		*/
205     unsigned char x_nlinno[2];		/* # line numbers		*/
206   } x_scn;
207   /* CSECT auxiliary entry.  */
208   union
209   {
210     struct
211     {
212       struct
213       {
214 	unsigned char x_scnlen[4];	/* csect length */
215 	unsigned char x_parmhash[4];	/* parm type hash index */
216 	unsigned char x_snhash[2];	/* sect num with parm hash */
217 	unsigned char x_smtyp;		/* symbol align and type */
218 	unsigned char x_smclas;		/* storage mapping class */
219 	unsigned char x_stab;		/* dbx stab info index */
220 	unsigned char x_snstab[2];	/* sect num with dbx stab */
221       } x_csect;
222     } xcoff32;
223     struct
224     {
225       struct
226       {
227 	unsigned char x_scnlen_lo[4];	/* csect length */
228 	unsigned char x_parmhash[4];	/* parm type hash index */
229 	unsigned char x_snhash[2];	/* sect num with parm hash */
230 	unsigned char x_smtyp;		/* symbol align and type */
231 	unsigned char x_smclas;		/* storage mapping class */
232 	unsigned char x_scnlen_hi[4];
233 	unsigned char pad;
234 	unsigned char x_auxtype;
235       } x_csect;
236     } xcoff64;
237   } u;
238   /* SECTION/DWARF auxiliary entry.  */
239   struct
240   {
241     unsigned char x_scnlen[4];		/* section length */
242     unsigned char pad1[4];
243     unsigned char x_nreloc[4];		/* number RLDs */
244   } x_sect;
245 };
246 
247 /* Symbol-related constants.  */
248 
249 #define N_DEBUG		(-2)
250 #define IMAGE_SYM_TYPE_NULL	(0)
251 #define IMAGE_SYM_DTYPE_NULL	(0)
252 #define IMAGE_SYM_CLASS_STATIC	(3)
253 #define IMAGE_SYM_CLASS_FILE	(103)
254 
255 #define IMAGE_SYM_TYPE \
256   ((IMAGE_SYM_DTYPE_NULL << 4) | IMAGE_SYM_TYPE_NULL)
257 
258 #define C_EXT		(2)
259 #define C_STAT		(3)
260 #define C_FILE		(103)
261 #define C_HIDEXT	(107)
262 
263 #define XTY_SD		(1)	/* section definition */
264 
265 #define XMC_XO		(7)	/* extended operation */
266 
267 /* Private data for an simple_object_read.  */
268 
269 struct simple_object_xcoff_read
270 {
271   /* Magic number.  */
272   unsigned short magic;
273   /* Number of sections.  */
274   unsigned short nscns;
275   /* File offset of symbol table.  */
276   off_t symptr;
277   /* Number of symbol table entries.  */
278   unsigned int nsyms;
279   /* Flags.  */
280   unsigned short flags;
281   /* Offset of section headers in file.  */
282   off_t scnhdr_offset;
283 };
284 
285 /* Private data for an simple_object_attributes.  */
286 
287 struct simple_object_xcoff_attributes
288 {
289   /* Magic number.  */
290   unsigned short magic;
291   /* Flags.  */
292   unsigned short flags;
293 };
294 
295 /* See if we have a XCOFF file.  */
296 
297 static void *
simple_object_xcoff_match(unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],int descriptor,off_t offset,const char * segment_name ATTRIBUTE_UNUSED,const char ** errmsg,int * err)298 simple_object_xcoff_match (unsigned char header[SIMPLE_OBJECT_MATCH_HEADER_LEN],
299 			   int descriptor, off_t offset,
300 			   const char *segment_name ATTRIBUTE_UNUSED,
301 			   const char **errmsg, int *err)
302 {
303   unsigned short magic;
304   unsigned short (*fetch_16) (const unsigned char *);
305   unsigned int (*fetch_32) (const unsigned char *);
306   ulong_type (*fetch_64) (const unsigned char *);
307   unsigned char hdrbuf[sizeof (struct external_filehdr)];
308   struct simple_object_xcoff_read *ocr;
309   int u64;
310 
311   magic = simple_object_fetch_big_16 (header);
312 
313   if (magic != U802TOCMAGIC && magic != U64_TOCMAGIC)
314     {
315       *errmsg = NULL;
316       *err = 0;
317       return NULL;
318     }
319 
320   fetch_16 = simple_object_fetch_big_16;
321   fetch_32 = simple_object_fetch_big_32;
322   fetch_64 = simple_object_fetch_big_64;
323 
324   if (!simple_object_internal_read (descriptor, offset, hdrbuf, sizeof hdrbuf,
325 				    errmsg, err))
326     return NULL;
327 
328   u64 = magic == U64_TOCMAGIC;
329 
330   ocr = XNEW (struct simple_object_xcoff_read);
331   ocr->magic = magic;
332   ocr->nscns = fetch_16 (hdrbuf + offsetof (struct external_filehdr, f_nscns));
333   if (u64)
334     {
335       ocr->symptr = fetch_64 (hdrbuf
336 			      + offsetof (struct external_filehdr,
337 					  u.xcoff64.f_symptr));
338       ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr,
339 						u.xcoff64.f_nsyms));
340       ocr->scnhdr_offset = (sizeof (struct external_filehdr)
341 			    + fetch_16 (hdrbuf + offsetof (struct external_filehdr,
342 							   u.xcoff64.f_opthdr)));
343 
344     }
345   else
346     {
347       ocr->symptr = fetch_32 (hdrbuf
348 			      + offsetof (struct external_filehdr,
349 					  u.xcoff32.f_symptr));
350       ocr->nsyms = fetch_32 (hdrbuf + offsetof (struct external_filehdr,
351 						u.xcoff32.f_nsyms));
352       ocr->scnhdr_offset = (sizeof (struct external_filehdr) - 4
353 			    + fetch_16 (hdrbuf + offsetof (struct external_filehdr,
354 							   u.xcoff32.f_opthdr)));
355 
356     }
357 
358   return (void *) ocr;
359 }
360 
361 /* Read the string table in a XCOFF file.  */
362 
363 static char *
simple_object_xcoff_read_strtab(simple_object_read * sobj,size_t * strtab_size,const char ** errmsg,int * err)364 simple_object_xcoff_read_strtab (simple_object_read *sobj, size_t *strtab_size,
365 				 const char **errmsg, int *err)
366 {
367   struct simple_object_xcoff_read *ocr =
368     (struct simple_object_xcoff_read *) sobj->data;
369   off_t strtab_offset;
370   unsigned char strsizebuf[4];
371   size_t strsize;
372   char *strtab;
373 
374   strtab_offset = sobj->offset + ocr->symptr
375     + ocr->nsyms * SYMESZ;
376   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
377 				    strsizebuf, 4, errmsg, err))
378     return NULL;
379   strsize = simple_object_fetch_big_32 (strsizebuf);
380   strtab = XNEWVEC (char, strsize);
381   if (!simple_object_internal_read (sobj->descriptor, strtab_offset,
382 				    (unsigned char *) strtab, strsize, errmsg,
383 				    err))
384     {
385       XDELETEVEC (strtab);
386       return NULL;
387     }
388   *strtab_size = strsize;
389   return strtab;
390 }
391 
392 /* Find all sections in a XCOFF file.  */
393 
394 static const char *
simple_object_xcoff_find_sections(simple_object_read * sobj,int (* pfn)(void *,const char *,off_t offset,off_t length),void * data,int * err)395 simple_object_xcoff_find_sections (simple_object_read *sobj,
396 				  int (*pfn) (void *, const char *,
397 					      off_t offset, off_t length),
398 				  void *data,
399 				  int *err)
400 {
401   struct simple_object_xcoff_read *ocr =
402     (struct simple_object_xcoff_read *) sobj->data;
403   int u64 = ocr->magic == U64_TOCMAGIC;
404   size_t scnhdr_size;
405   unsigned char *scnbuf;
406   const char *errmsg;
407   unsigned short (*fetch_16) (const unsigned char *);
408   unsigned int (*fetch_32) (const unsigned char *);
409   ulong_type (*fetch_64) (const unsigned char *);
410   unsigned int nscns;
411   char *strtab;
412   size_t strtab_size;
413   struct external_syment *symtab = NULL;
414   unsigned int i;
415 
416   scnhdr_size = u64 ? SCNHSZ64 : SCNHSZ32;
417   scnbuf = XNEWVEC (unsigned char, scnhdr_size * ocr->nscns);
418   if (!simple_object_internal_read (sobj->descriptor,
419 				    sobj->offset + ocr->scnhdr_offset,
420 				    scnbuf, scnhdr_size * ocr->nscns, &errmsg,
421 				    err))
422     {
423       XDELETEVEC (scnbuf);
424       return errmsg;
425     }
426 
427   fetch_16 = simple_object_fetch_big_16;
428   fetch_32 = simple_object_fetch_big_32;
429   fetch_64 = simple_object_fetch_big_64;
430 
431   nscns = ocr->nscns;
432   strtab = NULL;
433   strtab_size = 0;
434   for (i = 0; i < nscns; ++i)
435     {
436       unsigned char *scnhdr;
437       unsigned char *scnname;
438       char namebuf[SCNNMLEN + 1];
439       char *name;
440       off_t scnptr;
441       off_t size;
442 
443       scnhdr = scnbuf + i * scnhdr_size;
444       scnname = scnhdr + offsetof (struct external_scnhdr, s_name);
445       memcpy (namebuf, scnname, SCNNMLEN);
446       namebuf[SCNNMLEN] = '\0';
447       name = &namebuf[0];
448       if (namebuf[0] == '/')
449 	{
450 	  size_t strindex;
451 	  char *end;
452 
453 	  strindex = strtol (namebuf + 1, &end, 10);
454 	  if (*end == '\0')
455 	    {
456 	      /* The real section name is found in the string
457 		 table.  */
458 	      if (strtab == NULL)
459 		{
460 		  strtab = simple_object_xcoff_read_strtab (sobj,
461 							   &strtab_size,
462 							   &errmsg, err);
463 		  if (strtab == NULL)
464 		    {
465 		      XDELETEVEC (scnbuf);
466 		      return errmsg;
467 		    }
468 		}
469 
470 	      if (strindex < 4 || strindex >= strtab_size)
471 		{
472 		  XDELETEVEC (strtab);
473 		  XDELETEVEC (scnbuf);
474 		  *err = 0;
475 		  return "section string index out of range";
476 		}
477 
478 	      name = strtab + strindex;
479 	    }
480 	}
481 
482       if (u64)
483 	{
484 	  scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
485 						u.xcoff64.s_scnptr));
486 	  size = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
487 					      u.xcoff64.s_size));
488 	}
489       else
490 	{
491 	  scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
492 						u.xcoff32.s_scnptr));
493 	  size = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
494 					      u.xcoff32.s_size));
495 	}
496 
497       if (!(*pfn) (data, name, scnptr, size))
498 	break;
499     }
500 
501   /* Special handling for .go_export csect.  */
502   if (ocr->nsyms > 0)
503     {
504       unsigned char *sym;
505       const char *n_name;
506       off_t size, n_value;
507       unsigned int n_numaux, n_offset, n_zeroes;
508       short n_scnum;
509 
510       /* Read symbol table.  */
511       symtab = XNEWVEC (struct external_syment, ocr->nsyms * SYMESZ);
512       if (!simple_object_internal_read (sobj->descriptor,
513 					sobj->offset + ocr->symptr,
514 					(unsigned char *) symtab,
515 					ocr->nsyms * SYMESZ,
516 					&errmsg, err))
517 	{
518 	  XDELETEVEC (symtab);
519 	  XDELETEVEC (scnbuf);
520 	  return NULL;
521 	}
522 
523       /* Search in symbol table if we have a ".go_export" symbol.  */
524       for (i = 0; i < ocr->nsyms; i += n_numaux + 1)
525 	{
526 	  sym = (unsigned char *) &symtab[i];
527 	  n_numaux = symtab[i].n_numaux[0];
528 
529 	  if (symtab[i].n_sclass[0] != C_EXT
530 	      && symtab[i].n_sclass[0] != C_HIDEXT)
531 	    continue;
532 
533 	  /* Must have at least one csect auxiliary entry.  */
534 	  if (n_numaux < 1 || i + n_numaux >= ocr->nsyms)
535 	    continue;
536 
537 	  n_scnum = fetch_16 (sym + offsetof (struct external_syment,
538 					      n_scnum));
539 	  if (n_scnum < 1 || (unsigned int) n_scnum > nscns)
540 	    continue;
541 
542 	  if (u64)
543 	    {
544 	      n_value = fetch_64 (sym + offsetof (struct external_syment,
545 						  u.xcoff64.n_value));
546 	      n_offset = fetch_32 (sym + offsetof (struct external_syment,
547 						   u.xcoff64.n_offset));
548 	    }
549 	  else
550 	    {
551 	      /* ".go_export" is longer than N_SYMNMLEN.  */
552 	      n_zeroes = fetch_32 (sym + offsetof (struct external_syment,
553 						   u.xcoff32.n.n.n_zeroes));
554 	      if (n_zeroes != 0)
555 		continue;
556 
557 	      n_value = fetch_32 (sym + offsetof (struct external_syment,
558 						  u.xcoff32.n_value));
559 	      n_offset = fetch_32 (sym + offsetof (struct external_syment,
560 						   u.xcoff32.n.n.n_offset));
561 	    }
562 
563 	  /* The real symbol name is found in the string table.  */
564 	  if (strtab == NULL)
565 	    {
566 	      strtab = simple_object_xcoff_read_strtab (sobj,
567 	  						&strtab_size,
568 							&errmsg, err);
569 	      if (strtab == NULL)
570 		{
571 		  XDELETEVEC (symtab);
572 		  XDELETEVEC (scnbuf);
573 		  return errmsg;
574 		}
575 	    }
576 
577 	  if (n_offset >= strtab_size)
578 	    {
579 	      XDELETEVEC (strtab);
580 	      XDELETEVEC (symtab);
581 	      XDELETEVEC (scnbuf);
582 	      *err = 0;
583 	      return "symbol string index out of range";
584 	    }
585 	  n_name = strtab + n_offset;
586 
587 	  if (!strcmp (n_name, ".go_export"))
588 	    {
589 	      union external_auxent *auxent;
590 	      unsigned char *aux, *scnhdr;
591 	      off_t scnptr, x_scnlen;
592 
593 	      /* Found .go_export symbol, read its csect auxiliary entry.
594 		 By convention, it is the last auxiliary entry.  */
595 	      auxent = (union external_auxent *) &symtab[i + n_numaux];
596 	      aux = (unsigned char *) auxent;
597 	      if (u64)
598 		{
599 		  if ((auxent->u.xcoff64.x_csect.x_smtyp & 0x7) != XTY_SD
600 		      || auxent->u.xcoff64.x_csect.x_smclas != XMC_XO)
601 		    continue;
602 
603 		  x_scnlen = fetch_32 (aux + offsetof (union external_auxent,
604 						       u.xcoff64.x_csect.x_scnlen_hi));
605 		  x_scnlen = x_scnlen << 32
606 			   | fetch_32 (aux + offsetof (union external_auxent,
607 						       u.xcoff64.x_csect.x_scnlen_lo));
608 		}
609 	      else
610 		{
611 		  if ((auxent->u.xcoff32.x_csect.x_smtyp & 0x7) != XTY_SD
612 		      || auxent->u.xcoff32.x_csect.x_smclas != XMC_XO)
613 		    continue;
614 
615 		  x_scnlen = fetch_32 (aux + offsetof (union external_auxent,
616 						       u.xcoff32.x_csect.x_scnlen));
617 		}
618 
619 	      /* Get header of containing section.  */
620 	      scnhdr = scnbuf + (n_scnum - 1) * scnhdr_size;
621 	      if (u64)
622 		{
623 		  scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
624 							u.xcoff64.s_scnptr));
625 		  size = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
626 						      u.xcoff64.s_size));
627 		}
628 	      else
629 		{
630 		  scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
631 							u.xcoff32.s_scnptr));
632 		  size = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
633 						      u.xcoff32.s_size));
634 		}
635 	      if (n_value + x_scnlen > size)
636 		break;
637 
638 	      (*pfn) (data, ".go_export", scnptr + n_value, x_scnlen);
639 	      break;
640 	    }
641 	}
642     }
643 
644   if (symtab != NULL)
645     XDELETEVEC (symtab);
646   if (strtab != NULL)
647     XDELETEVEC (strtab);
648   XDELETEVEC (scnbuf);
649 
650   return NULL;
651 }
652 
653 /* Fetch the attributes for an simple_object_read.  */
654 
655 static void *
simple_object_xcoff_fetch_attributes(simple_object_read * sobj,const char ** errmsg ATTRIBUTE_UNUSED,int * err ATTRIBUTE_UNUSED)656 simple_object_xcoff_fetch_attributes (simple_object_read *sobj,
657 				     const char **errmsg ATTRIBUTE_UNUSED,
658 				     int *err ATTRIBUTE_UNUSED)
659 {
660   struct simple_object_xcoff_read *ocr =
661     (struct simple_object_xcoff_read *) sobj->data;
662   struct simple_object_xcoff_attributes *ret;
663 
664   ret = XNEW (struct simple_object_xcoff_attributes);
665   ret->magic = ocr->magic;
666   ret->flags = ocr->flags;
667   return ret;
668 }
669 
670 /* Release the private data for an simple_object_read.  */
671 
672 static void
simple_object_xcoff_release_read(void * data)673 simple_object_xcoff_release_read (void *data)
674 {
675   XDELETE (data);
676 }
677 
678 /* Compare two attributes structures.  */
679 
680 static const char *
simple_object_xcoff_attributes_merge(void * todata,void * fromdata,int * err)681 simple_object_xcoff_attributes_merge (void *todata, void *fromdata, int *err)
682 {
683   struct simple_object_xcoff_attributes *to =
684     (struct simple_object_xcoff_attributes *) todata;
685   struct simple_object_xcoff_attributes *from =
686     (struct simple_object_xcoff_attributes *) fromdata;
687 
688   if (to->magic != from->magic)
689     {
690       *err = 0;
691       return "XCOFF object format mismatch";
692     }
693   return NULL;
694 }
695 
696 /* Release the private data for an attributes structure.  */
697 
698 static void
simple_object_xcoff_release_attributes(void * data)699 simple_object_xcoff_release_attributes (void *data)
700 {
701   XDELETE (data);
702 }
703 
704 /* Prepare to write out a file.  */
705 
706 static void *
simple_object_xcoff_start_write(void * attributes_data,const char ** errmsg ATTRIBUTE_UNUSED,int * err ATTRIBUTE_UNUSED)707 simple_object_xcoff_start_write (void *attributes_data,
708 				const char **errmsg ATTRIBUTE_UNUSED,
709 				int *err ATTRIBUTE_UNUSED)
710 {
711   struct simple_object_xcoff_attributes *attrs =
712     (struct simple_object_xcoff_attributes *) attributes_data;
713   struct simple_object_xcoff_attributes *ret;
714 
715   /* We're just going to record the attributes, but we need to make a
716      copy because the user may delete them.  */
717   ret = XNEW (struct simple_object_xcoff_attributes);
718   *ret = *attrs;
719   return ret;
720 }
721 
722 /* Write out a XCOFF filehdr.  */
723 
724 static int
simple_object_xcoff_write_filehdr(simple_object_write * sobj,int descriptor,unsigned int nscns,size_t symtab_offset,unsigned int nsyms,const char ** errmsg,int * err)725 simple_object_xcoff_write_filehdr (simple_object_write *sobj, int descriptor,
726 				  unsigned int nscns, size_t symtab_offset,
727 				  unsigned int nsyms, const char **errmsg,
728 				  int *err)
729 {
730   struct simple_object_xcoff_attributes *attrs =
731     (struct simple_object_xcoff_attributes *) sobj->data;
732   int u64 = attrs->magic == U64_TOCMAGIC;
733   unsigned char hdrbuf[sizeof (struct external_filehdr)];
734   unsigned char *hdr;
735   void (*set_16) (unsigned char *, unsigned short);
736   void (*set_32) (unsigned char *, unsigned int);
737   void (*set_64) (unsigned char *, ulong_type);
738 
739   hdr = &hdrbuf[0];
740 
741   set_16 = simple_object_set_big_16;
742   set_32 = simple_object_set_big_32;
743   set_64 = simple_object_set_big_64;
744 
745   memset (hdr, 0, sizeof (struct external_filehdr));
746 
747   set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
748   set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
749   /* f_timdat left as zero.  */
750   if (u64)
751     {
752       set_64 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr),
753 	      symtab_offset);
754       set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms),
755 	      nsyms);
756       /* f_opthdr left as zero.  */
757       set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags),
758 	      attrs->flags);
759     }
760   else
761     {
762       set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr),
763 	      symtab_offset);
764       set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms),
765 	      nsyms);
766       /* f_opthdr left as zero.  */
767       set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags),
768 	      attrs->flags);
769     }
770 
771   return simple_object_internal_write (descriptor, 0, hdrbuf,
772 				       sizeof (struct external_filehdr),
773 				       errmsg, err);
774 }
775 
776 /* Write out a XCOFF section header.  */
777 
778 static int
simple_object_xcoff_write_scnhdr(simple_object_write * sobj,int descriptor,const char * name,size_t * name_offset,off_t scnhdr_offset,size_t scnsize,off_t offset,unsigned int align,const char ** errmsg,int * err)779 simple_object_xcoff_write_scnhdr (simple_object_write *sobj,
780 				  int descriptor,
781 				  const char *name, size_t *name_offset,
782 				  off_t scnhdr_offset, size_t scnsize,
783 				  off_t offset, unsigned int align,
784 				  const char **errmsg, int *err)
785 {
786   struct simple_object_xcoff_read *ocr =
787     (struct simple_object_xcoff_read *) sobj->data;
788   int u64 = ocr->magic == U64_TOCMAGIC;
789   void (*set_32) (unsigned char *, unsigned int);
790   void (*set_64) (unsigned char *, unsigned int);
791   unsigned char hdrbuf[sizeof (struct external_scnhdr)];
792   unsigned char *hdr;
793   size_t namelen;
794   unsigned int flags;
795 
796   set_32 = simple_object_set_big_32;
797   set_64 = simple_object_set_big_32;
798 
799   memset (hdrbuf, 0, sizeof hdrbuf);
800   hdr = &hdrbuf[0];
801 
802   namelen = strlen (name);
803   if (namelen <= SCNNMLEN)
804     strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name),
805 	     name, SCNNMLEN);
806   else
807     {
808       snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
809 		SCNNMLEN, "/%lu", (unsigned long) *name_offset);
810       *name_offset += namelen + 1;
811     }
812 
813   /* s_paddr left as zero.  */
814   /* s_vaddr left as zero.  */
815   if (u64)
816     {
817       set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_size),
818 	      scnsize);
819       set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_scnptr),
820 	      offset);
821     }
822   else
823     {
824       set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_size),
825 	      scnsize);
826       set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_scnptr),
827 	      offset);
828     }
829   /* s_relptr left as zero.  */
830   /* s_lnnoptr left as zero.  */
831   /* s_nreloc left as zero.  */
832   /* s_nlnno left as zero.  */
833   flags = STYP_DATA;
834   if (align > 13)
835     align = 13;
836   if (u64)
837     set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_flags), flags);
838   else
839     set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_flags), flags);
840 
841   return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
842 				       u64 ? SCNHSZ64 : SCNHSZ32,
843 				       errmsg, err);
844 }
845 
846 /* Write out a complete XCOFF file.  */
847 
848 static const char *
simple_object_xcoff_write_to_file(simple_object_write * sobj,int descriptor,int * err)849 simple_object_xcoff_write_to_file (simple_object_write *sobj, int descriptor,
850 				  int *err)
851 {
852   struct simple_object_xcoff_read *ocr =
853     (struct simple_object_xcoff_read *) sobj->data;
854   int u64 = ocr->magic == U64_TOCMAGIC;
855   unsigned int nscns, secnum;
856   simple_object_write_section *section;
857   off_t scnhdr_offset;
858   size_t symtab_offset;
859   off_t secsym_offset;
860   unsigned int nsyms;
861   size_t offset;
862   size_t name_offset;
863   const char *errmsg;
864   unsigned char strsizebuf[4];
865   /* The interface doesn't give us access to the name of the input file
866      yet.  We want to use its basename for the FILE symbol.  This is
867      what 'gas' uses when told to assemble from stdin.  */
868   const char *source_filename = "fake";
869   size_t sflen;
870   union
871   {
872     struct external_syment sym;
873     union external_auxent aux;
874   } syms[2];
875   void (*set_16) (unsigned char *, unsigned short);
876   void (*set_32) (unsigned char *, unsigned int);
877 
878   set_16 = simple_object_set_big_16;
879   set_32 = simple_object_set_big_32;
880 
881   nscns = 0;
882   for (section = sobj->sections; section != NULL; section = section->next)
883     ++nscns;
884 
885   scnhdr_offset = sizeof (struct external_filehdr) - (u64 ? 4 : 0);
886   offset = scnhdr_offset + nscns * (u64 ? SCNHSZ64 : SCNHSZ32);
887   name_offset = 4;
888   for (section = sobj->sections; section != NULL; section = section->next)
889     {
890       size_t mask;
891       size_t new_offset;
892       size_t scnsize;
893       struct simple_object_write_section_buffer *buffer;
894 
895       mask = (1U << section->align) - 1;
896       new_offset = offset & mask;
897       new_offset &= ~ mask;
898       while (new_offset > offset)
899 	{
900 	  unsigned char zeroes[16];
901 	  size_t write;
902 
903 	  memset (zeroes, 0, sizeof zeroes);
904 	  write = new_offset - offset;
905 	  if (write > sizeof zeroes)
906 	    write = sizeof zeroes;
907 	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
908 					     &errmsg, err))
909 	    return errmsg;
910 	}
911 
912       scnsize = 0;
913       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
914 	{
915 	  if (!simple_object_internal_write (descriptor, offset + scnsize,
916 					     ((const unsigned char *)
917 					      buffer->buffer),
918 					     buffer->size, &errmsg, err))
919 	    return errmsg;
920 	  scnsize += buffer->size;
921 	}
922 
923       if (!simple_object_xcoff_write_scnhdr (sobj, descriptor, section->name,
924 					    &name_offset, scnhdr_offset,
925 					    scnsize, offset, section->align,
926 					    &errmsg, err))
927 	return errmsg;
928 
929       scnhdr_offset += u64 ? SCNHSZ64 : SCNHSZ32;
930       offset += scnsize;
931     }
932 
933   /* Symbol table is always half-word aligned.  */
934   offset += (offset & 1);
935   /* There is a file symbol and a section symbol per section,
936      and each of these has a single auxiliary symbol following.  */
937   nsyms = 2 * (nscns + 1);
938   symtab_offset = offset;
939   /* Advance across space reserved for symbol table to locate
940      start of string table.  */
941   offset += nsyms * SYMESZ;
942 
943   /* Write out file symbol.  */
944   memset (&syms[0], 0, sizeof (syms));
945   if (!u64)
946     strcpy ((char *)&syms[0].sym.u.xcoff32.n.n_name[0], ".file");
947   set_16 (&syms[0].sym.n_scnum[0], N_DEBUG);
948   set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE);
949   syms[0].sym.n_sclass[0] = C_FILE;
950   syms[0].sym.n_numaux[0] = 1;
951   /* The name need not be nul-terminated if it fits into the x_fname field
952      directly, but must be if it has to be placed into the string table.  */
953   sflen = strlen (source_filename);
954   if (sflen <= FILNMLEN)
955     memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
956   else
957     {
958       set_32 (&syms[1].aux.x_file._x.x_offset[0], name_offset);
959       if (!simple_object_internal_write (descriptor, offset + name_offset,
960 					 ((const unsigned char *)
961 					  source_filename),
962 					 sflen + 1, &errmsg, err))
963 	return errmsg;
964       name_offset += strlen (source_filename) + 1;
965     }
966   if (!simple_object_internal_write (descriptor, symtab_offset,
967 				     (const unsigned char *) &syms[0],
968 				     sizeof (syms), &errmsg, err))
969     return errmsg;
970 
971   /* Write the string table length, followed by the strings and section
972      symbols in step with each other.  */
973   set_32 (strsizebuf, name_offset);
974   if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
975 				     &errmsg, err))
976     return errmsg;
977 
978   name_offset = 4;
979   secsym_offset = symtab_offset + sizeof (syms);
980   memset (&syms[0], 0, sizeof (syms));
981   set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE);
982   syms[0].sym.n_sclass[0] = C_STAT;
983   syms[0].sym.n_numaux[0] = 1;
984   secnum = 1;
985 
986   for (section = sobj->sections; section != NULL; section = section->next)
987     {
988       size_t namelen;
989       size_t scnsize;
990       struct simple_object_write_section_buffer *buffer;
991 
992       namelen = strlen (section->name);
993       set_16 (&syms[0].sym.n_scnum[0], secnum++);
994       scnsize = 0;
995       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
996 	scnsize += buffer->size;
997       set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
998       if (namelen > SCNNMLEN)
999 	{
1000 	  set_32 (&syms[0].sym.u.xcoff32.n.n.n_zeroes[0], 0);
1001 	  set_32 (&syms[0].sym.u.xcoff32.n.n.n_offset[0], name_offset);
1002 	  if (!simple_object_internal_write (descriptor, offset + name_offset,
1003 					     ((const unsigned char *)
1004 					      section->name),
1005 					     namelen + 1, &errmsg, err))
1006 	    return errmsg;
1007 	  name_offset += namelen + 1;
1008 	}
1009       else
1010 	{
1011 	  memcpy (&syms[0].sym.u.xcoff32.n.n_name[0], section->name,
1012 		  strlen (section->name));
1013 	  memset (&syms[0].sym.u.xcoff32.n.n_name[strlen (section->name)], 0,
1014 		  N_SYMNMLEN - strlen (section->name));
1015 	}
1016 
1017       if (!simple_object_internal_write (descriptor, secsym_offset,
1018 					 (const unsigned char *) &syms[0],
1019 					 sizeof (syms), &errmsg, err))
1020 	return errmsg;
1021       secsym_offset += sizeof (syms);
1022     }
1023 
1024   if (!simple_object_xcoff_write_filehdr (sobj, descriptor, nscns,
1025 					 symtab_offset, nsyms, &errmsg, err))
1026     return errmsg;
1027 
1028   return NULL;
1029 }
1030 
1031 /* Release the private data for an simple_object_write structure.  */
1032 
1033 static void
simple_object_xcoff_release_write(void * data)1034 simple_object_xcoff_release_write (void *data)
1035 {
1036   XDELETE (data);
1037 }
1038 
1039 /* The XCOFF functions.  */
1040 
1041 const struct simple_object_functions simple_object_xcoff_functions =
1042 {
1043   simple_object_xcoff_match,
1044   simple_object_xcoff_find_sections,
1045   simple_object_xcoff_fetch_attributes,
1046   simple_object_xcoff_release_read,
1047   simple_object_xcoff_attributes_merge,
1048   simple_object_xcoff_release_attributes,
1049   simple_object_xcoff_start_write,
1050   simple_object_xcoff_write_to_file,
1051   simple_object_xcoff_release_write,
1052   NULL
1053 };
1054