/[gentoo-projects]/pax-utils/paxmacho.c
Gentoo

Contents of /pax-utils/paxmacho.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.20 - (show annotations) (download) (as text)
Sun Nov 4 07:26:24 2012 UTC (20 months, 3 weeks ago) by vapier
Branch: MAIN
Changes since 1.19: +5 -5 lines
File MIME type: text/x-csrc
update copyright years

1 /*
2 * Copyright 2003-2012 Gentoo Foundation
3 * Distributed under the terms of the GNU General Public License v2
4 * $Header: /var/cvsroot/gentoo-projects/pax-utils/paxmacho.c,v 1.19 2010/12/08 01:29:36 vapier Exp $
5 *
6 * Copyright 2005-2012 Ned Ludd - <solar@gentoo.org>
7 * Copyright 2005-2012 Mike Frysinger - <vapier@gentoo.org>
8 * 2008-2012 Fabian Groffen - <grobian@gentoo.org>
9 */
10
11 #include "paxinc.h"
12
13 /* lil' static string pool */
14 static const char STR_BE[] = "BE";
15 static const char STR_LE[] = "LE";
16 static const char STR_PPC[] = "ppc";
17 static const char STR_PPC64[] = "ppc64";
18 static const char STR_I386[] = "i386";
19 static const char STR_X86_64[] = "x86_64";
20 static const char STR_ARM[] = "arm"; /* iPhone */
21 static const char STR_UNKNOWN[] = "unknown";
22
23 #define QUERY(n) { #n, n }
24 typedef const struct {
25 const char *str;
26 int value;
27 } pairtype;
28
29 static inline const char *find_pairtype(pairtype *pt, int type)
30 {
31 size_t i;
32 for (i = 0; pt[i].str; ++i)
33 if (type == pt[i].value)
34 return pt[i].str;
35 return "UNKNOWN TYPE";
36 }
37
38 /* translate misc mach-o MH_ defines */
39 static pairtype macho_mh_type[] = {
40 QUERY(MH_OBJECT),
41 QUERY(MH_EXECUTE),
42 QUERY(MH_BUNDLE),
43 QUERY(MH_DYLIB),
44 QUERY(MH_PRELOAD),
45 QUERY(MH_CORE),
46 QUERY(MH_DYLINKER),
47 QUERY(MH_DYLIB_STUB),
48 QUERY(MH_DSYM),
49 { 0, 0 }
50 };
51 const char *get_machomhtype(fatobj *fobj)
52 {
53 /* can use 32-bits header, since 64 and 32 are aligned here */
54 return find_pairtype(macho_mh_type, MOBJGET(fobj, mhdr.hdr32->filetype));
55 }
56
57 /* translate misc mach-o MH_ flags */
58 static pairtype macho_mh_flag[] = {
59 QUERY(MH_NOUNDEFS),
60 QUERY(MH_INCRLINK),
61 QUERY(MH_DYLDLINK),
62 QUERY(MH_TWOLEVEL),
63 QUERY(MH_BINDATLOAD),
64 QUERY(MH_PREBOUND),
65 QUERY(MH_PREBINDABLE),
66 QUERY(MH_NOFIXPREBINDING),
67 QUERY(MH_ALLMODSBOUND),
68 QUERY(MH_CANONICAL),
69 QUERY(MH_SPLIT_SEGS),
70 QUERY(MH_FORCE_FLAT),
71 QUERY(MH_SUBSECTIONS_VIA_SYMBOLS),
72 QUERY(MH_NOMULTIDEFS),
73 { 0, 0 }
74 };
75 void get_machomhflags(fatobj *fobj, char **ret, size_t *ret_len)
76 {
77 uint32_t flags;
78 int i;
79 char first = 1;
80
81 /* can use 32-bits header, since 64 and 32 are aligned here */
82 flags = MOBJGET(fobj, mhdr.hdr32->flags);
83
84 for (i = 0; macho_mh_flag[i].str; ++i)
85 if ((flags & macho_mh_flag[i].value) == macho_mh_flag[i].value) {
86 if (!first)
87 xchrcat(ret, ',', ret_len);
88 xstrcat(ret, macho_mh_flag[i].str, ret_len);
89 first = 0;
90 }
91 }
92
93 static pairtype macho_cputype[] = {
94 QUERY(CPU_TYPE_POWERPC),
95 QUERY(CPU_TYPE_I386),
96 QUERY(CPU_TYPE_ARM),
97 QUERY(CPU_TYPE_POWERPC64),
98 QUERY(CPU_TYPE_X86_64),
99 { 0, 0 }
100 };
101 const char *get_machocputype(fatobj *fobj)
102 {
103 /* can use 32-bits header, since 64 and 32 are aligned here */
104 const char *ret = find_pairtype(macho_cputype, MOBJGET(fobj, mhdr.hdr32->cputype));
105 return ret + sizeof("CPU_TYPE_") - 1;
106 }
107
108 /* translate cpusubtypes */
109 static pairtype macho_cpusubtypeppc[] = {
110 QUERY(CPU_SUBTYPE_POWERPC_ALL),
111 QUERY(CPU_SUBTYPE_POWERPC_601),
112 QUERY(CPU_SUBTYPE_POWERPC_602),
113 QUERY(CPU_SUBTYPE_POWERPC_603),
114 QUERY(CPU_SUBTYPE_POWERPC_603e),
115 QUERY(CPU_SUBTYPE_POWERPC_603ev),
116 QUERY(CPU_SUBTYPE_POWERPC_604),
117 QUERY(CPU_SUBTYPE_POWERPC_604e),
118 QUERY(CPU_SUBTYPE_POWERPC_620),
119 QUERY(CPU_SUBTYPE_POWERPC_750),
120 QUERY(CPU_SUBTYPE_POWERPC_7400),
121 QUERY(CPU_SUBTYPE_POWERPC_7450),
122 QUERY(CPU_SUBTYPE_POWERPC_970),
123 { 0, 0 }
124 };
125 static pairtype macho_cpusubtypex86[] = {
126 QUERY(CPU_SUBTYPE_I386_ALL),
127 QUERY(CPU_SUBTYPE_486),
128 QUERY(CPU_SUBTYPE_586),
129 QUERY(CPU_SUBTYPE_PENTIUM_3),
130 QUERY(CPU_SUBTYPE_PENTIUM_M),
131 QUERY(CPU_SUBTYPE_PENTIUM_4),
132 QUERY(CPU_SUBTYPE_ITANIUM),
133 QUERY(CPU_SUBTYPE_XEON),
134 { 0, 0 }
135 };
136 const char *get_machosubcputype(fatobj *fobj)
137 {
138 const char *ret;
139 /* can use 32-bits header, since 64 and 32 are aligned here */
140 uint32_t type = MOBJGET(fobj, mhdr.hdr32->cputype);
141 pairtype *pt = NULL;
142
143 if (type == CPU_TYPE_I386 || type == CPU_TYPE_X86_64)
144 pt = macho_cpusubtypex86;
145 else if (type == CPU_TYPE_POWERPC || type == CPU_TYPE_POWERPC64)
146 pt = macho_cpusubtypeppc;
147
148 if (pt) {
149 type = MOBJGET(fobj, mhdr.hdr32->cpusubtype);
150 ret = find_pairtype(pt, type);
151 return ret + sizeof("CPU_SUBTYPE_") - 1;
152 } else
153 return STR_UNKNOWN;
154 }
155
156 /* Determines the type of this object, and sets the right 32-bit or
157 * 64-bits pointer. The ismach64 flag is filled in appropriately. The
158 * return of this function is the read magic value, or 0 when the file
159 * is not recognised.
160 * Note: the input addr must be enough to map on struct mach_header! */
161 inline static uint32_t read_mach_header(fatobj *fobj, void *addr)
162 {
163 struct mach_header *mhdr = addr;
164 fobj->mhdata = addr;
165 switch (mhdr->magic) {
166 case MH_CIGAM:
167 fobj->swapped = 1;
168 case MH_MAGIC:
169 /* 32-bits */
170 fobj->ismach64 = 0;
171 fobj->mhdr.hdr32 = mhdr;
172 fobj->isbigendian = (*fobj->mhdata == (char)(MH_MAGIC >> 24) ? 1 : 0);
173 return mhdr->magic;
174 case MH_CIGAM_64:
175 fobj->swapped = 1;
176 case MH_MAGIC_64:
177 /* 64-bits */
178 fobj->ismach64 = 1;
179 fobj->mhdr.hdr64 = addr;
180 fobj->isbigendian = (*fobj->mhdata == (char)(MH_MAGIC_64 >> 24) ? 1 : 0);
181 return mhdr->magic;
182 default:
183 return 0; /* unrecognised file */
184 }
185 }
186
187 /* Read a macho into memory, returning a fatobj struct with at least one
188 * arch. */
189 fatobj *readmacho(const char *filename)
190 {
191 struct stat st;
192 int fd;
193
194 if (stat(filename, &st) == -1)
195 return NULL;
196
197 if ((fd = open(filename, O_RDONLY)) == -1)
198 return NULL;
199
200 return readmacho_fd(filename, fd, st.st_size);
201 }
202
203 fatobj *readmacho_fd(const char *filename, int fd, size_t len)
204 {
205 char *data;
206 fatobj *ret;
207
208 if (len == 0) {
209 struct stat st;
210 if (fstat(fd, &st) == -1)
211 return NULL;
212 len = st.st_size;
213 if (len == 0)
214 return NULL;
215 }
216
217 data = mmap(0, len, PROT_READ, MAP_PRIVATE, fd, 0);
218 if (data == MAP_FAILED) {
219 warn("mmap on '%s' of %zu bytes failed :(", filename, len);
220 return NULL;
221 }
222
223 ret = readmacho_buffer(filename, data, len);
224 if (ret != NULL) {
225 ret->fd = fd;
226 return ret;
227 }
228
229 munmap(data, len);
230 return NULL;
231 }
232
233 fatobj *readmacho_buffer(const char *filename, char *buffer, size_t buffer_len)
234 {
235 struct fat_header *fhdr;
236 fatobj *ret = xmalloc(sizeof(*ret));
237
238 ret->fd = -1;
239 ret->filename = filename;
240 ret->base_filename = strrchr(ret->filename, '/');
241 ret->base_filename =
242 (ret->base_filename == NULL ? ret->filename : ret->base_filename + 1);
243 ret->len = buffer_len;
244 ret->data = buffer;
245 ret->swapped = 0;
246
247 /* make sure we have enough bytes to scan */
248 if (ret->len <= sizeof(struct fat_header))
249 return NULL;
250
251 fhdr = ret->data;
252 /* Check what kind of file this is. Unfortunately we don't have
253 * configure, so we don't know if we're on big or little endian, so
254 * we cannot check if the fat_header is in bigendian like it should.
255 */
256 if (fhdr->magic == FAT_MAGIC || fhdr->magic == FAT_CIGAM) {
257 /* we're indeed in a FAT file */
258 int i;
259 fatobj *fobj = ret;
260 struct fat_arch *farch;
261 void *dptr = ret->data + sizeof(struct fat_header);
262 uint32_t bufleft = ret->len - sizeof(struct fat_header);
263 char swapped = 0;
264 uint32_t narchs = fhdr->nfat_arch;
265 uint32_t offset;
266
267 /* FAT headers are always big-endian, so swap if on little
268 * machines... */
269 if (fhdr->magic == FAT_CIGAM) {
270 swapped = 1;
271 narchs = bswap_32(narchs);
272 }
273
274 /* can we read the headers at all?
275 * beware of corrupt files and Java bytecode which shares
276 * the same magic with us :( */
277 if (sizeof(struct fat_arch) * narchs > bufleft)
278 return NULL;
279
280 for (i = 1; i <= narchs; i++) {
281 farch = (struct fat_arch *)dptr;
282 offset = MGET(swapped, farch->offset);
283 if (offset + sizeof(struct mach_header) >= bufleft ||
284 read_mach_header(fobj, ret->data + offset) == 0)
285 return NULL;
286 if (i < narchs) {
287 fobj = fobj->next = xzalloc(sizeof(*fobj));
288 /* filename and size are necessary for printing */
289 fobj->filename = ret->filename;
290 fobj->base_filename = ret->base_filename;
291 fobj->len = ret->len;
292 } else {
293 fobj->next = NULL;
294 }
295 dptr += sizeof(struct fat_arch);
296 bufleft -= sizeof(struct fat_arch);
297 }
298 } else {
299 /* simple Mach-O file, treat as single arch FAT file */
300 if (ret->len < sizeof(struct mach_header) ||
301 read_mach_header(ret, ret->data) == 0)
302 return NULL;
303 ret->next = NULL;
304 }
305
306 return ret;
307 }
308
309 /* undo the readmacho() stuff */
310 void unreadmacho(fatobj *macho)
311 {
312 if (macho->data != NULL) {
313 munmap(macho->data, macho->len);
314 close(macho->fd);
315 }
316 /* free all arches recursively */
317 if (macho->next != NULL)
318 unreadmacho(macho->next);
319 free(macho);
320 }
321
322 /* Returns the first load_command in the file (after the mach_header)
323 * and allocates a loadcmd struct to store it together with some
324 * convenience data. The struct can be manually freed, if not traversed
325 * until the end of the load section. */
326 loadcmd *firstloadcmd(fatobj *fobj)
327 {
328 loadcmd *ret = xmalloc(sizeof(*ret));
329 ret->data = fobj->mhdata +
330 (fobj->ismach64 ? sizeof(struct mach_header_64) : sizeof(struct mach_header));
331 ret->lcmd = ret->data;
332 ret->cleft = MOBJGET(fobj, mhdr.hdr32->ncmds); /* 32 and 64 bits are aligned here */
333 ret->align = (fobj->ismach64 ? 8 : 4);
334 ret->swapped = fobj->swapped;
335 /* a bit useless, but a nice consistency check for ourselves now */
336 if (ret->lcmd->cmdsize % ret->align != 0)
337 warn("cmdsize isn't properly aligned on %d bytes boundary (%d)",
338 ret->align, ret->lcmd->cmdsize);
339 return ret;
340 }
341
342 /* Sets up the given loadcmd struct with the next load command, or frees
343 * it if there are no more load commands. If a new load command was
344 * loaded, 1 is returned, 0 otherwise. This behaviour is useful when
345 * looping over all load commands, since firstloadcmd will allocate the
346 * loadcmd struct, and nextloadcmd will free it once all load commands
347 * have been seen. */
348 int nextloadcmd(loadcmd *lcmd)
349 {
350 uint32_t size = MOBJGET(lcmd, lcmd->cmdsize);
351
352 if (--(lcmd->cleft) == 0) {
353 free(lcmd);
354 return 0;
355 }
356
357 if (size % lcmd->align != 0) {
358 /* fix alignment, this should actually never happen, but the doc
359 * says we have to pad if the alignment sucks */
360 size += lcmd->align - (size % lcmd->align);
361 }
362 lcmd->data += size;
363 lcmd->lcmd = lcmd->data;
364
365 return 1;
366 }
367
368 const char *get_machoendian(fatobj *fobj)
369 {
370 return fobj->isbigendian ? STR_BE : STR_LE;
371 }
372
373 const char *get_machomtype(fatobj *fobj)
374 {
375 switch (MOBJGET(fobj, mhdr.hdr32->cputype)) {
376 case CPU_TYPE_POWERPC: return STR_PPC;
377 case CPU_TYPE_I386: return STR_I386;
378 case CPU_TYPE_ARM: return STR_ARM;
379 case CPU_TYPE_POWERPC64: return STR_PPC64;
380 case CPU_TYPE_X86_64: return STR_X86_64;
381 default: return STR_UNKNOWN;
382 }
383 }

  ViewVC Help
Powered by ViewVC 1.1.20