ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/Darwin/lowmem.c
Revision: 1.9
Committed: 2011-12-27T21:01:32Z (12 years, 11 months ago) by asvitkine
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.8: +9 -11 lines
Log Message:
get file size using stat, which seems to be more reliable

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 #include <sys/stat.h>
34
35 static const char progname[] = "lowmem";
36 static const char *filename;
37
38 static int do_swap = 0;
39
40 static uint32_t target_uint32(uint32_t value)
41 {
42 if (do_swap)
43 value = OSSwapInt32(value);
44 return value;
45 }
46
47 void pagezero_32(struct mach_header *machhead)
48 {
49 struct segment_command *sc_cmd;
50
51 if (target_uint32(machhead->filetype) != MH_EXECUTE) {
52 (void)fprintf(stderr, "%s: %s does not appear to be an executable file\n",
53 progname, filename);
54 exit(1);
55 }
56 if (machhead->ncmds == 0) {
57 (void)fprintf(stderr, "%s: %s does not contain any load commands\n",
58 progname, filename);
59 exit(1);
60 }
61 sc_cmd = (void *)&machhead[1];
62 if (target_uint32(sc_cmd->cmd) != LC_SEGMENT){
63 (void)fprintf(stderr, "%s: load segment not first command in %s\n",
64 progname, filename);
65 exit(1);
66 }
67 if (strncmp(sc_cmd->segname, "__PAGEZERO", sizeof (*sc_cmd->segname))) {
68 (void)fprintf(stderr, "%s: zero page not first segment in %s\n",
69 progname, filename);
70 exit(1);
71 }
72 /* change the permissions */
73 sc_cmd->maxprot = target_uint32(VM_PROT_ALL);
74 sc_cmd->initprot = target_uint32(VM_PROT_ALL);
75 }
76
77 #if defined(MH_MAGIC_64)
78 void pagezero_64(struct mach_header_64 *machhead)
79 {
80 struct segment_command_64 *sc_cmd;
81
82 if (target_uint32(machhead->filetype) != MH_EXECUTE) {
83 (void)fprintf(stderr, "%s: %s does not appear to be an executable file\n",
84 progname, filename);
85 exit(1);
86 }
87 if (machhead->ncmds == 0) {
88 (void)fprintf(stderr, "%s: %s does not contain any load commands\n",
89 progname, filename);
90 exit(1);
91 }
92 sc_cmd = (void *)&machhead[1];
93 if (target_uint32(sc_cmd->cmd) != LC_SEGMENT_64) {
94 (void)fprintf(stderr, "%s: load segment not first command in %s\n",
95 progname, filename);
96 exit(1);
97 }
98 if (strncmp(sc_cmd->segname, "__PAGEZERO", sizeof(*sc_cmd->segname))) {
99 (void)fprintf(stderr, "%s: zero page not first segment in %s\n",
100 progname, filename);
101 exit(1);
102 }
103 /* change the permissions */
104 sc_cmd->maxprot = target_uint32(VM_PROT_ALL);
105 sc_cmd->initprot = target_uint32(VM_PROT_ALL);
106 }
107 #endif
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 struct stat f;
139
140 if (argc != 2) {
141 (void)fprintf(stderr, "Usage: %s executable\n", progname);
142 exit(1);
143 }
144
145 filename = argv[1];
146
147 if (stat(filename, &f)) {
148 (void)fprintf(stderr, "%s: could not stat %s: %s\n",
149 progname, filename, strerror(errno));
150 exit(1);
151 }
152 file_size = f.st_size;
153
154 fd = open(filename, O_RDWR, 0);
155 if (fd == -1) {
156 (void)fprintf(stderr, "%s: could not open %s: %s\n",
157 progname, filename, strerror(errno));
158 exit(1);
159 }
160
161 /*
162 * Size does not really matter, it will be rounded-up to a multiple
163 * of the page size automatically.
164 */
165 addr = mmap(NULL, file_size, PROT_READ | PROT_WRITE,
166 MAP_FILE | MAP_SHARED, fd, 0);
167 if (addr == NULL || addr == MAP_FAILED) {
168 (void)fprintf(stderr, "%s: could not mmap %s: %s\n",
169 progname, filename, strerror(errno));
170 exit(1);
171 }
172
173 /*
174 * Check to see if the Mach-O magic bytes are in the header.
175 */
176 machhead = (void *)addr;
177 #if defined(MH_MAGIC_64)
178 machhead64 = (void *)addr;
179 #endif
180 fathead = (void *)addr;
181
182 #if defined(MH_MAGIC_64)
183 do_swap = machhead->magic == MH_CIGAM || fathead->magic == FAT_CIGAM || machhead64->magic == MH_CIGAM_64;
184 #else
185 do_swap = machhead->magic == MH_CIGAM || fathead->magic == FAT_CIGAM;
186 #endif
187
188 if (target_uint32(machhead->magic) == MH_MAGIC) {
189 pagezero_32(machhead);
190 #if defined(MH_MAGIC_64)
191 } else if (target_uint32(machhead64->magic) == MH_MAGIC_64) {
192 pagezero_64(machhead64);
193 #endif
194 } else if (target_uint32(fathead->magic) == FAT_MAGIC) {
195 struct fat_arch *arch = (void *)&fathead[1];
196 int saved_swap = do_swap;
197 int i;
198 for (i = 0; i < target_uint32(fathead->nfat_arch); ++i, ++arch) {
199 machhead = (void *)(addr + target_uint32(arch->offset));
200 #if defined(MH_MAGIC_64)
201 machhead64 = (void *)(addr + target_uint32(arch->offset));
202 #endif
203 #if defined(MH_MAGIC_64)
204 do_swap = machhead->magic == MH_CIGAM || machhead64->magic == MH_CIGAM_64;
205 #else
206 do_swap = machhead->magic == MH_CIGAM;
207 #endif
208 if (target_uint32(machhead->magic) == MH_MAGIC) {
209 pagezero_32(machhead);
210 #if defined(MH_MAGIC_64)
211 } else if (target_uint32(machhead64->magic) == MH_MAGIC_64) {
212 pagezero_64(machhead64);
213 #endif
214 } else {
215 (void)fprintf(stderr, "%s: %s does not appear to be a Mach-O object file\n",
216 progname, filename);
217 exit(1);
218 }
219 do_swap = saved_swap;
220 }
221 } else {
222 (void)fprintf(stderr, "%s: %s does not appear to be a Mach-O object file\n",
223 progname, filename);
224 exit(1);
225 }
226
227 /*
228 * We do not make __PAGEZERO 8K in this program because then
229 * all of the offsets would be wrong in the object file after
230 * this segment. Instead we use the -pagezero_size option
231 * to link the executable.
232 */
233 if (msync(addr, file_size, MS_SYNC) == -1) {
234 (void)fprintf(stderr, "%s: could not sync %s: %s\n",
235 progname, filename, strerror(errno));
236 exit(1);
237 }
238
239 if (munmap(addr, file_size) == -1) {
240 (void)fprintf(stderr, "%s: could not unmap %s: %s\n",
241 progname, filename, strerror(errno));
242 exit(1);
243 }
244
245 (void)close(fd);
246
247 exit(0);
248 }