1 | /*␊ |
2 | * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.␊ |
3 | *␊ |
4 | * @APPLE_LICENSE_HEADER_START@␊ |
5 | * ␊ |
6 | * The contents of this file constitute Original Code as defined in and␊ |
7 | * are subject to the Apple Public Source License Version 2.0 (the␊ |
8 | * "License"). You may not use this file except in compliance with the␊ |
9 | * License. Please obtain a copy of the License at␊ |
10 | * http://www.apple.com/publicsource and read it before using this file.␊ |
11 | * ␊ |
12 | * This Original Code and all software distributed under the License are␊ |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER␊ |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,␊ |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,␊ |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the␊ |
17 | * License for the specific language governing rights and limitations␊ |
18 | * under the License.␊ |
19 | * ␊ |
20 | * @APPLE_LICENSE_HEADER_END@␊ |
21 | *␊ |
22 | * load.c - Functions for decoding a Mach-o Kernel.␊ |
23 | *␊ |
24 | * Copyright (c) 1998-2003 Apple Computer, Inc.␊ |
25 | *␊ |
26 | */␊ |
27 | ␊ |
28 | #include <mach-o/fat.h>␊ |
29 | #include <mach-o/loader.h>␊ |
30 | #include <mach/machine/thread_status.h>␊ |
31 | ␊ |
32 | #include <sl.h>␊ |
33 | #include "boot.h"␊ |
34 | #include "kernel_patcher_internal.h"␊ |
35 | ␊ |
36 | #if DEBUG␊ |
37 | ␉#define DBG(x...)␉printf(x)␊ |
38 | #else␊ |
39 | ␉#define DBG(x...)␉msglog(x)␊ |
40 | #endif␊ |
41 | ␊ |
42 | // Private functions.␊ |
43 | static long DecodeSegment(long cmdBase, unsigned int *load_addr, unsigned int *load_size, void *binary, u_int32_t uncompressed_size);␊ |
44 | static long DecodeUnixThread(long cmdBase, unsigned int *entry);␊ |
45 | static long DecodeSymbolTable(long cmdBase);␊ |
46 | ␊ |
47 | ␊ |
48 | static unsigned long gBinaryAddress;␊ |
49 | bool gHaveKernelCache;␉␉␉/* XXX aserebln: uninitialized? and only set to true, never to false */␊ |
50 | cpu_type_t archCpuType = CPU_TYPE_I386;␊ |
51 | ␊ |
52 | bool incompatibleModuleLoaded = false;␊ |
53 | ␊ |
54 | //==============================================================================␊ |
55 | // Public function.␊ |
56 | ␊ |
57 | long ThinFatFile(void **binary, unsigned long *length)␊ |
58 | {␊ |
59 | ␉unsigned long nfat, swapped, size = 0;␊ |
60 | ␉struct fat_header *fhp = (struct fat_header *)*binary;␊ |
61 | ␉struct fat_arch *fap = (struct fat_arch *)((unsigned long)*binary + sizeof(struct fat_header));␊ |
62 | ␉cpu_type_t fapcputype;␊ |
63 | ␉uint32_t fapoffset;␊ |
64 | ␉uint32_t fapsize;␊ |
65 | ␊ |
66 | ␉if (fhp->magic == FAT_MAGIC)/* 0xcafebabe */␊ |
67 | ␉{␊ |
68 | ␉␉nfat = fhp->nfat_arch;␊ |
69 | ␉␉swapped = 0;␊ |
70 | ␉}␊ |
71 | ␉else if (fhp->magic == FAT_CIGAM)/* 0xbebafeca */␊ |
72 | ␉{␊ |
73 | ␉␉nfat = OSSwapInt32(fhp->nfat_arch);␊ |
74 | ␉␉swapped = 1;␊ |
75 | ␉}␊ |
76 | ␉else␊ |
77 | ␉{␊ |
78 | ␉␉return -1;␊ |
79 | ␉}␊ |
80 | ␊ |
81 | ␉for (; nfat > 0; nfat--, fap++)␊ |
82 | ␉{␊ |
83 | ␉␉if (swapped)␊ |
84 | ␉␉{␊ |
85 | ␉␉␉fapcputype = OSSwapInt32(fap->cputype);␊ |
86 | ␉␉␉fapoffset = OSSwapInt32(fap->offset);␊ |
87 | ␉␉␉fapsize = OSSwapInt32(fap->size);␊ |
88 | ␉␉}␊ |
89 | ␉␉else␊ |
90 | ␉␉{␊ |
91 | ␉␉␉fapcputype = fap->cputype;␊ |
92 | ␉␉␉fapoffset = fap->offset;␊ |
93 | ␉␉␉fapsize = fap->size;␊ |
94 | ␉␉}␊ |
95 | ␊ |
96 | ␉␉if (fapcputype == archCpuType)␊ |
97 | ␉␉{␊ |
98 | ␉␉␉*binary = (void *) ((unsigned long)*binary + fapoffset);␊ |
99 | ␉␉␉size = fapsize;␊ |
100 | ␉␉␉break;␊ |
101 | ␉␉}␊ |
102 | ␉}␊ |
103 | ␊ |
104 | ␉if (length != 0)␊ |
105 | ␉{␊ |
106 | ␉␉*length = size;␊ |
107 | ␉}␊ |
108 | ␊ |
109 | ␉return 0;␊ |
110 | }␊ |
111 | ␊ |
112 | ␊ |
113 | //==============================================================================␊ |
114 | ␊ |
115 | long DecodeMachO(void *binary, u_int32_t uncompressed_size, entry_t *rentry, char **raddr, int *rsize)␊ |
116 | {␊ |
117 | ␉struct mach_header *mH;␊ |
118 | ␉unsigned long ncmds, cmdBase, cmd, cmdsize, cmdstart;␊ |
119 | ␉// long headerBase, headerAddr, headerSize;␊ |
120 | ␉unsigned int vmaddr = ~0;␊ |
121 | ␉unsigned int vmend = 0;␊ |
122 | ␉unsigned long cnt;␊ |
123 | ␉long ret = -1;␊ |
124 | ␉unsigned int entry = 0;␊ |
125 | ␊ |
126 | ␉gBinaryAddress = (unsigned long)binary;␊ |
127 | ␊ |
128 | ␉mH = (struct mach_header *)(gBinaryAddress);␊ |
129 | ␊ |
130 | /*#if DEBUG␊ |
131 | ␉DBG("magic: 0x%x\n", (unsigned)mH->magic);␊ |
132 | ␉DBG("cputype: 0x%x\n", (unsigned)mH->cputype);␊ |
133 | ␉DBG("cpusubtype: 0x%x\n", (unsigned)mH->cpusubtype);␊ |
134 | ␉DBG("filetype: 0x%x\n", (unsigned)mH->filetype);␊ |
135 | ␉DBG("ncmds: 0x%x\n", (unsigned)mH->ncmds);␊ |
136 | ␉DBG("sizeofcmds: 0x%x\n", (unsigned)mH->sizeofcmds);␊ |
137 | ␉DBG("flags: 0x%x\n", (unsigned)mH->flags);␊ |
138 | ␉DBG("archCpuType: 0x%x\n", archCpuType);␊ |
139 | ␉//getchar();␊ |
140 | #endif*/␊ |
141 | ␊ |
142 | ␉switch (archCpuType)␊ |
143 | ␉{␊ |
144 | ␉␉case CPU_TYPE_I386:␊ |
145 | ␊ |
146 | ␉␉␉if (mH->magic != MH_MAGIC)␊ |
147 | ␉␉␉{␊ |
148 | ␉␉␉␉error("Mach-O (i386) file has bad magic number\n");␊ |
149 | ␉␉␉␉return -1;␊ |
150 | ␉␉␉}␊ |
151 | ␊ |
152 | ␉␉␉cmdstart = (unsigned long)gBinaryAddress + sizeof(struct mach_header);␊ |
153 | ␉␉␉break;␊ |
154 | ␊ |
155 | ␉␉case CPU_TYPE_X86_64:␊ |
156 | /*␊ |
157 | ␉␉␉if (mH->magic != MH_MAGIC_64 && mH->magic == MH_MAGIC)␊ |
158 | ␉␉␉{␊ |
159 | ␉␉␉␉return -1;␊ |
160 | ␉␉␉}␊ |
161 | */␊ |
162 | ␉␉␉if (mH->magic != MH_MAGIC_64)␊ |
163 | ␉␉␉{␊ |
164 | ␉␉␉␉error("Mach-O file (x86_64) has bad magic number\n");␊ |
165 | ␉␉␉␉return -1;␊ |
166 | ␉␉␉}␊ |
167 | ␊ |
168 | ␉␉␉cmdstart = (unsigned long)gBinaryAddress + sizeof(struct mach_header_64);␊ |
169 | ␉␉␉break;␊ |
170 | ␊ |
171 | ␉␉default:␊ |
172 | ␊ |
173 | ␉␉␉error("Unknown CPU type\n");␊ |
174 | ␉␉␉return -1;␊ |
175 | ␉}␊ |
176 | ␊ |
177 | ␉if (skipKernelPatcher)␊ |
178 | ␉{␊ |
179 | ␉␉verbose("\nInternal kernel patcher skipped as requested\n\n");␊ |
180 | ␉}␊ |
181 | ␉else␊ |
182 | ␉{␊ |
183 | ␊ |
184 | ␉␉if( is_module_loaded("KernelPatcher.dylib"))␊ |
185 | ␉␉{␊ |
186 | ␉␉␉verbose("\nKernelPatcher.dylib prevent the internal kernel/kexts Patcher, skipped!\n\n");␊ |
187 | ␉␉␉incompatibleModuleLoaded = true;␊ |
188 | ␉␉}␊ |
189 | ␉␉else␊ |
190 | ␉␉{␊ |
191 | ␉␉␉// call the kernel patcher main function␊ |
192 | ␉␉␉/*␊ |
193 | ␉␉␉␉if (( KernelBooter_kexts ||␊ |
194 | ␉␉␉␉␉KernelPm ||␊ |
195 | ␉␉␉␉␉KernelLapicError ||␊ |
196 | ␉␉␉␉␉KernelLapicVersion ||␊ |
197 | ␉␉␉␉␉KernelHaswell ||␊ |
198 | ␉␉␉␉␉KernelcpuFamily ||␊ |
199 | ␉␉␉␉␉KernelSSE3 ) &&␊ |
200 | ␉␉␉␉␉(MacOSVerCurrent >= MacOSVer2Int("10.6"))␊ |
201 | ␉␉␉␉␉)␊ |
202 | ␉␉␉␉{␊ |
203 | ␉␉␉␉␉patch_kernel_internal(binary, uncompressed_size);␊ |
204 | ␉␉␉␉}␊ |
205 | ␉␉␉*/␊ |
206 | ␊ |
207 | ␉␉␉// since users can done their own patches, just ensure for the minimum OS version␊ |
208 | ␉␉␉// w/o dependencies from other patches (e.g KernelBooter_kexts)␊ |
209 | ␉␉␉if (MacOSVerCurrent >= MacOSVer2Int("10.6"))␊ |
210 | ␉␉␉{␊ |
211 | ␉␉␉␉patch_kernel_internal(binary, uncompressed_size);␊ |
212 | ␉␉␉}␊ |
213 | ␉␉}␊ |
214 | ␉}␊ |
215 | ␊ |
216 | ␉cmdBase = cmdstart;␊ |
217 | ␉ncmds = mH->ncmds;␊ |
218 | ␊ |
219 | ␉for (cnt = 0; cnt < ncmds; cnt++)␊ |
220 | ␉{␊ |
221 | ␉␉cmd = ((long *)cmdBase)[0];␊ |
222 | ␉␉cmdsize = ((long *)cmdBase)[1];␊ |
223 | ␉␉unsigned int load_addr;␊ |
224 | ␉␉unsigned int load_size;␊ |
225 | ␊ |
226 | ␉␉switch (cmd)␊ |
227 | ␉␉{␊ |
228 | ␉␉␉case LC_SEGMENT:␊ |
229 | ␉␉␉case LC_SEGMENT_64:␊ |
230 | ␉␉␉ret = DecodeSegment(cmdBase, &load_addr, &load_size, binary, uncompressed_size);␊ |
231 | ␊ |
232 | ␉␉␉if (ret == 0 && load_size != 0 && load_addr >= KERNEL_ADDR)␊ |
233 | ␉␉␉{␊ |
234 | ␉␉␉␉vmaddr = MIN(vmaddr, load_addr);␊ |
235 | ␉␉␉␉vmend = MAX(vmend, load_addr + load_size);␊ |
236 | ␉␉␉}␊ |
237 | ␉␉␉break;␊ |
238 | ␊ |
239 | ␉␉␉case LC_MAIN:␉/* Mountain Lion's replacement for LC_UNIXTHREAD */␊ |
240 | ␉␉␉case LC_UNIXTHREAD:␊ |
241 | ␉␉␉␉ret = DecodeUnixThread(cmdBase, &entry);␊ |
242 | ␉␉␉break;␊ |
243 | ␊ |
244 | ␉␉␉case LC_SYMTAB:␊ |
245 | ␉␉␉break;␊ |
246 | ␊ |
247 | ␉␉␉default:␊ |
248 | #if NOTDEF␊ |
249 | ␉␉␉printf("Ignoring cmd type %d.\n", (unsigned)cmd);␊ |
250 | #endif␊ |
251 | ␉␉␉break;␊ |
252 | ␉␉}␊ |
253 | ␊ |
254 | ␉if (ret != 0)␊ |
255 | ␉{␊ |
256 | ␉␉return -1;␊ |
257 | ␉}␊ |
258 | ␊ |
259 | ␉cmdBase += cmdsize;␊ |
260 | ␉}␊ |
261 | ␊ |
262 | ␉*rentry = (entry_t)( (unsigned long) entry & 0x3fffffff );␊ |
263 | ␉*rsize = vmend - vmaddr;␊ |
264 | ␉*raddr = (char *)vmaddr;␊ |
265 | ␊ |
266 | ␉cmdBase = cmdstart;␊ |
267 | ␊ |
268 | ␉for (cnt = 0; cnt < ncmds; cnt++)␊ |
269 | ␉{␊ |
270 | ␉␉cmd = ((long *)cmdBase)[0];␊ |
271 | ␉␉cmdsize = ((long *)cmdBase)[1];␊ |
272 | ␉␉␊ |
273 | ␉␉if (cmd == LC_SYMTAB)␊ |
274 | ␉␉{␊ |
275 | ␉␉␉if (DecodeSymbolTable(cmdBase) != 0)␊ |
276 | ␉␉␉{␊ |
277 | ␉␉␉␉return -1;␊ |
278 | ␉␉␉}␊ |
279 | ␉␉}␊ |
280 | ␊ |
281 | ␉␉cmdBase += cmdsize;␊ |
282 | ␉}␊ |
283 | ␊ |
284 | ␉return ret;␊ |
285 | }␊ |
286 | ␊ |
287 | ␊ |
288 | //==============================================================================␊ |
289 | // Private function.␊ |
290 | ␊ |
291 | static long DecodeSegment(long cmdBase, unsigned int *load_addr, unsigned int *load_size, void *binary, u_int32_t uncompressed_size)␊ |
292 | {␊ |
293 | ␉char *segname;␊ |
294 | ␉long vmsize, filesize;␊ |
295 | ␉unsigned long vmaddr, fileoff, fileaddr;␊ |
296 | ␊ |
297 | ␉if (((long *)cmdBase)[0] == LC_SEGMENT_64)␊ |
298 | ␉{␊ |
299 | ␉␉struct segment_command_64 *segCmd;␊ |
300 | ␉␉segCmd = (struct segment_command_64 *)cmdBase;␊ |
301 | ␉␉vmaddr = (segCmd->vmaddr & 0x3fffffff);␊ |
302 | ␉␉vmsize = segCmd->vmsize;␊ |
303 | ␉␉fileoff = segCmd->fileoff;␊ |
304 | ␉␉fileaddr = (gBinaryAddress + segCmd->fileoff);␊ |
305 | ␉␉filesize = segCmd->filesize;␊ |
306 | ␉␉segname = (char *)segCmd->segname;␊ |
307 | ␊ |
308 | #ifdef DEBUG␊ |
309 | ␉printf("segname: %s, vmaddr: %x, vmsize: %x, fileoff: %x, fileaddr: %x, filesize: %x, nsects: %d, flags: %x.\n",␊ |
310 | ␉␉segCmd->segname, (unsigned)vmaddr, (unsigned)vmsize, segCmd->fileoff, (unsigned)fileaddr, (unsigned)filesize,␊ |
311 | ␉␉(unsigned) segCmd->nsects, (unsigned)segCmd->flags);␊ |
312 | ␉getchar();␊ |
313 | #endif␊ |
314 | ␉}␊ |
315 | ␉else␊ |
316 | ␉{␊ |
317 | ␉␉struct segment_command *segCmd;␊ |
318 | ␊ |
319 | ␉␉segCmd = (struct segment_command *)cmdBase;␊ |
320 | ␊ |
321 | ␉␉vmaddr = (segCmd->vmaddr & 0x3fffffff);␊ |
322 | ␉␉vmsize = segCmd->vmsize;␊ |
323 | ␉␉fileoff = segCmd->fileoff;␊ |
324 | ␉␉fileaddr = (gBinaryAddress + segCmd->fileoff);␊ |
325 | ␉␉filesize = segCmd->filesize;␊ |
326 | ␉␉segname = (char *)segCmd->segname;␊ |
327 | ␊ |
328 | #ifdef DEBUG␊ |
329 | ␉printf("segname: %s, vmaddr: %x, vmsize: %x, fileoff: %x, fileaddr: %x, filesize: %x, nsects: %d, flags: %x.\n",␊ |
330 | ␉␉segCmd->segname, (unsigned)vmaddr, (unsigned)vmsize, segCmd->fileoff, (unsigned)fileaddr, (unsigned)filesize,␊ |
331 | ␉␉(unsigned) segCmd->nsects, (unsigned)segCmd->flags);␊ |
332 | ␉getchar();␊ |
333 | #endif␊ |
334 | ␉}␊ |
335 | ␊ |
336 | ␉// patching prelinked kexts␊ |
337 | ␉if (!strcmp(segname, "__PRELINK_TEXT") && !incompatibleModuleLoaded)␊ |
338 | ␉{␊ |
339 | ␉␉if (!skipKextsPatcher)␊ |
340 | ␉␉{␊ |
341 | ␉␉␉if (vmaddr && fileoff)␊ |
342 | ␉␉␉{␊ |
343 | ␉␉␉␉patch_prelinked_kexts(binary,␊ |
344 | ␉␉␉␉␉uncompressed_size,␊ |
345 | ␉␉␉␉␉vmaddr,␊ |
346 | ␉␉␉␉␉fileoff);␊ |
347 | ␉␉␉}␊ |
348 | ␉␉}␊ |
349 | ␉␉else␊ |
350 | ␉␉{␊ |
351 | ␉␉␉verbose("\nInternal kexts patcher skipped as requested\n\n");␊ |
352 | ␉␉}␊ |
353 | ␉}␊ |
354 | //===================================================␊ |
355 | ␊ |
356 | ␉if (vmsize == 0 || filesize == 0)␊ |
357 | ␉{␊ |
358 | ␉␉*load_addr = ~0;␊ |
359 | ␉␉*load_size = 0;␊ |
360 | ␉␉return 0;␊ |
361 | ␉}␊ |
362 | ␊ |
363 | ␉if (! ((vmaddr >= KERNEL_ADDR && (vmaddr + vmsize) <= (KERNEL_ADDR + KERNEL_LEN)) ||␊ |
364 | ␉␉ (vmaddr >= HIB_ADDR && (vmaddr + vmsize) <= (HIB_ADDR + HIB_LEN))))␊ |
365 | ␉{␊ |
366 | ␉␉stop("Kernel overflows available space");␊ |
367 | ␉}␊ |
368 | ␊ |
369 | ␉if (vmsize && ((strncmp(segname, "__PRELINK_INFO", sizeof("__PRELINK_INFO")) == 0) || (strncmp(segname, "__PRELINK", sizeof("__PRELINK")) == 0)))␊ |
370 | ␉{␊ |
371 | ␉␉gHaveKernelCache = true;␊ |
372 | ␉}␊ |
373 | ␊ |
374 | ␉// Copy from file load area.␊ |
375 | ␉if (vmsize>0 && filesize > 0)␊ |
376 | ␉{␊ |
377 | ␉␉bcopy((char *)fileaddr, (char *)vmaddr, vmsize > filesize ? filesize : vmsize);␊ |
378 | ␉}␊ |
379 | ␊ |
380 | ␉// Zero space at the end of the segment.␊ |
381 | ␉if (vmsize > filesize)␊ |
382 | ␉{␊ |
383 | ␉␉bzero((char *)(vmaddr + filesize), vmsize - filesize);␊ |
384 | ␉}␊ |
385 | ␊ |
386 | ␉*load_addr = vmaddr;␊ |
387 | ␉*load_size = vmsize;␊ |
388 | ␊ |
389 | ␉return 0;␊ |
390 | }␊ |
391 | ␊ |
392 | //==============================================================================␊ |
393 | ␊ |
394 | static long DecodeUnixThread(long cmdBase, unsigned int *entry)␊ |
395 | {␊ |
396 | ␉switch (archCpuType)␊ |
397 | ␉{␊ |
398 | ␉␉case CPU_TYPE_I386:␊ |
399 | ␉␉{␊ |
400 | ␉␉␉i386_thread_state_t *i386ThreadState;␊ |
401 | ␉␉␉i386ThreadState = (i386_thread_state_t *) (cmdBase + sizeof(struct thread_command) + 8);␊ |
402 | ␊ |
403 | ␉␉␉*entry = i386ThreadState->eip;␊ |
404 | ␉␉␉return 0;␊ |
405 | ␉␉}␊ |
406 | ␉␉␉␊ |
407 | ␉␉case CPU_TYPE_X86_64:␊ |
408 | ␉␉{␊ |
409 | ␉␉␉x86_thread_state64_t *x86_64ThreadState;␊ |
410 | ␉␉␉x86_64ThreadState = (x86_thread_state64_t *) (cmdBase + sizeof(struct thread_command) + 8);␊ |
411 | ␉␉␉*entry = x86_64ThreadState->rip;␊ |
412 | ␉␉␉return 0;␊ |
413 | ␉␉}␊ |
414 | ␉␉␉␊ |
415 | ␉␉default:␊ |
416 | ␉␉␉error("Unknown CPU type\n");␊ |
417 | ␉␉␉return -1;␊ |
418 | ␉}␊ |
419 | }␊ |
420 | ␊ |
421 | ␊ |
422 | //==============================================================================␊ |
423 | ␊ |
424 | static long DecodeSymbolTable(long cmdBase)␊ |
425 | {␊ |
426 | ␉long tmpAddr, symsSize, totalSize;␊ |
427 | ␉long gSymbolTableAddr;␊ |
428 | ␉long gSymbolTableSize;␊ |
429 | ␊ |
430 | ␉struct symtab_command *symTab, *symTableSave;␊ |
431 | ␊ |
432 | ␉symTab = (struct symtab_command *)cmdBase;␊ |
433 | ␊ |
434 | #if DEBUG␊ |
435 | ␉printf("symoff: %x, nsyms: %x, stroff: %x, strsize: %x\n",␊ |
436 | ␉␉symTab->symoff, symTab->nsyms, symTab->stroff, symTab->strsize);␊ |
437 | ␉getchar();␊ |
438 | #endif␊ |
439 | ␊ |
440 | ␉symsSize = symTab->stroff - symTab->symoff;␊ |
441 | ␉totalSize = symsSize + symTab->strsize;␊ |
442 | ␊ |
443 | ␉gSymbolTableSize = totalSize + sizeof(struct symtab_command);␊ |
444 | ␉gSymbolTableAddr = AllocateKernelMemory(gSymbolTableSize);␊ |
445 | ␉// Add the SymTab to the memory-map.␊ |
446 | ␉AllocateMemoryRange("Kernel-__SYMTAB", gSymbolTableAddr, gSymbolTableSize, -1);␊ |
447 | ␊ |
448 | ␉symTableSave = (struct symtab_command *)gSymbolTableAddr;␊ |
449 | ␉tmpAddr = gSymbolTableAddr + sizeof(struct symtab_command);␊ |
450 | ␊ |
451 | ␉symTableSave->symoff = tmpAddr;␊ |
452 | ␉symTableSave->nsyms = symTab->nsyms;␊ |
453 | ␉symTableSave->stroff = tmpAddr + symsSize;␊ |
454 | ␉symTableSave->strsize = symTab->strsize;␊ |
455 | ␉␊ |
456 | ␉bcopy((char *)(gBinaryAddress + symTab->symoff), (char *)tmpAddr, totalSize);␊ |
457 | ␊ |
458 | ␉return 0;␊ |
459 | }␊ |
460 | |