1/*
2 * Copyright (c) 2006-2008, 2012 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28#include <unistd.h>
29#include <string.h>
30
31#include <stdio.h>
32#include <fcntl.h>
33
34#include <Kernel/mach/vm_types.h>
35#include <Kernel/mach/vm_param.h>
36
37#include <mach-o/swap.h>
38
39#include <sys/types.h>
40#include <sys/stat.h>
41#include <sys/mman.h>
42
43#include "macho_util.h"
44
45static boolean_t macho_swap_32(u_char *file);
46static boolean_t macho_swap_64(u_char *file);
47
48static boolean_t macho_unswap_32(u_char *file);
49static boolean_t macho_unswap_64(u_char *file);
50
51/*******************************************************************************
52*
53*******************************************************************************/
54macho_seek_result macho_find_symbol(
55    const void          * file_start,
56    const void          * file_end,
57    const char          * name,
58          uint8_t       * nlist_type,
59    const void         ** symbol_address)
60{
61    macho_seek_result       result = macho_seek_result_not_found;
62    macho_seek_result       symtab_result = macho_seek_result_not_found;
63    uint8_t                 swap = 0;
64    char                    sixtyfourbit = 0;
65    struct symtab_command * symtab = NULL;
66    struct nlist          * syms_address;
67    struct nlist_64       * syms_address_64;
68    const void            * string_list;
69    char                  * symbol_name;
70    unsigned int            symtab_offset;
71    unsigned int            str_offset;
72    unsigned int            num_syms;
73    unsigned int            syms_bytes;
74    unsigned int            sym_index;
75
76    if (symbol_address) {
77         *symbol_address = 0;
78    }
79
80    symtab_result = macho_find_symtab(file_start, file_end, &symtab);
81    if (symtab_result != macho_seek_result_found) {
82        goto finish;
83    }
84
85    if (ISSWAPPEDMACHO(MAGIC32(file_start))) {
86        swap = 1;
87    }
88    if (ISMACHO64(MAGIC32(file_start))) {
89        sixtyfourbit = 1;
90    }
91
92    symtab_offset = CondSwapInt32(swap, symtab->symoff);
93    str_offset = CondSwapInt32(swap, symtab->stroff);
94    num_syms   = CondSwapInt32(swap, symtab->nsyms);
95
96    syms_address = (struct nlist *)(file_start + symtab_offset);
97    syms_address_64 = (struct nlist_64 *)(file_start + symtab_offset);
98
99    string_list = file_start + str_offset;
100    if (sixtyfourbit) {
101        syms_bytes = num_syms * sizeof(struct nlist_64);
102    } else {
103        syms_bytes = num_syms * sizeof(struct nlist);
104    }
105
106    if ((char *)syms_address + syms_bytes > (char *)file_end) {
107        result = macho_seek_result_error;
108        goto finish;
109    }
110
111    if ((char *)syms_address + syms_bytes > (char *)file_end ||
112        (char *)string_list > (char *)file_end) {
113        result = macho_seek_result_error;
114        goto finish;
115    }
116
117    for (sym_index = 0; sym_index < num_syms; sym_index++) {
118        struct nlist    * seekptr;
119        struct nlist_64 * seekptr_64;
120        uint32_t          string_index;
121        uint8_t           n_type;
122        uint8_t           n_sect;
123        uint64_t          n_value;
124
125        if (sixtyfourbit) {
126            seekptr_64   = &syms_address_64[sym_index];
127            string_index = CondSwapInt32(swap, seekptr_64->n_un.n_strx);
128            n_type       = seekptr_64->n_type;
129            n_sect       = seekptr_64->n_sect;
130            n_value      = CondSwapInt64(swap, seekptr_64->n_value);
131        } else {
132            seekptr      = &syms_address[sym_index];
133            string_index = CondSwapInt32(swap, seekptr->n_un.n_strx);
134            n_type       = seekptr->n_type;
135            n_sect       = seekptr->n_sect;
136            n_value      = (uint64_t)CondSwapInt32(swap, seekptr->n_value);
137        }
138
139        if (string_index == 0 || n_type & N_STAB) {
140            continue;
141        }
142        symbol_name = (char *)(string_list + string_index);
143        if (symbol_name > (char *)file_end) {
144            result = macho_seek_result_error;
145            goto finish;
146        }
147
148        if (strcmp(name, symbol_name) == 0) {
149
150            if (nlist_type) {
151                *nlist_type = n_type;
152            }
153            switch (n_type & N_TYPE) {
154                case N_SECT:
155                    {
156                        void * v_sect_info = macho_find_section_numbered(
157                            file_start, file_end, n_sect);
158
159
160                        if (!v_sect_info) {
161                            break;  // out of the switch
162                        }
163
164                        if (symbol_address) {
165                            if (sixtyfourbit) {
166                                struct section_64 * sect_info_64 =
167                                    (struct section_64 *)v_sect_info;
168
169                                // this isn't right for 64bit? compare below
170                                size_t reloffset = (n_value -
171                                    CondSwapInt64(swap, sect_info_64->addr));
172
173                                *symbol_address = file_start;
174                                *symbol_address += CondSwapInt32(swap,
175                                    sect_info_64->offset);
176                                *symbol_address += reloffset;
177                            } else {
178                                struct section * sect_info =
179                                    (struct section *)v_sect_info;
180
181                                size_t reloffset = (n_value -
182                                    CondSwapInt32(swap, sect_info->addr));
183
184                                *symbol_address = file_start;
185                                *symbol_address += CondSwapInt32(swap,
186                                    sect_info->offset);
187                                *symbol_address += reloffset;
188                            }
189                        }
190                        result = macho_seek_result_found;
191                        goto finish;
192                    }
193                    break;
194
195                case N_UNDF:
196                    result = macho_seek_result_found_no_value;
197                    goto finish;
198                    break;
199
200                case N_ABS:
201                    result = macho_seek_result_found_no_value;
202                    goto finish;
203                    break;
204
205              /* We don't chase indirect symbols as they can be external.
206               */
207                case N_INDR:
208                    result = macho_seek_result_found_no_value;
209                    goto finish;
210                    break;
211
212                default:
213                    goto finish;
214                    break;
215            }
216        }
217    }
218
219finish:
220    return result;
221}
222
223/*******************************************************************************
224*
225*******************************************************************************/
226typedef struct {
227    struct symtab_command * symtab;
228} _symtab_scan;
229
230static macho_seek_result __macho_lc_is_symtab(
231    struct load_command * lc_cmd,
232    const void          * file_end,
233    uint8_t               swap,
234    void                * user_data);
235
236/******************************************************************************/
237
238macho_seek_result macho_find_symtab(
239    const void             * file_start,
240    const void             * file_end,
241    struct symtab_command ** symtab)
242{
243    macho_seek_result result = macho_seek_result_not_found;
244    _symtab_scan      sym_data;
245
246    bzero(&sym_data, sizeof(sym_data));
247
248    if (symtab) {
249        *symtab = NULL;
250    }
251
252    result = macho_scan_load_commands(file_start,
253        file_end, &__macho_lc_is_symtab, &sym_data);
254
255    if (result == macho_seek_result_found) {
256        if (symtab) {
257            *symtab = sym_data.symtab;
258        }
259    }
260
261    return result;
262}
263
264/******************************************************************************/
265
266static macho_seek_result __macho_lc_is_symtab(
267    struct load_command * lc_cmd,
268    const void          * file_end,
269    uint8_t               swap,
270    void                * user_data)
271{
272    macho_seek_result   result = macho_seek_result_not_found;
273    _symtab_scan      * sym_data = (_symtab_scan *)user_data;
274    uint32_t            cmd;
275
276    if ((void *)(lc_cmd + sizeof(struct load_command)) > file_end) {
277        result = macho_seek_result_error;
278        goto finish;
279    }
280
281    cmd = CondSwapInt32(swap, lc_cmd->cmd);
282
283    if (cmd == LC_SYMTAB) {
284        uint32_t cmd_size = CondSwapInt32(swap, lc_cmd->cmdsize);
285
286        if ((cmd_size != sizeof(struct symtab_command)) ||
287            ((void *)(lc_cmd + sizeof(struct symtab_command)) > file_end)) {
288            result = macho_seek_result_error;
289            goto finish;
290        }
291        sym_data->symtab = (struct symtab_command *)lc_cmd;
292        result = macho_seek_result_found;
293        goto finish;
294    }
295
296finish:
297    return result;
298}
299
300/*******************************************************************************
301 *
302 *******************************************************************************/
303typedef struct {
304    struct dysymtab_command * dysymtab;
305} _dysymtab_scan;
306
307static macho_seek_result __macho_lc_is_dysymtab(
308                                                struct load_command * lc_cmd,
309                                                const void          * file_end,
310                                                uint8_t               swap,
311                                                void                * user_data);
312
313/******************************************************************************/
314
315macho_seek_result macho_find_dysymtab(
316                                      const void             * file_start,
317                                      const void             * file_end,
318                                      struct dysymtab_command ** dysymtab)
319{
320    macho_seek_result result = macho_seek_result_not_found;
321    _dysymtab_scan      dysym_data;
322
323    bzero(&dysym_data, sizeof(dysym_data));
324
325    if (dysymtab) {
326        *dysymtab = NULL;
327    }
328
329    result = macho_scan_load_commands(file_start,
330                                      file_end, &__macho_lc_is_dysymtab, &dysym_data);
331
332    if (result == macho_seek_result_found) {
333        if (dysymtab) {
334            *dysymtab = dysym_data.dysymtab;
335        }
336    }
337
338    return result;
339}
340
341/******************************************************************************/
342
343static macho_seek_result __macho_lc_is_dysymtab(
344                                                struct load_command * lc_cmd,
345                                                const void          * file_end,
346                                                uint8_t               swap,
347                                                void                * user_data)
348{
349    macho_seek_result   result = macho_seek_result_not_found;
350    _dysymtab_scan      * dysym_data = (_dysymtab_scan *)user_data;
351    uint32_t            cmd;
352
353    if ((void *)(lc_cmd + sizeof(struct load_command)) > file_end) {
354        result = macho_seek_result_error;
355        goto finish;
356    }
357
358    cmd = CondSwapInt32(swap, lc_cmd->cmd);
359
360    if (cmd == LC_DYSYMTAB) {
361        uint32_t cmd_size = CondSwapInt32(swap, lc_cmd->cmdsize);
362
363        if ((cmd_size != sizeof(struct dysymtab_command)) ||
364            ((void *)(lc_cmd + sizeof(struct dysymtab_command)) > file_end)) {
365            result = macho_seek_result_error;
366            goto finish;
367        }
368        dysym_data->dysymtab = (struct dysymtab_command *)lc_cmd;
369        result = macho_seek_result_found;
370        goto finish;
371    }
372
373finish:
374    return result;
375}
376
377/*******************************************************************************
378* macho_find_uuid()
379*
380* Returns a pointer to the UUID bytes.
381*******************************************************************************/
382struct _uuid_scan {
383    unsigned int   uuid_size;
384    char         * uuid;
385};
386
387macho_seek_result __uuid_callback(
388    struct load_command * load_command,
389    const void * file_end,
390    uint8_t swap __unused,
391    void * user_data)
392{
393    struct _uuid_scan * uuid_stuff = (struct _uuid_scan *)user_data;
394    if (load_command->cmd == LC_UUID) {
395        struct uuid_command * uuid_command = (struct uuid_command *)load_command;
396        if (((void *)load_command + load_command->cmdsize) > file_end) {
397            return macho_seek_result_error;
398        }
399        uuid_stuff->uuid_size = sizeof(uuid_command->uuid);
400        uuid_stuff->uuid = (char *)uuid_command->uuid;
401        return macho_seek_result_found;
402    }
403    return macho_seek_result_not_found;
404}
405
406macho_seek_result macho_find_uuid(
407    const void * file_start,
408    const void * file_end,
409    char       **uuid)
410{
411    macho_seek_result  result;
412    struct _uuid_scan seek_uuid;
413
414    result = macho_scan_load_commands(
415        file_start, file_end,
416        __uuid_callback, (const void **)&seek_uuid);
417    if (result == macho_seek_result_found && uuid) {
418        *uuid = seek_uuid.uuid;
419    }
420
421    return result;
422}
423
424/*******************************************************************************
425* macho_find_section_numbered()
426*
427* Returns a pointer to a section in a mach-o file based on its global index
428* (which starts at 1, not zero!). The section number is typically garnered from
429* some other mach-o struct, such as a symtab entry. Returns NULL if the numbered
430* section can't be found.
431*******************************************************************************/
432typedef struct {
433    char       sixtyfourbit;
434    uint8_t    sect_num;
435    uint8_t    sect_counter;
436    void     * sect_info;  // struct section or section_64 depending
437} _sect_scan;
438
439static macho_seek_result __macho_sect_in_lc(
440    struct load_command * lc_cmd,
441    const void          * file_end,
442    uint8_t               swap,
443    void                * user_data);
444
445/******************************************************************************/
446
447void * macho_find_section_numbered(
448    const void * file_start,
449    const void * file_end,
450    uint8_t      sect_num)
451{
452    _sect_scan sect_data;
453
454    bzero(&sect_data, sizeof(sect_data));
455
456    sect_data.sect_num = sect_num;
457
458    if (ISMACHO64(MAGIC32(file_start))) {
459        sect_data.sixtyfourbit = 1;
460    }
461
462    if (macho_seek_result_found == macho_scan_load_commands(
463        file_start, file_end, &__macho_sect_in_lc, &sect_data)) {
464
465        return sect_data.sect_info;
466    }
467
468    return NULL;
469}
470
471/******************************************************************************/
472
473static macho_seek_result __macho_sect_in_lc(
474    struct load_command * lc_cmd,
475    const void          * file_end,
476    uint8_t               swap,
477    void                * user_data)
478{
479    macho_seek_result   result    = macho_seek_result_not_found;
480    _sect_scan        * sect_data = (_sect_scan *)user_data;
481    uint32_t            cmd;
482
483    if (sect_data->sect_counter > sect_data->sect_num) {
484        result = macho_seek_result_stop;
485        goto finish;
486    }
487
488    if ((void *)(lc_cmd + sizeof(struct load_command)) > file_end) {
489        result = macho_seek_result_error;
490        goto finish;
491    }
492
493    cmd = CondSwapInt32(swap, lc_cmd->cmd);
494
495    if (cmd == LC_SEGMENT_64) {
496        struct segment_command_64 * seg_cmd =
497            (struct segment_command_64 *)lc_cmd;
498        uint32_t                    cmd_size;
499        uint32_t                    num_sects;
500        uint32_t                    sects_size;
501        struct section_64         * seek_sect;
502        uint32_t                    sect_index;
503
504        cmd_size = CondSwapInt32(swap, seg_cmd->cmdsize);
505        num_sects = CondSwapInt32(swap, seg_cmd->nsects);
506        sects_size = num_sects * sizeof(*seek_sect);
507
508        if (cmd_size != (sizeof(*seg_cmd) + sects_size)) {
509            result = macho_seek_result_error;
510            goto finish;
511        }
512
513        if (((void *)lc_cmd + cmd_size) > file_end) {
514            result = macho_seek_result_error;
515            goto finish;
516        }
517
518        for (sect_index = 0; sect_index < num_sects; sect_index++) {
519
520            seek_sect = (struct section_64 *)((void *)lc_cmd +
521                sizeof(*seg_cmd) +
522                (sect_index * sizeof(*seek_sect)));
523
524            sect_data->sect_counter++;
525
526            if (sect_data->sect_counter == sect_data->sect_num) {
527                sect_data->sect_info = seek_sect;
528                result = macho_seek_result_found;
529                goto finish;
530            }
531        }
532    } else if (cmd == LC_SEGMENT) {
533        struct segment_command    * seg_cmd = (struct segment_command *)lc_cmd;
534        uint32_t                    cmd_size;
535        uint32_t                    num_sects;
536        uint32_t                    sects_size;
537        struct section            * seek_sect;
538        uint32_t                    sect_index;
539
540        cmd_size = CondSwapInt32(swap, seg_cmd->cmdsize);
541        num_sects = CondSwapInt32(swap, seg_cmd->nsects);
542        sects_size = num_sects * sizeof(*seek_sect);
543
544        if (cmd_size != (sizeof(*seg_cmd) + sects_size)) {
545            result = macho_seek_result_error;
546            goto finish;
547        }
548
549        if (((void *)lc_cmd + cmd_size) > file_end) {
550            result = macho_seek_result_error;
551            goto finish;
552        }
553
554        for (sect_index = 0; sect_index < num_sects; sect_index++) {
555
556            seek_sect = (struct section *)((void *)lc_cmd +
557                sizeof(*seg_cmd) +
558                (sect_index * sizeof(*seek_sect)));
559
560            sect_data->sect_counter++;
561
562            if (sect_data->sect_counter == sect_data->sect_num) {
563                sect_data->sect_info = seek_sect;
564                result = macho_seek_result_found;
565                goto finish;
566            }
567        }
568    }
569
570finish:
571    return result;
572}
573
574/*******************************************************************************
575 * macho_find_source_version()
576 *
577 * Returns the contents of the version field of an LC_SOURCE_VERSION load
578 * command.
579 *******************************************************************************/
580static macho_seek_result __source_version_callback(
581                                  struct load_command * load_command,
582                                  const void * file_end,
583                                  uint8_t swap __unused,
584                                  void * user_data)
585{
586    uint64_t    *versionPtr = (uint64_t *) user_data;
587
588    if (load_command->cmd == LC_SOURCE_VERSION) {
589        struct source_version_command * sv_command = (struct source_version_command *)load_command;
590        if (((void *)load_command + load_command->cmdsize) > file_end) {
591            return macho_seek_result_error;
592        }
593        *versionPtr = sv_command->version;
594        return macho_seek_result_found;
595    }
596    return macho_seek_result_not_found;
597}
598
599macho_seek_result macho_find_source_version(
600                                  const void * file_start,
601                                  const void * file_end,
602                                  uint64_t   * version)
603{
604    macho_seek_result  result;
605
606    *version = 0;
607    result = macho_scan_load_commands(file_start, file_end, __source_version_callback, version);
608    return result;
609}
610
611/*******************************************************************************
612*
613*******************************************************************************/
614#define CMDSIZE_MULT_32  (4)
615#define CMDSIZE_MULT_64  (8)
616
617macho_seek_result macho_scan_load_commands(
618    const void        * file_start,
619    const void        * file_end,
620    macho_lc_callback   lc_callback,
621    void              * user_data)
622{
623    macho_seek_result       result = macho_seek_result_not_found;
624    struct mach_header    * mach_header = (struct mach_header *)file_start;
625
626    uint8_t                 swap = 0;
627    uint32_t                cmdsize_mult = CMDSIZE_MULT_32;
628
629    uint32_t                num_cmds;
630    uint32_t                sizeofcmds;
631    char                  * cmds_end;
632
633    uint32_t                cmd_index;
634    struct load_command  * load_commands;
635    struct load_command  * seek_lc;
636
637    switch (MAGIC32(file_start)) {
638      case MH_MAGIC_64:
639        cmdsize_mult = CMDSIZE_MULT_64;
640        break;
641      case MH_CIGAM_64:
642        cmdsize_mult = CMDSIZE_MULT_64;
643        swap = 1;
644        break;
645      case MH_CIGAM:
646        swap = 1;
647        break;
648      case MH_MAGIC:
649        break;
650      default:
651        result = macho_seek_result_error;
652        goto finish;
653        break;
654    }
655
656    if (cmdsize_mult == CMDSIZE_MULT_64) {
657        load_commands = (struct load_command *)
658            (file_start + sizeof(struct mach_header_64));
659    } else {
660        load_commands = (struct load_command *)
661            (file_start + sizeof(struct mach_header));
662    }
663
664    if (file_start >= file_end || (((void *)load_commands) > file_end)) {
665        result = macho_seek_result_error;
666        goto finish;
667    }
668
669    num_cmds   = CondSwapInt32(swap, mach_header->ncmds);
670    sizeofcmds = CondSwapInt32(swap, mach_header->sizeofcmds);
671    cmds_end = (char *)load_commands + sizeofcmds;
672
673    if (cmds_end > (char *)file_end) {
674        result = macho_seek_result_error;
675        goto finish;
676    }
677
678    seek_lc = load_commands;
679
680    for (cmd_index = 0; cmd_index < num_cmds; cmd_index++) {
681        uint32_t cmd_size;
682        char * lc_end;
683
684        cmd_size = CondSwapInt32(swap, seek_lc->cmdsize);
685        lc_end = (char *)seek_lc + cmd_size;
686
687        if ((cmd_size % cmdsize_mult != 0) || (lc_end > cmds_end)) {
688            result = macho_seek_result_error;
689            goto finish;
690        }
691
692        result = lc_callback(seek_lc, file_end, swap, user_data);
693
694        switch (result) {
695          case macho_seek_result_not_found:
696            /* Not found, keep scanning. */
697            break;
698
699          case macho_seek_result_stop:
700            /* Definitely found that it isn't there. */
701            result = macho_seek_result_not_found;
702            goto finish;
703            break;
704
705          case macho_seek_result_found:
706            /* Found it! */
707            goto finish;
708            break;
709
710          default:
711            /* Error, fall through default case. */
712            result = macho_seek_result_error;
713            goto finish;
714            break;
715        }
716
717        seek_lc = (struct load_command *)((char *)seek_lc + cmd_size);
718    }
719
720finish:
721    return result;
722}
723
724/*******************************************************************************
725*******************************************************************************/
726boolean_t macho_swap(
727    u_char    * file)
728{
729    boolean_t result = FALSE;
730    struct mach_header *hdr = (struct mach_header *) file;
731
732    if (hdr->magic == MH_CIGAM) {
733        result = macho_swap_32(file);
734    } else if (hdr->magic == MH_CIGAM_64) {
735        result = macho_swap_64(file);
736    }
737
738    return result;
739}
740
741/*******************************************************************************
742*******************************************************************************/
743static boolean_t macho_swap_32(
744    u_char    * file)
745{
746    boolean_t result = FALSE;
747    struct mach_header *hdr = (struct mach_header *) file;
748    struct load_command *lc = (struct load_command *) &hdr[1];
749    struct segment_command *seg = NULL;
750    u_long offset = 0;
751    u_int cmd = 0;
752    u_int cmdsize = 0;
753    u_int i = 0;
754
755    if (!hdr || hdr->magic != MH_CIGAM) goto finish;
756
757    swap_mach_header(hdr, NXHostByteOrder());
758
759    offset = sizeof(*hdr);
760    for (i = 0; i < hdr->ncmds; ++i) {
761        lc = (struct load_command *) (file + offset);
762
763        cmd = OSSwapInt32(lc->cmd);
764        cmdsize = OSSwapInt32(lc->cmdsize);
765        offset += cmdsize;
766
767        if (cmd == LC_SEGMENT) {
768            seg = (struct segment_command *) lc;
769            swap_segment_command(seg, NXHostByteOrder());
770        } else {
771            swap_load_command(lc, NXHostByteOrder());
772        }
773    }
774
775    result = TRUE;
776finish:
777    return result;
778}
779
780/*******************************************************************************
781*******************************************************************************/
782boolean_t macho_swap_64(
783    u_char    * file)
784{
785    boolean_t result = FALSE;
786    struct mach_header_64 *hdr = (struct mach_header_64 *) file;
787    struct load_command *lc = (struct load_command *) &hdr[1];
788    struct segment_command_64 *seg = NULL;
789    u_long offset = 0;
790    u_int cmd = 0;
791    u_int cmdsize = 0;
792    u_int i = 0;
793
794    if (!hdr || hdr->magic != MH_CIGAM_64) goto finish;
795
796    swap_mach_header_64(hdr, NXHostByteOrder());
797
798    offset = sizeof(*hdr);
799    for (i = 0; i < hdr->ncmds; ++i) {
800        lc = (struct load_command *) (file + offset);
801
802        cmd = OSSwapInt32(lc->cmd);
803        cmdsize = OSSwapInt32(lc->cmdsize);
804        offset += cmdsize;
805
806        if (cmd == LC_SEGMENT_64) {
807            seg = (struct segment_command_64 *) lc;
808            swap_segment_command_64(seg, NXHostByteOrder());
809        } else {
810            swap_load_command(lc, NXHostByteOrder());
811        }
812    }
813
814    result = TRUE;
815finish:
816    return result;
817}
818
819/*******************************************************************************
820*******************************************************************************/
821boolean_t macho_unswap(
822    u_char    * file)
823{
824    boolean_t result = FALSE;
825    struct mach_header *hdr = (struct mach_header *) file;
826
827    if (hdr->magic == MH_MAGIC) {
828        result = macho_unswap_32(file);
829    } else if (hdr->magic == MH_MAGIC_64) {
830        result = macho_unswap_64(file);
831    }
832
833    return result;
834}
835
836/*******************************************************************************
837*******************************************************************************/
838boolean_t macho_unswap_32(
839    u_char    * file)
840{
841    boolean_t result = FALSE;
842    enum NXByteOrder order = 0;
843    struct mach_header *hdr = (struct mach_header *) file;
844    struct load_command *lc = (struct load_command *) &hdr[1];
845    struct segment_command *seg = NULL;
846    u_long offset = 0;
847    u_int i = 0;
848
849    if (NXHostByteOrder() == NX_LittleEndian) {
850        order = NX_BigEndian;
851    } else {
852        order = NX_LittleEndian;
853    }
854
855    if (!hdr || hdr->magic != MH_MAGIC) goto finish;
856
857    offset = sizeof(*hdr);
858    for (i = 0; i < hdr->ncmds; ++i) {
859        lc = (struct load_command *) (file + offset);
860        offset += lc->cmdsize;
861
862        if (lc->cmd == LC_SEGMENT) {
863            seg = (struct segment_command *) lc;
864            swap_segment_command(seg, order);
865        } else {
866            swap_load_command(lc, order);
867        }
868
869    }
870
871    swap_mach_header(hdr, order);
872
873    result = TRUE;
874finish:
875    return result;
876}
877
878/*******************************************************************************
879*******************************************************************************/
880boolean_t macho_unswap_64(
881    u_char    * file)
882{
883    boolean_t result = FALSE;
884    enum NXByteOrder order = 0;
885    struct mach_header_64 *hdr = (struct mach_header_64 *) file;
886    struct load_command *lc = (struct load_command *) &hdr[1];
887    struct segment_command_64 *seg = NULL;
888    u_long offset = 0;
889    u_int i = 0;
890
891    if (NXHostByteOrder() == NX_LittleEndian) {
892        order = NX_BigEndian;
893    } else {
894        order = NX_LittleEndian;
895    }
896
897    if (!hdr || hdr->magic != MH_MAGIC_64) goto finish;
898
899    offset = sizeof(*hdr);
900    for (i = 0; i < hdr->ncmds; ++i) {
901        lc = (struct load_command *) (file + offset);
902        offset += lc->cmdsize;
903
904        if (lc->cmd == LC_SEGMENT_64) {
905            seg = (struct segment_command_64 *) lc;
906            swap_segment_command_64(seg, order);
907        } else {
908            swap_load_command(lc, order);
909        }
910    }
911
912    swap_mach_header_64(hdr, order);
913
914    result = TRUE;
915finish:
916    return result;
917}
918
919/*******************************************************************************
920*******************************************************************************/
921struct segment_command * macho_get_segment_by_name(
922    struct mach_header    * mach_header,
923    const char            * segname)
924{
925    struct segment_command *segment = NULL;
926    struct load_command *lc = NULL;
927    u_char *base = (u_char *) mach_header;
928    size_t offset = sizeof(*mach_header);
929    u_int i = 0;
930
931    if (mach_header->magic != MH_MAGIC) goto finish;
932
933    for (i = 0; i < mach_header->ncmds; ++i) {
934        lc = (struct load_command *) (base + offset);
935
936        if (lc->cmd == LC_SEGMENT) {
937            segment = (struct segment_command *) lc;
938            if (!strncmp(segment->segname, segname, sizeof(segment->segname))) {
939                break;
940            }
941            segment = NULL;
942        }
943
944        offset += lc->cmdsize;
945    }
946
947finish:
948    return segment;
949}
950
951/*******************************************************************************
952*******************************************************************************/
953struct segment_command_64 * macho_get_segment_by_name_64(
954    struct mach_header_64      * mach_header,
955    const char                 * segname)
956{
957    struct segment_command_64 *segment = NULL;
958    struct load_command *lc = NULL;
959    u_char *base = (u_char *) mach_header;
960    size_t offset = sizeof(*mach_header);
961    u_int i = 0;
962
963    if (mach_header->magic != MH_MAGIC_64) goto finish;
964
965    for (i = 0; i < mach_header->ncmds; ++i) {
966        lc = (struct load_command *) (base + offset);
967
968        if (lc->cmd == LC_SEGMENT_64) {
969            segment = (struct segment_command_64 *) lc;
970            if (!strncmp(segment->segname, segname, sizeof(segment->segname))) {
971                break;
972            }
973            segment = NULL;
974        }
975
976        offset += lc->cmdsize;
977    }
978
979finish:
980    return segment;
981}
982
983/*******************************************************************************
984*******************************************************************************/
985struct section * macho_get_section_by_name(
986    struct mach_header    * mach_header,
987    const char            * segname,
988    const char            * sectname)
989{
990    struct segment_command *segment = NULL;
991    struct section *section = NULL;
992    u_int i = 0;
993
994    if (mach_header->magic != MH_MAGIC) goto finish;
995
996    segment = macho_get_segment_by_name(mach_header, segname);
997    if (!segment) goto finish;
998
999    section = (struct section *) (&segment[1]);
1000    for (i = 0; i < segment->nsects; ++i, ++section) {
1001        if (!strncmp(section->sectname, sectname, sizeof(section->sectname))) {
1002            break;
1003        }
1004    }
1005
1006    if (i == segment->nsects) {
1007        section = NULL;
1008    }
1009
1010finish:
1011    return section;
1012}
1013
1014/*******************************************************************************
1015*******************************************************************************/
1016struct section_64 * macho_get_section_by_name_64(
1017    struct mach_header_64     * mach_header,
1018    const char                * segname,
1019    const char                * sectname)
1020{
1021    struct segment_command_64 *segment = NULL;
1022    struct section_64 *section = NULL;
1023    u_int i = 0;
1024
1025    if (mach_header->magic != MH_MAGIC_64) goto finish;
1026
1027    segment = macho_get_segment_by_name_64(mach_header, segname);
1028    if (!segment) goto finish;
1029
1030    section = (struct section_64 *) (&segment[1]);
1031    for (i = 0; i < segment->nsects; ++i, ++section) {
1032        if (!strncmp(section->sectname, sectname, sizeof(section->sectname))) {
1033            break;
1034        }
1035    }
1036
1037    if (i == segment->nsects) {
1038        section = NULL;
1039    }
1040
1041finish:
1042    return section;
1043}
1044
1045/*******************************************************************************
1046*******************************************************************************/
1047boolean_t macho_remove_linkedit(u_char *macho, u_long *linkedit_size)
1048{
1049    boolean_t result = FALSE;
1050    struct mach_header *mach_hdr;
1051    struct mach_header_64 *mach_hdr64;
1052    u_char *src, *dst;
1053    uint32_t ncmds, cmdsize;
1054    boolean_t swap = FALSE;
1055    u_int i;
1056
1057    swap = macho_swap(macho);
1058
1059    mach_hdr = (struct mach_header *) macho;
1060    mach_hdr64 = (struct mach_header_64 *) macho;
1061
1062    /* Find the start of the load commands */
1063
1064    if (mach_hdr->magic == MH_MAGIC) {
1065        src = dst = macho + sizeof(*mach_hdr);
1066        ncmds = mach_hdr->ncmds;
1067    } else if (mach_hdr->magic == MH_MAGIC_64) {
1068        src = dst = macho + sizeof(*mach_hdr64);
1069        ncmds = mach_hdr64->ncmds;
1070    } else {
1071        goto finish;
1072    }
1073
1074    /* Remove any LINKEDIT-related load commands */
1075
1076    for (i = 0; i < ncmds; ++i, src += cmdsize) {
1077        struct load_command * lc = (struct load_command *) src;
1078        struct segment_command *seg = (struct segment_command *) src;
1079        struct segment_command_64 *seg64 = (struct segment_command_64 *) src;
1080        boolean_t strip = FALSE;
1081
1082        cmdsize = lc->cmdsize;
1083
1084        /* We delete the LINKEDIT segment and any symtab load commands */
1085
1086        switch (lc->cmd) {
1087        case LC_SEGMENT:
1088            if (!strncmp(seg->segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT) - 1)) {
1089                strip = TRUE;
1090                *linkedit_size = seg->vmsize;
1091            }
1092            break;
1093        case LC_SEGMENT_64:
1094            if (!strncmp(seg64->segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT) - 1)) {
1095                strip = TRUE;
1096                *linkedit_size = seg64->vmsize;
1097            }
1098            break;
1099        case LC_SYMTAB:
1100        case LC_DYSYMTAB:
1101            strip = TRUE;
1102            break;
1103        }
1104
1105        if (strip) {
1106            if (mach_hdr->magic == MH_MAGIC) {
1107                mach_hdr->ncmds--;
1108                mach_hdr->sizeofcmds -= cmdsize;
1109            } else {
1110                mach_hdr64->ncmds--;
1111                mach_hdr64->sizeofcmds -= cmdsize;
1112            }
1113            bzero(src, lc->cmdsize);
1114        } else {
1115            memmove(dst, src, cmdsize);
1116            dst += cmdsize;
1117        }
1118    }
1119
1120    result = TRUE;
1121finish:
1122    if (swap) macho_unswap(macho);
1123    return result;
1124}
1125
1126/*******************************************************************************
1127 * remove the symbol table and string table from the LINKEDIT segment, leaving *
1128 * any relocation data within the segment.                                     *
1129*******************************************************************************/
1130boolean_t macho_trim_linkedit(
1131    u_char  *macho,
1132    u_long  *amount_trimmed)
1133{
1134    boolean_t result = FALSE;
1135    struct mach_header *mach_hdr;
1136    struct mach_header_64 *mach_hdr64;
1137    u_char *src, *dst;
1138    uint32_t ncmds, cmdsize;
1139    boolean_t swap = FALSE;
1140    boolean_t is32bit = FALSE;
1141    u_int i;
1142    u_char *linkedit_segment = NULL;
1143    struct symtab_command *symtab = NULL;
1144    struct dysymtab_command *dysymtab = NULL;
1145
1146    *amount_trimmed = 0;    /* initialize */
1147
1148    swap = macho_swap(macho);
1149
1150    mach_hdr = (struct mach_header *) macho;
1151    mach_hdr64 = (struct mach_header_64 *) macho;
1152
1153    /* Find the start of the load commands */
1154
1155    if (mach_hdr->magic == MH_MAGIC) {
1156        src = dst = macho + sizeof(*mach_hdr);
1157        ncmds = mach_hdr->ncmds;
1158        is32bit = TRUE;
1159    } else if (mach_hdr->magic == MH_MAGIC_64) {
1160        src = dst = macho + sizeof(*mach_hdr64);
1161        ncmds = mach_hdr64->ncmds;
1162        is32bit = FALSE;
1163    } else {
1164        goto finish;
1165    }
1166
1167    /* find any LINKEDIT-related load commands */
1168
1169    for (i = 0; i < ncmds; ++i, src += cmdsize) {
1170        struct load_command * lc = (struct load_command *) src;
1171        struct segment_command *seg = (struct segment_command *) src;
1172        struct segment_command_64 *seg64 = (struct segment_command_64 *) src;
1173
1174        cmdsize = lc->cmdsize;
1175
1176        /* First, identify the load commands of interest */
1177        switch (lc->cmd) {
1178            case LC_SEGMENT:
1179                if (!strncmp(seg->segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT) - 1)) {
1180                    linkedit_segment = src;
1181                }
1182                break;
1183
1184            case LC_SEGMENT_64:
1185                if (!strncmp(seg64->segname, SEG_LINKEDIT, sizeof(SEG_LINKEDIT) - 1)) {
1186                    linkedit_segment = src;
1187                }
1188                break;
1189
1190            case LC_SYMTAB:
1191                symtab = (struct symtab_command *) src;
1192                break;
1193
1194            case LC_DYSYMTAB:
1195                dysymtab = (struct dysymtab_command *) src;
1196                break;
1197        }
1198    }
1199
1200    /* was a LINKEDIT segment found? (it damned well better be there!) */
1201    if (linkedit_segment == NULL)
1202        goto finish;	/* yowza! */
1203
1204    /* if no DYSYMTAB command was found, just remove the entire LINKEDIT segment */
1205    if (dysymtab == NULL) {
1206        if (swap) macho_unswap(macho);
1207        return (macho_remove_linkedit(macho, amount_trimmed));
1208    }
1209    else {
1210
1211        /* Calculate size of symbol table (including strings):
1212         *   # of symbols * sizeof (nlist | nlist_64)...
1213         *   + size of string table...
1214         *   aligned to 8-byte boundary
1215         */
1216        u_long symtab_size = (((symtab->nsyms
1217                                * (is32bit ? sizeof(struct nlist) : sizeof(struct nlist_64)))
1218                               + symtab->strsize) + 7 ) & ~7;
1219
1220        /* calculate size of relocation entries */
1221        u_long reloc_size = dysymtab->nlocrel * sizeof(struct relocation_info);
1222
1223        /* cache old vmsize */
1224        u_long  old_vmsize =
1225            (is32bit
1226                ? ((struct segment_command *)    linkedit_segment)->vmsize
1227                : ((struct segment_command_64 *) linkedit_segment)->vmsize);
1228
1229        /* calculate new segment size after removal of symtab/stringtab data */
1230        u_long  new_vmsize = round_page(reloc_size);
1231
1232        /* If the relocation entries are positioned within the LINKEDIT segment AFTER
1233         * the symbol table, those entries must be moved within the segment. Otherwise,
1234         * the segment can simply be truncated to remove the symbol table.
1235         */
1236        if (symtab->symoff < dysymtab->locreloff) {
1237            /* move them up within the segment, overwriting the existing symbol table */
1238            memmove(macho + symtab->symoff, macho + dysymtab->locreloff, reloc_size);
1239
1240            /* update the header field */
1241            dysymtab->locreloff = symtab->symoff;
1242
1243            /* clear now-unused data within the segment */
1244            bzero(macho + dysymtab->locreloff + reloc_size, symtab_size);
1245        }
1246        else {
1247            /* symtab/stringtab entries are located after the relocation entries
1248             * in the segment. Therefore, we just have to truncate the segment
1249             * appropriately
1250             */
1251            bzero(macho + symtab->symoff, symtab_size);	/* wipe any existing data */
1252        }
1253
1254        /* update LINKEDIT segment command with new size */
1255        if (is32bit) {
1256            ((struct segment_command *) linkedit_segment)->vmsize =
1257            ((struct segment_command *) linkedit_segment)->filesize = new_vmsize;
1258        }
1259        else {
1260            ((struct segment_command_64 *) linkedit_segment)->vmsize =
1261            ((struct segment_command_64 *) linkedit_segment)->filesize = new_vmsize;
1262        }
1263
1264        /* notify caller of # of bytes removed from segment */
1265        *amount_trimmed = old_vmsize - new_vmsize;
1266    }
1267
1268    /* now that the LINKEDIT segment contents have been adjusted properly, we must
1269     * remove the actual SYMTAB load command from the header
1270     */
1271    src = dst;  /* reset for second pass through header */
1272    for (i = 0; i < ncmds; ++i, src += cmdsize) {
1273        struct load_command * lc = (struct load_command *) src;
1274
1275        cmdsize = lc->cmdsize;
1276
1277        if (lc->cmd == LC_SYMTAB) {
1278            if (is32bit) {
1279                mach_hdr->ncmds--;
1280                mach_hdr->sizeofcmds -= cmdsize;
1281            } else {
1282                mach_hdr64->ncmds--;
1283                mach_hdr64->sizeofcmds -= cmdsize;
1284            }
1285            bzero(src, lc->cmdsize);	/* zap the SYMTAB command */
1286        }
1287        else {
1288            /* move remaining load commands up within the header */
1289            if (dst != src) {
1290                memmove(dst, src, cmdsize);
1291            }
1292            dst += cmdsize;
1293        }
1294    }
1295
1296    result = TRUE;
1297finish:
1298    if (swap) macho_unswap(macho);
1299    return result;
1300}
1301