ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Darwin/lowmem.c
Revision: 1.6
Committed: 2009-11-13T01:57:48Z (15 years ago) by asvitkine
Content type: text/plain
Branch: MAIN
Changes since 1.5: +138 -47 lines
Log Message:
[ Patch from Jean-Pierre <chombier@free.fr> ]
Make lowmem be able to deal with x86_64 binaries.

File Contents

# Content
1 /*
2 * lowmem.c - enable access to low memory globals on Darwin
3 *
4 * Copyright (c) 2003 Michael Z. Sliczniak
5 *
6 * Basilisk II (C) 1997-2005 Christian Bauer
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22
23 #include <sys/types.h>
24 #include <sys/mman.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <mach/vm_prot.h>
31 #include <mach-o/loader.h>
32 #include <mach-o/fat.h>
33
34 static const char progname[] = "lowmem";
35 static const char *filename;
36
37 static int do_swap = 0;
38
39 static uint32_t target_uint32(uint32_t value)
40 {
41 if (do_swap)
42 value = OSSwapInt32(value);
43 return value;
44 }
45
46 void pagezero_32(struct mach_header *machhead)
47 {
48 struct segment_command *sc_cmd;
49
50 if (target_uint32(machhead->filetype) != MH_EXECUTE) {
51 (void)fprintf(stderr, "%s: %s does not appear to be an executable file\n",
52 progname, filename);
53 exit(1);
54 }
55 if (machhead->ncmds == 0) {
56 (void)fprintf(stderr, "%s: %s does not contain any load commands\n",
57 progname, filename);
58 exit(1);
59 }
60 sc_cmd = (void *)&machhead[1];
61 if (target_uint32(sc_cmd->cmd) != LC_SEGMENT){
62 (void)fprintf(stderr, "%s: load segment not first command in %s\n",
63 progname, filename);
64 exit(1);
65 }
66 if (strncmp(sc_cmd->segname, "__PAGEZERO", sizeof (*sc_cmd->segname))) {
67 (void)fprintf(stderr, "%s: zero page not first segment in %s\n",
68 progname, filename);
69 exit(1);
70 }
71 /* change the permissions */
72 sc_cmd->maxprot = target_uint32(VM_PROT_ALL);
73 sc_cmd->initprot = target_uint32(VM_PROT_ALL);
74 }
75
76 #if defined(MH_MAGIC_64)
77 void pagezero_64(struct mach_header_64 *machhead)
78 {
79 struct segment_command_64 *sc_cmd;
80
81 if (target_uint32(machhead->filetype) != MH_EXECUTE) {
82 (void)fprintf(stderr, "%s: %s does not appear to be an executable file\n",
83 progname, filename);
84 exit(1);
85 }
86 if (machhead->ncmds == 0) {
87 (void)fprintf(stderr, "%s: %s does not contain any load commands\n",
88 progname, filename);
89 exit(1);
90 }
91 sc_cmd = (void *)&machhead[1];
92 if (target_uint32(sc_cmd->cmd) != LC_SEGMENT_64) {
93 (void)fprintf(stderr, "%s: load segment not first command in %s\n",
94 progname, filename);
95 exit(1);
96 }
97 if (strncmp(sc_cmd->segname, "__PAGEZERO", sizeof(*sc_cmd->segname))) {
98 (void)fprintf(stderr, "%s: zero page not first segment in %s\n",
99 progname, filename);
100 exit(1);
101 }
102 /* change the permissions */
103 sc_cmd->maxprot = target_uint32(VM_PROT_ALL);
104 sc_cmd->initprot = target_uint32(VM_PROT_ALL);
105 }
106 #endif
107
108 /*
109 /*
110 * Under Mach there is very little assumed about the memory map of object
111 * files. It is the job of the loader to create the initial memory map of an
112 * executable. In a Mach-O executable there will be numerous loader commands
113 * that the loader must process. Some of these will create the initial memory
114 * map used by the executable. Under Darwin the static object file linker,
115 * ld, automatically adds the __PAGEZERO segment to all executables. The
116 * default size of this segment is the page size of the target system and
117 * the initial and maximum permissions are set to allow no access. This is so
118 * that all programs fault on a NULL pointer dereference. Arguably this is
119 * incorrect and the maximum permissions shoould be rwx so that programs can
120 * change this default behavior. Then programs could be written that assume
121 * a null string at the null address, which was the convention on some
122 * systems. In our case we need to have 8K mapped at zero for the low memory
123 * globals and this program modifies the segment load command in the
124 * basiliskII executable so that it can be used for data.
125 */
126
127 int
128 main(int argc, const char *argv[])
129 {
130 int fd;
131 char *addr;
132 off_t file_size;
133 struct mach_header *machhead;
134 #if defined(MH_MAGIC_64)
135 struct mach_header_64 *machhead64;
136 #endif
137 struct fat_header *fathead;
138
139 if (argc != 2) {
140 (void)fprintf(stderr, "Usage: %s executable\n", progname);
141 exit(1);
142 }
143
144 filename = argv[1];
145
146 fd = open(filename, O_RDWR, 0);
147 if (fd == -1) {
148 (void)fprintf(stderr, "%s: could not open %s: %s\n",
149 progname, filename, strerror(errno));
150 exit(1);
151 }
152
153 file_size = lseek(fd, 0, SEEK_END);
154 if (file_size == -1) {
155 // for some mysterious reason, this sometimes fails...
156 file_size = 0x1000;
157 #if 0
158 (void)fprintf(stderr, "%s: could not get size of %s: %s\n",
159 progname, filename, strerror(errno));
160 exit(1);
161 #endif
162 }
163
164 /*
165 * Size does not really matter, it will be rounded-up to a multiple
166 * of the page size automatically.
167 */
168 addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
169 MAP_FILE | MAP_SHARED, fd, 0);
170 if (addr == NULL) {
171 (void)fprintf(stderr, "%s: could not mmap %s: %s\n",
172 progname, filename, strerror(errno));
173 exit(1);
174 }
175
176 /*
177 * Check to see if the Mach-O magic bytes are in the header.
178 */
179 machhead = (void *)addr;
180 #if defined(MH_MAGIC_64)
181 machhead64 = (void *)addr;
182 #endif
183 fathead = (void *)addr;
184
185 #if defined(MH_MAGIC_64)
186 do_swap = machhead->magic == MH_CIGAM || fathead->magic == FAT_CIGAM || machhead64->magic == MH_CIGAM_64;
187 #else
188 do_swap = machhead->magic == MH_CIGAM || fathead->magic == FAT_CIGAM;
189 #endif
190
191 if (target_uint32(machhead->magic) == MH_MAGIC) {
192 pagezero_32(machhead);
193 #if defined(MH_MAGIC_64)
194 } else if (target_uint32(machhead64->magic) == MH_MAGIC_64) {
195 pagezero_64(machhead64);
196 #endif
197 } else if (target_uint32(fathead->magic) == FAT_MAGIC) {
198 struct fat_arch *arch = (void *)&fathead[1];
199 int saved_swap = do_swap;
200 int i;
201 for (i = 0; i < target_uint32(fathead->nfat_arch); ++i, ++arch) {
202 machhead = (void *)(addr + target_uint32(arch->offset));
203 #if defined(MH_MAGIC_64)
204 machhead64 = (void *)(addr + target_uint32(arch->offset));
205 #endif
206 #if defined(MH_MAGIC_64)
207 do_swap = machhead->magic == MH_CIGAM || machhead64->magic == MH_CIGAM_64;
208 #else
209 do_swap = machhead->magic == MH_CIGAM;
210 #endif
211 if (target_uint32(machhead->magic) == MH_MAGIC) {
212 pagezero_32(machhead);
213 #if defined(MH_MAGIC_64)
214 } else if (target_uint32(machhead64->magic) == MH_MAGIC_64) {
215 pagezero_64(machhead64);
216 #endif
217 } else {
218 (void)fprintf(stderr, "%s: %s does not appear to be a Mach-O object file\n",
219 progname, filename);
220 exit(1);
221 }
222 do_swap = saved_swap;
223 }
224 } else {
225 (void)fprintf(stderr, "%s: %s does not appear to be a Mach-O object file\n",
226 progname, filename);
227 exit(1);
228 }
229
230 /*
231 * We do not make __PAGEZERO 8K in this program because then
232 * all of the offsets would be wrong in the object file after
233 * this segment. Instead we use the -pagezero_size option
234 * to link the executable.
235 */
236 if (msync(addr, file_size, MS_SYNC) == -1) {
237 (void)fprintf(stderr, "%s: could not sync %s: %s\n",
238 progname, filename, strerror(errno));
239 exit(1);
240 }
241
242 if (munmap(addr, file_size) == -1) {
243 (void)fprintf(stderr, "%s: could not unmap %s: %s\n",
244 progname, filename, strerror(errno));
245 exit(1);
246 }
247
248 (void)close(fd);
249
250 exit(0);
251 }