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