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