1/* AIX cross support for collect2. 2 Copyright (C) 2009-2015 Free Software Foundation, Inc. 3 4This file is part of GCC. 5 6GCC is free software; you can redistribute it and/or modify it under 7the terms of the GNU General Public License as published by the Free 8Software Foundation; either version 3, or (at your option) any later 9version. 10 11GCC is distributed in the hope that it will be useful, but WITHOUT ANY 12WARRANTY; without even the implied warranty of MERCHANTABILITY or 13FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14for more details. 15 16You should have received a copy of the GNU General Public License 17along with GCC; see the file COPYING3. If not see 18<http://www.gnu.org/licenses/>. */ 19 20#include "config.h" 21#include "system.h" 22#include "coretypes.h" 23#include "tm.h" 24#include "collect2-aix.h" 25 26#ifdef CROSS_AIX_SUPPORT 27 28/* Read SIZE bytes starting at DATA as a big-endian value. */ 29 30static inline bfd_vma 31read_value (char *data, unsigned int size) 32{ 33 bfd_vma value; 34 unsigned int i; 35 36 value = 0; 37 for (i = 0; i < size; i++) 38 { 39 value <<= 8; 40 value += (unsigned char) data[i]; 41 } 42 return value; 43} 44 45/* FIELD is a char array. Read the contents as a big-endian integer. */ 46#define READ_FIELD(FIELD) \ 47 read_value (FIELD, sizeof (FIELD)) 48 49/* OBJECT is a char pointer to an in-file object of type struct TYPE. 50 Return the address of field FIELD. */ 51#define OBJECT_FIELD(OBJECT, TYPE, FIELD) \ 52 (OBJECT) + offsetof (struct TYPE, FIELD) 53 54/* Return the size of FIELD, which is a field of struct TYPE. */ 55#define FIELD_SIZE(TYPE, FIELD) \ 56 sizeof (((struct TYPE *) (0))->FIELD) 57 58/* OBJECT is a char pointer to an in-file object of type struct TYPE. 59 Read the value of field FIELD as a big-endian integer. */ 60#define READ_OBJECT(OBJECT, TYPE, FIELD) \ 61 read_value (OBJECT_FIELD (OBJECT, TYPE, FIELD), FIELD_SIZE (TYPE, FIELD)) 62 63/* Copy FIELD from an external structure of type TYPE at address FROM 64 to an internal structure pointed to by TO. */ 65#define COPY_FIELD(TO, FROM, TYPE, FIELD) \ 66 ((TO)->FIELD = READ_OBJECT (FROM, TYPE, FIELD)) 67 68/* Return true if STRING is less than SIZE bytes long. EXTRA_TERMINATOR 69 is another character (besides '\0') that acts as a terminator, 70 or '\0' if none. */ 71 72static bool 73string_within_bounds_p (const char *string, size_t size, char extra_terminator) 74{ 75 const char *p; 76 77 for (p = string; p < string + size; p++) 78 if (*p == '\0' || *p == extra_terminator) 79 return true; 80 return false; 81} 82 83/* STRING is a pointer to a char array. Try to read its value as an 84 ASCII-encoded integer. On success, return true and store the result 85 in TARGET. */ 86#define PARSE_INTEGER(TARGET, STRING) \ 87 (string_within_bounds_p (&(STRING)[0], sizeof (STRING), ' ') \ 88 && ((TARGET) = strtoul (STRING, NULL, 0), true)) 89 90/* Check that LDFILE's current object has SIZE bytes starting at OFFSET. */ 91 92static inline bool 93within_object_p (LDFILE *ldfile, size_t offset, size_t size) 94{ 95 return offset <= ldfile->object_size && offset + size <= ldfile->object_size; 96} 97 98/* Try to read the file header for an XCOFF object at OFFSET bytes into 99 LDFILE. The object is expected to be OBJECT_SIZE bytes in size. 100 If the object is a member of an archive, NEXT_MEMBER is the offset 101 of the next member, otherwise it is -1. 102 103 Return true on success, recording the object information in LDFILE. */ 104 105static bool 106read_xcoff_object (LDFILE *ldfile, size_t offset, size_t object_size, 107 off_t next_member) 108{ 109 struct internal_filehdr *internal; 110 char *external; 111 void *map; 112 size_t page_size; 113 114 /* First try to map the file into memory. */ 115 page_size = getpagesize (); 116 ldfile->page_offset = offset & (page_size - 1); 117 map = mmap (NULL, object_size + ldfile->page_offset, PROT_READ, 118 MAP_SHARED, ldfile->fd, offset - ldfile->page_offset); 119 if (map == MAP_FAILED) 120 return false; 121 122 /* Record the success. */ 123 ldfile->object = (char *) map + ldfile->page_offset; 124 ldfile->object_size = object_size; 125 ldfile->next_member = next_member; 126 127 /* Read the magic value to determine the type of file. */ 128 if (!within_object_p (ldfile, 0, F_MAGIC_SIZE)) 129 return false; 130 131 internal = &ldfile->filehdr; 132 external = ldfile->object; 133 internal->f_magic = read_value (external, F_MAGIC_SIZE); 134 if (internal->f_magic == U802TOCMAGIC) 135 { 136 if (!within_object_p (ldfile, 0, sizeof (struct external_filehdr_32))) 137 return false; 138 139 COPY_FIELD (internal, external, external_filehdr_32, f_nscns); 140 COPY_FIELD (internal, external, external_filehdr_32, f_timdat); 141 COPY_FIELD (internal, external, external_filehdr_32, f_symptr); 142 COPY_FIELD (internal, external, external_filehdr_32, f_nsyms); 143 COPY_FIELD (internal, external, external_filehdr_32, f_opthdr); 144 COPY_FIELD (internal, external, external_filehdr_32, f_flags); 145 return true; 146 } 147 else if (internal->f_magic == U803XTOCMAGIC 148 || internal->f_magic == U64_TOCMAGIC) 149 { 150 if (!within_object_p (ldfile, 0, sizeof (struct external_filehdr_64))) 151 return false; 152 153 COPY_FIELD (internal, external, external_filehdr_64, f_nscns); 154 COPY_FIELD (internal, external, external_filehdr_64, f_timdat); 155 COPY_FIELD (internal, external, external_filehdr_64, f_symptr); 156 COPY_FIELD (internal, external, external_filehdr_64, f_nsyms); 157 COPY_FIELD (internal, external, external_filehdr_64, f_opthdr); 158 COPY_FIELD (internal, external, external_filehdr_64, f_flags); 159 return true; 160 } 161 return false; 162} 163 164/* Try to read an archive member at OFFSET bytes into LDFILE. 165 Return true on success, recording the member and object 166 information in LDFILE. */ 167 168static bool 169read_archive_member (LDFILE *ldfile, size_t offset) 170{ 171 struct external_big_ar_member member; 172 size_t namlen; 173 size_t size; 174 off_t next_member; 175 176 if (lseek (ldfile->fd, offset, SEEK_SET) >= 0 177 && read (ldfile->fd, &member, sizeof (member)) == sizeof (member) 178 && PARSE_INTEGER (namlen, member.ar_namlen) 179 /* Stop once we reach the member table entry, which has a name 180 of length 0. */ 181 && namlen > 0 182 && PARSE_INTEGER (size, member.ar_size) 183 && PARSE_INTEGER (next_member, member.ar_nextoff)) 184 { 185 /* The archive is followed by an even-padded name, then by 186 a magic string of length SXCOFFARFMAG. The object itself 187 starts after that. */ 188 offset += sizeof (member) + namlen + SXCOFFARFMAG; 189 offset += offset & 1; 190 return read_xcoff_object (ldfile, offset, size, next_member); 191 } 192 return false; 193} 194 195/* Try to treat LDFILE as a non-empty big archive. Return true 196 on success, storing the member and object information for 197 the first member in LDFILE. */ 198 199static bool 200read_big_archive (LDFILE *ldfile) 201{ 202 struct external_big_ar_filehdr filehdr; 203 size_t offset; 204 205 return (lseek (ldfile->fd, 0L, SEEK_SET) == 0 206 && read (ldfile->fd, &filehdr, sizeof (filehdr)) == sizeof (filehdr) 207 && memcmp (filehdr.fl_magic, FL_MAGIC_BIG_AR, FL_MAGIC_SIZE) == 0 208 && PARSE_INTEGER (offset, filehdr.fl_firstmemoff) 209 && read_archive_member (ldfile, offset)); 210} 211 212/* LDFILE is a zero-initialized structure. Try to open FILENAME, 213 returning true on success. */ 214 215static bool 216open_file (LDFILE *ldfile, const char *filename) 217{ 218 struct stat st; 219 220 ldfile->fd = open (filename, O_RDONLY); 221 if (ldfile->fd < 0) 222 return false; 223 224 if (read_big_archive (ldfile)) 225 return true; 226 227 if (fstat (ldfile->fd, &st) < 0) 228 return false; 229 230 return read_xcoff_object (ldfile, 0, st.st_size, -1); 231} 232 233/* Release the memory associated with the current object, if one has 234 been mapped. */ 235 236static void 237free_object (LDFILE *ldfile) 238{ 239 if (ldfile->object) 240 munmap (ldfile->object - ldfile->page_offset, 241 ldfile->object_size + ldfile->page_offset); 242} 243 244/* Free LDFILE and all resources associated with it. */ 245 246static void 247free_ldfile (LDFILE *ldfile) 248{ 249 if (ldfile->fd >= 0) 250 close (ldfile->fd); 251 XDELETE (ldfile); 252} 253 254/* Implement the API-defined ldopen function. */ 255 256LDFILE * 257ldopen (char *filename, LDFILE *ldfile) 258{ 259 if (ldfile == NULL) 260 { 261 ldfile = XCNEW (LDFILE); 262 if (!open_file (ldfile, filename)) 263 { 264 free_object (ldfile); 265 free_ldfile (ldfile); 266 return NULL; 267 } 268 } 269 return ldfile; 270} 271 272/* Implement the API-defined ldtbread function. */ 273 274int 275ldtbread (LDFILE *ldfile, long index, SYMENT *internal) 276{ 277 size_t offset, name_length; 278 char *external; 279 280 /* Make sure that the symbol index is valid. */ 281 if (index < 0 || index >= HEADER (ldfile).f_nsyms) 282 return FAILURE; 283 284 /* Work out the offset of the symbol table entry. */ 285 offset = HEADER (ldfile).f_symptr + index * sizeof (struct external_syment); 286 if (!within_object_p (ldfile, offset, sizeof (struct external_syment))) 287 return FAILURE; 288 289 /* Read all the fields. The format differs between 32-bit and 290 64-bit files. */ 291 external = ldfile->object + offset; 292 if (HEADER (ldfile).f_magic == U802TOCMAGIC) 293 { 294 /* Copy the n_zeroes/n_offset interpretation. */ 295 internal->n_zeroes = READ_OBJECT (external, external_syment, 296 u.xcoff32.u.u.n_zeroes); 297 internal->n_offset = READ_OBJECT (external, external_syment, 298 u.xcoff32.u.u.n_offset); 299 300 /* Copy the n_name interpretation. The internal version has room 301 for a null terminator. */ 302 name_length = FIELD_SIZE (external_syment, u.xcoff32.u.n_name); 303 memcpy (internal->n_name, 304 external + offsetof (struct external_syment, u.xcoff32.u.n_name), 305 name_length); 306 internal->n_name[name_length] = 0; 307 308 internal->n_value = READ_OBJECT (external, external_syment, 309 u.xcoff32.n_value); 310 } 311 else 312 { 313 internal->n_zeroes = 0; 314 internal->n_offset = READ_OBJECT (external, external_syment, 315 u.xcoff64.n_offset); 316 internal->n_value = READ_OBJECT (external, external_syment, 317 u.xcoff64.n_value); 318 } 319 COPY_FIELD (internal, external, external_syment, n_scnum); 320 COPY_FIELD (internal, external, external_syment, n_type); 321 COPY_FIELD (internal, external, external_syment, n_sclass); 322 COPY_FIELD (internal, external, external_syment, n_numaux); 323 return SUCCESS; 324} 325 326/* Implement the API-defined ldgetname function. */ 327 328char * 329ldgetname (LDFILE *ldfile, SYMENT *symbol) 330{ 331 char *name; 332 size_t offset; 333 334 /* If the zeroes field is nonzero, the name is in the symbol table 335 entry itself. */ 336 if (symbol->n_zeroes != 0) 337 return symbol->n_name; 338 339 /* Otherwise, the symbol table entry contains an offset into the 340 string table, which starts after the end of the symbol table. */ 341 offset = (HEADER (ldfile).f_symptr 342 + HEADER (ldfile).f_nsyms * sizeof (struct external_syment) 343 + symbol->n_offset); 344 if (offset >= ldfile->object_size) 345 return NULL; 346 347 /* Make sure that the name is entirely contained within the object. */ 348 name = ldfile->object + offset; 349 if (!string_within_bounds_p (name, ldfile->object_size - offset, '\0')) 350 return NULL; 351 352 return name; 353} 354 355/* Implement the API-defined ldclose function. */ 356 357int 358ldclose (LDFILE *ldfile) 359{ 360 free_object (ldfile); 361 if (ldfile->next_member >= 0 362 && read_archive_member (ldfile, ldfile->next_member)) 363 return FAILURE; 364 365 free_ldfile (ldfile); 366 return SUCCESS; 367} 368 369#endif 370