]>
Commit | Line | Data |
---|---|---|
dfb636dc | 1 | /* AIX cross support for collect2. |
71f3e391 | 2 | Copyright (C) 2009, 2010 Free Software Foundation, Inc. |
dfb636dc RS |
3 | |
4 | This file is part of GCC. | |
5 | ||
6 | GCC is free software; you can redistribute it and/or modify it under | |
7 | the terms of the GNU General Public License as published by the Free | |
8 | Software Foundation; either version 3, or (at your option) any later | |
9 | version. | |
10 | ||
11 | GCC is distributed in the hope that it will be useful, but WITHOUT ANY | |
12 | WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
14 | for more details. | |
15 | ||
16 | You should have received a copy of the GNU General Public License | |
17 | along 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 | ||
dfb636dc RS |
28 | /* Read SIZE bytes starting at DATA as a big-endian value. */ |
29 | ||
30 | static inline bfd_vma | |
31 | read_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 | ||
72 | static bool | |
73 | string_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 | ||
92 | static inline bool | |
93 | within_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 | ||
105 | static bool | |
106 | read_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 | ||
168 | static bool | |
169 | read_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 | ||
199 | static bool | |
200 | read_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 | ||
215 | static bool | |
216 | open_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 | ||
236 | static void | |
237 | free_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 | ||
246 | static void | |
247 | free_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 | ||
256 | LDFILE * | |
257 | ldopen (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 | ||
274 | int | |
275 | ldtbread (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 | ||
328 | char * | |
329 | ldgetname (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 | ||
357 | int | |
358 | ldclose (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 |