/[cmucl]/src/lisp/mach-o.c
ViewVC logotype

Contents of /src/lisp/mach-o.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.7 - (show annotations)
Mon Aug 2 21:59:43 2010 UTC (3 years, 8 months ago) by rtoy
Branch: MAIN
CVS Tags: GIT-CONVERSION, cross-sol-x86-base, snapshot-2010-12, snapshot-2010-11, cross-sol-x86-merged, snapshot-2011-09, snapshot-2011-06, snapshot-2011-07, snapshot-2011-04, snapshot-2011-02, snapshot-2011-03, snapshot-2011-01, release-20b-pre1, release-20b-pre2, cross-sparc-branch-base, snapshot-2010-08, RELEASE_20b, cross-sol-x86-2010-12-20, HEAD
Branch point for: cross-sol-x86-branch, cross-sparc-branch, RELEASE-20B-BRANCH
Changes since 1.6: +9 -3 lines
File MIME type: text/plain
More comment cleanups.
1 /*
2 * $Header: /tiger/var/lib/cvsroots/cmucl/src/lisp/mach-o.c,v 1.7 2010/08/02 21:59:43 rtoy Rel $
3 *
4 * This code was written by Raymond Toy as part of CMU Common Lisp and
5 * has been placed in the public domain.
6 *
7 * Mach-O support for generating executable images on Mac OS X (aka
8 * Darwin). This only knows enough of the Mach-O format to be able to
9 * write out very simple object files for the Lisp spaces and to read
10 * just enough of a Mach-O executable to find the segments containing
11 * the Lisp spaces.
12 *
13 * For details of the file format see " Mac OS X ABI Mach-O File
14 * Format Reference",
15 * http://developer.apple.com/mac/library/documentation/DeveloperTools/Conceptual/MachORuntime/Reference/reference.html
16 *
17 *
18 */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include "os.h"
29 #include "core.h"
30 #include "internals.h"
31 #include "globals.h"
32 #include "validate.h"
33
34 #include "elf.h"
35
36 typedef struct mach_header MachO_hdr;
37
38 /* Uncomment to enable debugging prints */
39 /* #define DEBUG_MACH_O */
40
41 /*
42 * Names of the Lisp image sections. These names must be the same as
43 * the corresponding names found in the linker script.
44 */
45
46 static char *section_names[] = {"CORDYN", "CORSTA", "CORRO"};
47
48 /*
49 * Starting addresses of the various spaces. Must be in the same
50 * order as section_names
51 */
52 static os_vm_address_t section_addr[] =
53 {
54 (os_vm_address_t) DYNAMIC_0_SPACE_START,
55 (os_vm_address_t) STATIC_SPACE_START,
56 (os_vm_address_t) READ_ONLY_SPACE_START
57 };
58
59 /* Note: write errors are not fatal. */
60 static int
61 ewrite(int fd, const void *buf, size_t nbytes, const char *func)
62 {
63 if (write(fd, buf, nbytes) < nbytes) {
64 perror(func);
65 return -1; /* Simple way to indicate error. */
66 }
67 return 0;
68 }
69
70 /*
71 Read errors are fatal, because these reads have to succeed for lisp to
72 get off the ground.
73 */
74 static void
75 eread(int d, void *buf, size_t nbytes, const char *func)
76 {
77 int res = read(d, buf, nbytes);
78
79 if (res == -1) {
80 perror(func);
81 exit(-1);
82 }
83
84 if (res < nbytes) {
85 fprintf(stderr, "Short read in %s!\n", func);
86 exit(-1);
87 }
88 }
89
90 static void
91 elseek(int d, off_t o, int whence, const char *func)
92 {
93 if (lseek(d, o, whence) == -1) {
94 perror(func);
95 exit(-1);
96 }
97 }
98
99 /*
100 * Create a file for the specified Lisp space in the specified
101 * directory.
102 */
103 static int
104 create_mach_o_file (const char *dir, int id)
105 {
106 char outfilename[FILENAME_MAX + 1];
107 int out;
108
109 /* Note: the space id will be either 1, 2 or 3. Subtract one to index
110 the name array. */
111 snprintf(outfilename, FILENAME_MAX, "%s/%s.o", dir, section_names[id - 1]);
112 out = open(outfilename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
113
114 if(!out) {
115 perror("create_mach_o_file: can't open file");
116 fprintf(stderr, "%s\n", outfilename);
117 }
118
119 return out;
120 }
121
122 /*
123 * Write the Mach-O header. We only handle what we need for our
124 * purposes.
125 */
126 static int
127 write_mach_o_header(int fd)
128 {
129 MachO_hdr eh;
130
131 /* Ident array. */
132 eh.magic = MH_MAGIC;
133 /* Currently only support x86's */
134 eh.cputype = CPU_TYPE_I386;
135 /*
136 * Support any kind x86. Should we be more specific? We need at
137 * least a pentium to run x87. For SSE2 we need at least a
138 * Pentium 4 chip. So if the core is SSE2, should we set this to
139 * Pentium 4?
140 */
141 eh.cpusubtype = CPU_SUBTYPE_I386_ALL;
142
143 eh.filetype = MH_OBJECT;
144
145 /* We only have 1 load command in our object */
146 eh.ncmds = 1;
147 /* Size of 1 segment command plus size of 1 section */
148 eh.sizeofcmds = sizeof(struct segment_command) + sizeof(struct section);
149 eh.flags = MH_NOUNDEFS | MH_NOMULTIDEFS;
150
151 return ewrite(fd, &eh, sizeof(MachO_hdr), __func__);
152 }
153
154 /*
155 * Write one segment (load) command to the object file in fd. The
156 * name of the segment is in name. We also need to specify the
157 * starting VM address (start) and the VM size (length) of the section
158 * of memory that we want this to map to.
159 */
160 static int
161 write_load_command(int fd, char* name, int length, os_vm_address_t start)
162 {
163 struct segment_command lc;
164
165 lc.cmd = LC_SEGMENT;
166 /* Size is 1 segment command + 1 section command */
167 lc.cmdsize = sizeof(lc) + sizeof(struct section);
168 /*
169 * Set the segment name. This is very important because
170 * map_core_sections looks for segment command whose segment name
171 * matches a Lisp section name.
172 */
173 strncpy(lc.segname, name, sizeof(lc.segname));
174 lc.vmaddr = (uint32_t) start;
175 /*
176 * The size is very important. map_core_sections uses this to
177 * determine how much to map.
178 */
179 lc.vmsize = length;
180 /*
181 * Offset where the data is. It's the header, the segment
182 * command, and one section.
183 */
184 lc.fileoff = lc.cmdsize + sizeof(struct mach_header);
185 lc.filesize = length;
186 lc.maxprot = VM_PROT_READ | VM_PROT_WRITE | VM_PROT_EXECUTE;
187 lc.initprot = lc.maxprot;
188 /* There's only one section for this segment command. */
189 lc.nsects = 1;
190 lc.flags = 0;
191
192 return ewrite(fd, &lc, sizeof(lc), __func__);
193 }
194
195 /*
196 * Write the section info to the object file, fd. Again, we need the
197 * object name (object_name) which is used as both the section name
198 * and the segment name. The starting VM address (start) and the VM
199 * length (length) is needed for the section.
200 */
201 static int
202 write_section(int fd, int length, os_vm_address_t start, char* object_name)
203 {
204 struct section sc;
205
206 /*
207 * sectname and segname are the same for our purposes. However,
208 * map_core_sections never looks here; it looks for the segment
209 * name from the segment commands.
210 */
211 strncpy(sc.sectname, object_name, sizeof(sc.sectname));
212 strncpy(sc.segname, object_name, sizeof(sc.segname));
213 sc.addr = (uint32_t) start;
214 sc.size = length;
215 /*
216 * Offset of the data. We have one header, one segment and one
217 * section
218 */
219 sc.offset = sizeof(struct mach_header) + sizeof(struct segment_command) +
220 sizeof(struct section);
221 sc.align = 12; /* Align on 2^12 = 4096 boundary */
222 sc.reloff = 0;
223 sc.nreloc = 0;
224 sc.flags = 0;
225 sc.reserved1 = 0;
226 sc.reserved2 = 0;
227
228 return ewrite(fd, &sc, sizeof(sc), __func__);
229 }
230
231 /*
232 * Write the actual data for the specific Lisp space to the object
233 * file. The data is read from memory starting at real_addr and
234 * consists of length bytes.
235 */
236
237 static int
238 write_section_data(int fd, long length, os_vm_address_t real_addr)
239 {
240 return ewrite(fd, (void *)real_addr, length, __func__);
241 }
242
243 /*
244 * Write out an object file containing the data for our Lisp space.
245 * The object file is written to the directory dir. The selected
246 * space is specified by id, and the starting address of the space is
247 * start, and goes to the address end.
248 */
249 int
250 write_space_object(const char *dir, int id, os_vm_address_t start, os_vm_address_t end)
251 {
252 int out = create_mach_o_file(dir, id);
253 int ret = 0;
254 /* The length should be a multiple of the page size. */
255 size_t length = end - start + (os_vm_page_size -
256 ((end - start) % os_vm_page_size));
257 static char *names[] = { "Dynamic", "Static", "Read-Only" };
258
259 if(id < 1 || id > 3) {
260 fprintf(stderr, "Invalid space id in %s: %d\n", __func__, id);
261 fprintf(stderr, "Executable not built.\n");
262 ret = -1;
263 }
264
265 /* Make id be 0-based to match array. */
266 id--;
267
268 printf("\t %s: %d bytes...\n", names[id], (end - start));
269 fflush(stdout);
270
271 if ((write_mach_o_header(out) == -1)
272 || (write_load_command(out, section_names[id], length, start) == -1)
273 || (write_section(out, length, start, section_names[id]) == -1)
274 || (write_section_data(out, length, start) == -1)) {
275 fprintf(stderr, "Executable not built.\n");
276 ret = -1;
277 }
278
279 close(out);
280 return ret;
281 }
282
283 /*
284 * Link everything together to create the executable.
285 */
286 int
287 obj_run_linker(long init_func_address, char *file)
288 {
289 lispobj libstring = SymbolValue(CMUCL_LIB); /* Get library: */
290 struct vector *vec = (struct vector *)PTR(libstring);
291 char *paths;
292 char command[FILENAME_MAX + 1];
293 char command_line[FILENAME_MAX + FILENAME_MAX + 10];
294 char *strptr;
295 struct stat st;
296 int ret;
297 extern int debug_lisp_search;
298 #ifndef UNICODE
299 paths = strdup((char *)vec->data);
300 #else
301 /*
302 * What should we do here with 16-bit characters? For now we just
303 * take the low 8-bits.
304 */
305 paths = malloc(vec->length);
306 {
307 int k;
308 unsigned short *data;
309 data = (unsigned short*) vec->data;
310
311 for (k = 0; k < vec->length; ++k) {
312 paths[k] = data[k] & 0xff;
313 }
314 }
315 #endif
316 strptr = strtok(paths, ":");
317
318 if (debug_lisp_search) {
319 printf("Searching for %s script\n", LINKER_SCRIPT);
320 }
321
322 while(strptr != NULL) {
323
324 sprintf(command, "%s/%s", strptr, LINKER_SCRIPT);
325
326 if (debug_lisp_search) {
327 printf(" %s\n", command);
328 }
329
330 if (stat(command, &st) == 0) {
331 free(paths);
332 printf("\t[%s: linking %s... \n", command, file);
333 fflush(stdout);
334 sprintf(command_line, "%s %s 0x%lx '%s' 0x%lx 0x%lx 0x%lx", command,
335 C_COMPILER, init_func_address, file,
336 (unsigned long) READ_ONLY_SPACE_START,
337 (unsigned long) STATIC_SPACE_START,
338 (unsigned long) DYNAMIC_0_SPACE_START);
339 ret = system(command_line);
340 if (ret == -1) {
341 perror("Can't run link script");
342 } else {
343 printf("\tdone]\n");
344 fflush(stdout);
345 }
346 return ret;
347 }
348 strptr = strtok(NULL, ":");
349 }
350
351 fprintf(stderr,
352 "Can't find %s script in CMUCL library directory list.\n", LINKER_SCRIPT);
353 free(paths);
354 return -1;
355 }
356
357
358 /*
359 * Read the Mach-O header from a file descriptor and stuff it into a
360 * structure. Make sure it is really a Mach-O header etc.
361 */
362 static void
363 read_mach_o_header(int fd, MachO_hdr *ehp)
364 {
365 eread(fd, ehp, sizeof(MachO_hdr), __func__);
366
367 if (ehp->magic != MH_MAGIC) {
368 fprintf(stderr,
369 "Bad Mach-O magic number --- not a Mach-O file. Exiting in %s.\n",
370 __func__);
371 exit(-1);
372 }
373 }
374
375
376 /*
377 * Map the built-in lisp core sections.
378 *
379 * NOTE! We need to do this without using malloc because the memory
380 * layout is not set until some time after this is done.
381 */
382 void
383 map_core_sections(const char *exec_name)
384 {
385 MachO_hdr eh;
386 int exec_fd;
387 int sections_remaining = 3;
388 int i;
389 int j;
390 extern int image_dynamic_space_size;
391 extern int image_static_space_size;
392 extern int image_read_only_space_size;
393
394 if (!(exec_fd = open(exec_name, O_RDONLY))) {
395 perror("Can't open executable!");
396 exit(-1);
397 }
398
399 read_mach_o_header(exec_fd, &eh);
400
401 for (i = 0; i < eh.ncmds && sections_remaining > 0; i++) {
402 struct load_command lc;
403 struct segment_command sc;
404
405 /*
406 * Read the load command to see what kind of command it is and
407 * how big it is.
408 */
409
410 eread(exec_fd, &lc, sizeof(lc), __func__);
411 #ifdef DEBUG_MACH_O
412 fprintf(stderr, "Load %d: cmd = %d, cmdsize = %d\n", i, lc.cmd, lc.cmdsize);
413 #endif
414
415 if (lc.cmd == LC_SEGMENT) {
416 /*
417 * Got a segment command, so read the rest of the command so
418 * we can see if it's the segment for one of our Lisp
419 * spaces.
420 */
421 #ifdef DEBUG_MACH_O
422 fprintf(stderr, "Reading next %ld bytes for SEGMENT\n", sizeof(sc) - sizeof(lc));
423 #endif
424
425 eread(exec_fd, &sc.segname, sizeof(sc) - sizeof(lc), __func__);
426
427 #ifdef DEBUG_MACH_O
428 fprintf(stderr, "LC_SEGMENT: name = %s\n", sc.segname);
429 #endif
430
431 /* See if the segment name matches any of our section names */
432 for (j = 0; j < 3; ++j) {
433 if (strncmp(sc.segname, section_names[j], sizeof(sc.segname)) == 0) {
434 os_vm_address_t addr;
435
436 /* Found a core segment. Map it! */
437 #ifdef DEBUG_MACH_O
438 fprintf(stderr, "Matched!\n");
439 fprintf(stderr, " Fileoff = %u\n", sc.fileoff);
440 fprintf(stderr, " vmaddr = 0x%x\n", sc.vmaddr);
441 fprintf(stderr, " vmsize = 0x%x\n", sc.vmsize);
442 #endif
443 /*
444 * We don't care what address the segment has. We
445 * will map it where want it to go.
446 */
447
448 addr = section_addr[j];
449
450 if ((os_vm_address_t) os_map(exec_fd, sc.fileoff,
451 (os_vm_address_t) addr,
452 sc.vmsize)
453 == (os_vm_address_t) -1) {
454 fprintf(stderr, "%s: Can't map section %s\n", __func__, section_names[j]);
455 exit(-1);
456 }
457 switch (j) {
458 case 0:
459 /* Dynamic space */
460 image_dynamic_space_size = sc.vmsize;
461 break;
462 case 1:
463 /* Static space */
464 image_static_space_size = sc.vmsize;
465 break;
466 case 2:
467 /* Read only */
468 image_read_only_space_size = sc.vmsize;
469 break;
470 default:
471 /* Shouldn't happen! */
472 abort();
473 }
474 --sections_remaining;
475 break;
476 }
477 }
478 #ifdef DEBUG_MACH_O
479 fprintf(stderr, "Skipping %ld remainder bytes left in command\n",
480 lc.cmdsize - sizeof(sc));
481 #endif
482 elseek(exec_fd, lc.cmdsize - sizeof(sc), SEEK_CUR, __func__);
483 } else {
484 /* Not a segment command, so seek to the next command */
485 #ifdef DEBUG_MACH_O
486 fprintf(stderr, "Seeking by %ld bytes\n", lc.cmdsize - sizeof(lc));
487 #endif
488 elseek(exec_fd, lc.cmdsize - sizeof(lc), SEEK_CUR, __func__);
489 }
490 }
491
492 close(exec_fd);
493
494 if (sections_remaining != 0) {
495 fprintf(stderr, "Couldn't map all core sections! Exiting!\n");
496 exit(-1);
497 }
498 }

  ViewVC Help
Powered by ViewVC 1.1.5