source: XIOS3/trunk/extern/cpptrace/src/mach-o.hpp

Last change on this file was 2573, checked in by ymipsl, 9 months ago

create new external source lib : cpptrace, for statck trace output
YM

File size: 5.3 KB
Line 
1#ifndef MACHO_HPP
2#define MACHO_HPP
3
4#include "common.hpp"
5
6#if IS_APPLE
7#include <cstdio>
8#include <cstring>
9#include <type_traits>
10
11#include <mach-o/loader.h>
12#include <mach-o/swap.h>
13#include <mach-o/fat.h>
14
15// Based on https://github.com/AlexDenisov/segment_dumper/blob/master/main.c
16// and https://lowlevelbits.org/parsing-mach-o-files/
17
18static bool is_magic_64(uint32_t magic) {
19    return magic == MH_MAGIC_64 || magic == MH_CIGAM_64;
20}
21
22static bool should_swap_bytes(uint32_t magic) {
23    return magic == MH_CIGAM || magic == MH_CIGAM_64 || magic == FAT_CIGAM;
24}
25
26#if defined(__aarch64__)
27 #define CURRENT_CPU CPU_TYPE_ARM64
28#elif defined(__arm__) && defined(__thumb__)
29 #define CURRENT_CPU CPU_TYPE_ARM
30#elif defined(__amd64__)
31 #define CURRENT_CPU CPU_TYPE_X86_64
32#elif defined(__i386__)
33 #define CURRENT_CPU CPU_TYPE_I386
34#else
35 #error "Unknown CPU architecture"
36#endif
37
38static uintptr_t macho_get_text_vmaddr_from_segments(FILE* obj_file, off_t offset, bool should_swap, uint32_t ncmds) {
39    off_t actual_offset = offset;
40    for(uint32_t i = 0; i < ncmds; i++) {
41        load_command cmd = load_bytes<load_command>(obj_file, actual_offset);
42        if(should_swap) {
43            swap_load_command(&cmd, NX_UnknownByteOrder);
44        }
45        if(cmd.cmd == LC_SEGMENT_64) {
46            segment_command_64 segment = load_bytes<segment_command_64>(obj_file, actual_offset);
47            if(should_swap) {
48                swap_segment_command_64(&segment, NX_UnknownByteOrder);
49            }
50            //printf("segname(64): %s\n", segment.segname);
51            //printf("             %d\n", segment.nsects);
52            //printf("             %p\n", segment.vmaddr);
53            //printf("             %p\n", segment.vmsize);
54            if(strcmp(segment.segname, "__TEXT") == 0) {
55                return segment.vmaddr;
56            }
57        } else if(cmd.cmd == LC_SEGMENT) {
58            segment_command segment = load_bytes<segment_command>(obj_file, actual_offset);
59            if(should_swap) {
60                swap_segment_command(&segment, NX_UnknownByteOrder);
61            }
62            //printf("segname: %s\n", segment.segname);
63            if(strcmp(segment.segname, "__TEXT") == 0) {
64                return segment.vmaddr;
65            }
66        }
67        actual_offset += cmd.cmdsize;
68    }
69    // somehow no __TEXT section was found...
70    return 0;
71}
72
73static uintptr_t macho_get_text_vmaddr_mach(FILE* obj_file, off_t offset, bool is_64, bool should_swap) {
74    uint32_t ncmds;
75    off_t load_commands_offset = offset;
76    if(is_64) {
77        size_t header_size = sizeof(mach_header_64);
78        mach_header_64 header = load_bytes<mach_header_64>(obj_file, offset);
79        //if(offset != 0) { // if fat the offset will be non-zero, if not fat the offset will be zero
80            if(header.cputype != CURRENT_CPU) {
81                return 0;
82            }
83        //}
84        if(should_swap) {
85            swap_mach_header_64(&header, NX_UnknownByteOrder);
86        }
87        ncmds = header.ncmds;
88        load_commands_offset += header_size;
89    } else {
90        size_t header_size = sizeof(mach_header);
91        mach_header header = load_bytes<mach_header>(obj_file, offset);
92        //if(offset != 0) { // if fat the offset will be non-zero, if not fat the offset will be zero
93            if(header.cputype != CURRENT_CPU) {
94                return 0;
95            }
96        //}
97        if(should_swap) {
98            swap_mach_header(&header, NX_UnknownByteOrder);
99        }
100        ncmds = header.ncmds;
101        load_commands_offset += header_size;
102    }
103    return macho_get_text_vmaddr_from_segments(obj_file, load_commands_offset, should_swap, ncmds);
104}
105
106static uintptr_t macho_get_text_vmaddr_fat(FILE* obj_file, bool should_swap) {
107    size_t header_size = sizeof(fat_header);
108    size_t arch_size = sizeof(fat_arch);
109    fat_header header = load_bytes<fat_header>(obj_file, 0);
110    if(should_swap) {
111        swap_fat_header(&header, NX_UnknownByteOrder);
112    }
113    off_t arch_offset = (off_t)header_size;
114    uintptr_t text_vmaddr = 0;
115    for(uint32_t i = 0; i < header.nfat_arch; i++) {
116        fat_arch arch = load_bytes<fat_arch>(obj_file, arch_offset);
117        if(should_swap) {
118            swap_fat_arch(&arch, 1, NX_UnknownByteOrder);
119        }
120        off_t mach_header_offset = (off_t)arch.offset;
121        arch_offset += arch_size;
122        uint32_t magic = load_bytes<uint32_t>(obj_file, mach_header_offset);
123        text_vmaddr = macho_get_text_vmaddr_mach(
124            obj_file,
125            mach_header_offset,
126            is_magic_64(magic),
127            should_swap_bytes(magic)
128        );
129        if(text_vmaddr != 0) {
130            return text_vmaddr;
131        }
132    }
133    // If this is reached... something went wrong. The cpu we're on wasn't found.
134    return text_vmaddr;
135}
136
137static uintptr_t macho_get_text_vmaddr(const char* path) {
138    FILE* obj_file = fopen(path, "rb");
139    if(obj_file == nullptr) {
140        throw file_error();
141    }
142    uint32_t magic = load_bytes<uint32_t>(obj_file, 0);
143    bool is_64 = is_magic_64(magic);
144    bool should_swap = should_swap_bytes(magic);
145    uintptr_t addr;
146    if(magic == FAT_MAGIC || magic == FAT_CIGAM) {
147        addr = macho_get_text_vmaddr_fat(obj_file, should_swap);
148    } else {
149        addr = macho_get_text_vmaddr_mach(obj_file, 0, is_64, should_swap);
150    }
151    fclose(obj_file);
152    return addr;
153}
154
155#endif
156
157#endif
Note: See TracBrowser for help on using the repository browser.