1 /* simple-object-coff.c -- routines to manipulate XCOFF object files.
2    Copyright (C) 2013-2020 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 *
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 *
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 *
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 		  /* Use an intermediate 64-bit type to avoid
600 		     compilation warning about 32-bit shift below on
601 		     hosts with 32-bit off_t which aren't supported by
602 		     AC_SYS_LARGEFILE.  */
603 		  ulong_type x_scnlen64;
604 
605 		  if ((auxent->u.xcoff64.x_csect.x_smtyp & 0x7) != XTY_SD
606 		      || auxent->u.xcoff64.x_csect.x_smclas != XMC_XO)
607 		    continue;
608 
609 		  x_scnlen64 =
610 		    fetch_32 (aux + offsetof (union external_auxent,
611 					      u.xcoff64.x_csect.x_scnlen_hi));
612 		  x_scnlen =
613 		    ((x_scnlen64 << 32)
614 		     | fetch_32 (aux
615 				 + offsetof (union external_auxent,
616 					     u.xcoff64.x_csect.x_scnlen_lo)));
617 		}
618 	      else
619 		{
620 		  if ((auxent->u.xcoff32.x_csect.x_smtyp & 0x7) != XTY_SD
621 		      || auxent->u.xcoff32.x_csect.x_smclas != XMC_XO)
622 		    continue;
623 
624 		  x_scnlen = fetch_32 (aux + offsetof (union external_auxent,
625 						       u.xcoff32.x_csect.x_scnlen));
626 		}
627 
628 	      /* Get header of containing section.  */
629 	      scnhdr = scnbuf + (n_scnum - 1) * scnhdr_size;
630 	      if (u64)
631 		{
632 		  scnptr = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
633 							u.xcoff64.s_scnptr));
634 		  size = fetch_64 (scnhdr + offsetof (struct external_scnhdr,
635 						      u.xcoff64.s_size));
636 		}
637 	      else
638 		{
639 		  scnptr = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
640 							u.xcoff32.s_scnptr));
641 		  size = fetch_32 (scnhdr + offsetof (struct external_scnhdr,
642 						      u.xcoff32.s_size));
643 		}
644 	      if (n_value + x_scnlen > size)
645 		break;
646 
647 	      (*pfn) (data, ".go_export", scnptr + n_value, x_scnlen);
648 	      break;
649 	    }
650 	}
651     }
652 
653   if (symtab != NULL)
654     XDELETEVEC (symtab);
655   if (strtab != NULL)
656     XDELETEVEC (strtab);
657   XDELETEVEC (scnbuf);
658 
659   return NULL;
660 }
661 
662 /* Fetch the attributes for an simple_object_read.  */
663 
664 static void *
665 simple_object_xcoff_fetch_attributes (simple_object_read *sobj,
666 				     const char **errmsg ATTRIBUTE_UNUSED,
667 				     int *err ATTRIBUTE_UNUSED)
668 {
669   struct simple_object_xcoff_read *ocr =
670     (struct simple_object_xcoff_read *) sobj->data;
671   struct simple_object_xcoff_attributes *ret;
672 
673   ret = XNEW (struct simple_object_xcoff_attributes);
674   ret->magic = ocr->magic;
675   ret->flags = ocr->flags;
676   return ret;
677 }
678 
679 /* Release the private data for an simple_object_read.  */
680 
681 static void
682 simple_object_xcoff_release_read (void *data)
683 {
684   XDELETE (data);
685 }
686 
687 /* Compare two attributes structures.  */
688 
689 static const char *
690 simple_object_xcoff_attributes_merge (void *todata, void *fromdata, int *err)
691 {
692   struct simple_object_xcoff_attributes *to =
693     (struct simple_object_xcoff_attributes *) todata;
694   struct simple_object_xcoff_attributes *from =
695     (struct simple_object_xcoff_attributes *) fromdata;
696 
697   if (to->magic != from->magic)
698     {
699       *err = 0;
700       return "XCOFF object format mismatch";
701     }
702   return NULL;
703 }
704 
705 /* Release the private data for an attributes structure.  */
706 
707 static void
708 simple_object_xcoff_release_attributes (void *data)
709 {
710   XDELETE (data);
711 }
712 
713 /* Prepare to write out a file.  */
714 
715 static void *
716 simple_object_xcoff_start_write (void *attributes_data,
717 				const char **errmsg ATTRIBUTE_UNUSED,
718 				int *err ATTRIBUTE_UNUSED)
719 {
720   struct simple_object_xcoff_attributes *attrs =
721     (struct simple_object_xcoff_attributes *) attributes_data;
722   struct simple_object_xcoff_attributes *ret;
723 
724   /* We're just going to record the attributes, but we need to make a
725      copy because the user may delete them.  */
726   ret = XNEW (struct simple_object_xcoff_attributes);
727   *ret = *attrs;
728   return ret;
729 }
730 
731 /* Write out a XCOFF filehdr.  */
732 
733 static int
734 simple_object_xcoff_write_filehdr (simple_object_write *sobj, int descriptor,
735 				  unsigned int nscns, size_t symtab_offset,
736 				  unsigned int nsyms, const char **errmsg,
737 				  int *err)
738 {
739   struct simple_object_xcoff_attributes *attrs =
740     (struct simple_object_xcoff_attributes *) sobj->data;
741   int u64 = attrs->magic == U64_TOCMAGIC;
742   unsigned char hdrbuf[sizeof (struct external_filehdr)];
743   unsigned char *hdr;
744   void (*set_16) (unsigned char *, unsigned short);
745   void (*set_32) (unsigned char *, unsigned int);
746   void (*set_64) (unsigned char *, ulong_type);
747 
748   hdr = &hdrbuf[0];
749 
750   set_16 = simple_object_set_big_16;
751   set_32 = simple_object_set_big_32;
752   set_64 = simple_object_set_big_64;
753 
754   memset (hdr, 0, sizeof (struct external_filehdr));
755 
756   set_16 (hdr + offsetof (struct external_filehdr, f_magic), attrs->magic);
757   set_16 (hdr + offsetof (struct external_filehdr, f_nscns), nscns);
758   /* f_timdat left as zero.  */
759   if (u64)
760     {
761       set_64 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr),
762 	      symtab_offset);
763       set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms),
764 	      nsyms);
765       /* f_opthdr left as zero.  */
766       set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags),
767 	      attrs->flags);
768     }
769   else
770     {
771       set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_symptr),
772 	      symtab_offset);
773       set_32 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_nsyms),
774 	      nsyms);
775       /* f_opthdr left as zero.  */
776       set_16 (hdr + offsetof (struct external_filehdr, u.xcoff64.f_flags),
777 	      attrs->flags);
778     }
779 
780   return simple_object_internal_write (descriptor, 0, hdrbuf,
781 				       sizeof (struct external_filehdr),
782 				       errmsg, err);
783 }
784 
785 /* Write out a XCOFF section header.  */
786 
787 static int
788 simple_object_xcoff_write_scnhdr (simple_object_write *sobj,
789 				  int descriptor,
790 				  const char *name, size_t *name_offset,
791 				  off_t scnhdr_offset, size_t scnsize,
792 				  off_t offset, unsigned int align,
793 				  const char **errmsg, int *err)
794 {
795   struct simple_object_xcoff_read *ocr =
796     (struct simple_object_xcoff_read *) sobj->data;
797   int u64 = ocr->magic == U64_TOCMAGIC;
798   void (*set_32) (unsigned char *, unsigned int);
799   void (*set_64) (unsigned char *, unsigned int);
800   unsigned char hdrbuf[sizeof (struct external_scnhdr)];
801   unsigned char *hdr;
802   size_t namelen;
803   unsigned int flags;
804 
805   set_32 = simple_object_set_big_32;
806   set_64 = simple_object_set_big_32;
807 
808   memset (hdrbuf, 0, sizeof hdrbuf);
809   hdr = &hdrbuf[0];
810 
811   namelen = strlen (name);
812   if (namelen <= SCNNMLEN)
813     strncpy ((char *) hdr + offsetof (struct external_scnhdr, s_name),
814 	     name, SCNNMLEN);
815   else
816     {
817       snprintf ((char *) hdr + offsetof (struct external_scnhdr, s_name),
818 		SCNNMLEN, "/%lu", (unsigned long) *name_offset);
819       *name_offset += namelen + 1;
820     }
821 
822   /* s_paddr left as zero.  */
823   /* s_vaddr left as zero.  */
824   if (u64)
825     {
826       set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_size),
827 	      scnsize);
828       set_64 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_scnptr),
829 	      offset);
830     }
831   else
832     {
833       set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_size),
834 	      scnsize);
835       set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_scnptr),
836 	      offset);
837     }
838   /* s_relptr left as zero.  */
839   /* s_lnnoptr left as zero.  */
840   /* s_nreloc left as zero.  */
841   /* s_nlnno left as zero.  */
842   flags = STYP_DATA;
843   if (align > 13)
844     align = 13;
845   if (u64)
846     set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff64.s_flags), flags);
847   else
848     set_32 (hdr + offsetof (struct external_scnhdr, u.xcoff32.s_flags), flags);
849 
850   return simple_object_internal_write (descriptor, scnhdr_offset, hdrbuf,
851 				       u64 ? SCNHSZ64 : SCNHSZ32,
852 				       errmsg, err);
853 }
854 
855 /* Write out a complete XCOFF file.  */
856 
857 static const char *
858 simple_object_xcoff_write_to_file (simple_object_write *sobj, int descriptor,
859 				  int *err)
860 {
861   struct simple_object_xcoff_read *ocr =
862     (struct simple_object_xcoff_read *) sobj->data;
863   int u64 = ocr->magic == U64_TOCMAGIC;
864   unsigned int nscns, secnum;
865   simple_object_write_section *section;
866   off_t scnhdr_offset;
867   size_t symtab_offset;
868   off_t secsym_offset;
869   unsigned int nsyms;
870   size_t offset;
871   size_t name_offset;
872   const char *errmsg;
873   unsigned char strsizebuf[4];
874   /* The interface doesn't give us access to the name of the input file
875      yet.  We want to use its basename for the FILE symbol.  This is
876      what 'gas' uses when told to assemble from stdin.  */
877   const char *source_filename = "fake";
878   size_t sflen;
879   union
880   {
881     struct external_syment sym;
882     union external_auxent aux;
883   } syms[2];
884   void (*set_16) (unsigned char *, unsigned short);
885   void (*set_32) (unsigned char *, unsigned int);
886 
887   set_16 = simple_object_set_big_16;
888   set_32 = simple_object_set_big_32;
889 
890   nscns = 0;
891   for (section = sobj->sections; section != NULL; section = section->next)
892     ++nscns;
893 
894   scnhdr_offset = sizeof (struct external_filehdr) - (u64 ? 4 : 0);
895   offset = scnhdr_offset + nscns * (u64 ? SCNHSZ64 : SCNHSZ32);
896   name_offset = 4;
897   for (section = sobj->sections; section != NULL; section = section->next)
898     {
899       size_t mask;
900       size_t new_offset;
901       size_t scnsize;
902       struct simple_object_write_section_buffer *buffer;
903 
904       mask = (1U << section->align) - 1;
905       new_offset = offset & mask;
906       new_offset &= ~ mask;
907       while (new_offset > offset)
908 	{
909 	  unsigned char zeroes[16];
910 	  size_t write;
911 
912 	  memset (zeroes, 0, sizeof zeroes);
913 	  write = new_offset - offset;
914 	  if (write > sizeof zeroes)
915 	    write = sizeof zeroes;
916 	  if (!simple_object_internal_write (descriptor, offset, zeroes, write,
917 					     &errmsg, err))
918 	    return errmsg;
919 	}
920 
921       scnsize = 0;
922       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
923 	{
924 	  if (!simple_object_internal_write (descriptor, offset + scnsize,
925 					     ((const unsigned char *)
926 					      buffer->buffer),
927 					     buffer->size, &errmsg, err))
928 	    return errmsg;
929 	  scnsize += buffer->size;
930 	}
931 
932       if (!simple_object_xcoff_write_scnhdr (sobj, descriptor, section->name,
933 					    &name_offset, scnhdr_offset,
934 					    scnsize, offset, section->align,
935 					    &errmsg, err))
936 	return errmsg;
937 
938       scnhdr_offset += u64 ? SCNHSZ64 : SCNHSZ32;
939       offset += scnsize;
940     }
941 
942   /* Symbol table is always half-word aligned.  */
943   offset += (offset & 1);
944   /* There is a file symbol and a section symbol per section,
945      and each of these has a single auxiliary symbol following.  */
946   nsyms = 2 * (nscns + 1);
947   symtab_offset = offset;
948   /* Advance across space reserved for symbol table to locate
949      start of string table.  */
950   offset += nsyms * SYMESZ;
951 
952   /* Write out file symbol.  */
953   memset (&syms[0], 0, sizeof (syms));
954   if (!u64)
955     strcpy ((char *)&syms[0].sym.u.xcoff32.n.n_name[0], ".file");
956   set_16 (&syms[0].sym.n_scnum[0], N_DEBUG);
957   set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE);
958   syms[0].sym.n_sclass[0] = C_FILE;
959   syms[0].sym.n_numaux[0] = 1;
960   /* The name need not be nul-terminated if it fits into the x_fname field
961      directly, but must be if it has to be placed into the string table.  */
962   sflen = strlen (source_filename);
963   if (sflen <= FILNMLEN)
964     memcpy (&syms[1].aux.x_file.x_fname[0], source_filename, sflen);
965   else
966     {
967       set_32 (&syms[1].aux.x_file._x.x_offset[0], name_offset);
968       if (!simple_object_internal_write (descriptor, offset + name_offset,
969 					 ((const unsigned char *)
970 					  source_filename),
971 					 sflen + 1, &errmsg, err))
972 	return errmsg;
973       name_offset += strlen (source_filename) + 1;
974     }
975   if (!simple_object_internal_write (descriptor, symtab_offset,
976 				     (const unsigned char *) &syms[0],
977 				     sizeof (syms), &errmsg, err))
978     return errmsg;
979 
980   /* Write the string table length, followed by the strings and section
981      symbols in step with each other.  */
982   set_32 (strsizebuf, name_offset);
983   if (!simple_object_internal_write (descriptor, offset, strsizebuf, 4,
984 				     &errmsg, err))
985     return errmsg;
986 
987   name_offset = 4;
988   secsym_offset = symtab_offset + sizeof (syms);
989   memset (&syms[0], 0, sizeof (syms));
990   set_16 (&syms[0].sym.n_type[0], IMAGE_SYM_TYPE);
991   syms[0].sym.n_sclass[0] = C_STAT;
992   syms[0].sym.n_numaux[0] = 1;
993   secnum = 1;
994 
995   for (section = sobj->sections; section != NULL; section = section->next)
996     {
997       size_t namelen;
998       size_t scnsize;
999       struct simple_object_write_section_buffer *buffer;
1000 
1001       namelen = strlen (section->name);
1002       set_16 (&syms[0].sym.n_scnum[0], secnum++);
1003       scnsize = 0;
1004       for (buffer = section->buffers; buffer != NULL; buffer = buffer->next)
1005 	scnsize += buffer->size;
1006       set_32 (&syms[1].aux.x_scn.x_scnlen[0], scnsize);
1007       if (namelen > SCNNMLEN)
1008 	{
1009 	  set_32 (&syms[0].sym.u.xcoff32.n.n.n_zeroes[0], 0);
1010 	  set_32 (&syms[0].sym.u.xcoff32.n.n.n_offset[0], name_offset);
1011 	  if (!simple_object_internal_write (descriptor, offset + name_offset,
1012 					     ((const unsigned char *)
1013 					      section->name),
1014 					     namelen + 1, &errmsg, err))
1015 	    return errmsg;
1016 	  name_offset += namelen + 1;
1017 	}
1018       else
1019 	{
1020 	  memcpy (&syms[0].sym.u.xcoff32.n.n_name[0], section->name,
1021 		  strlen (section->name));
1022 	  memset (&syms[0].sym.u.xcoff32.n.n_name[strlen (section->name)], 0,
1023 		  N_SYMNMLEN - strlen (section->name));
1024 	}
1025 
1026       if (!simple_object_internal_write (descriptor, secsym_offset,
1027 					 (const unsigned char *) &syms[0],
1028 					 sizeof (syms), &errmsg, err))
1029 	return errmsg;
1030       secsym_offset += sizeof (syms);
1031     }
1032 
1033   if (!simple_object_xcoff_write_filehdr (sobj, descriptor, nscns,
1034 					 symtab_offset, nsyms, &errmsg, err))
1035     return errmsg;
1036 
1037   return NULL;
1038 }
1039 
1040 /* Release the private data for an simple_object_write structure.  */
1041 
1042 static void
1043 simple_object_xcoff_release_write (void *data)
1044 {
1045   XDELETE (data);
1046 }
1047 
1048 /* The XCOFF functions.  */
1049 
1050 const struct simple_object_functions simple_object_xcoff_functions =
1051 {
1052   simple_object_xcoff_match,
1053   simple_object_xcoff_find_sections,
1054   simple_object_xcoff_fetch_attributes,
1055   simple_object_xcoff_release_read,
1056   simple_object_xcoff_attributes_merge,
1057   simple_object_xcoff_release_attributes,
1058   simple_object_xcoff_start_write,
1059   simple_object_xcoff_write_to_file,
1060   simple_object_xcoff_release_write,
1061   NULL
1062 };
1063