├── .gitignore ├── DOLLoader.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── DOLLoader ├── DOLLoader-Info.plist ├── DOLLoader-Prefix.pch ├── DOLLoader.h ├── DOLLoader.m └── en.lproj │ └── InfoPlist.strings ├── GNUmakefile ├── PPCCPU.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── PPCCPU ├── PPCCPU-Info.plist ├── PPCCPU-Prefix.pch ├── PPCCPU.h ├── PPCCPU.m ├── PPCCtx.h ├── PPCCtx.m ├── en.lproj │ └── InfoPlist.strings └── ppcd │ ├── CommonDefs.h │ ├── main.cpp │ ├── ppcd.h │ └── ppcd.m ├── README.md ├── RELLinker.xcodeproj ├── project.pbxproj └── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ └── IDEWorkspaceChecks.plist └── RELLinker ├── RELLinker-Info.plist ├── RELLinker-Prefix.pch ├── RELLinker.h ├── RELLinker.m └── en.lproj └── InfoPlist.strings /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ 27 | -------------------------------------------------------------------------------- /DOLLoader.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 076B713D18C4D07F00DE7FD0 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 076B713C18C4D07F00DE7FD0 /* CoreFoundation.framework */; }; 11 | 076B714318C4D07F00DE7FD0 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 076B714118C4D07F00DE7FD0 /* InfoPlist.strings */; }; 12 | 076B714C18C4D1A900DE7FD0 /* DOLLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 076B714B18C4D1A900DE7FD0 /* DOLLoader.m */; }; 13 | /* End PBXBuildFile section */ 14 | 15 | /* Begin PBXFileReference section */ 16 | 076B713918C4D07F00DE7FD0 /* DOLLoader.hopperLoader */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DOLLoader.hopperLoader; sourceTree = BUILT_PRODUCTS_DIR; }; 17 | 076B713C18C4D07F00DE7FD0 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 18 | 076B714018C4D07F00DE7FD0 /* DOLLoader-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "DOLLoader-Info.plist"; sourceTree = ""; }; 19 | 076B714218C4D07F00DE7FD0 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 20 | 076B714418C4D07F00DE7FD0 /* DOLLoader-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "DOLLoader-Prefix.pch"; sourceTree = ""; }; 21 | 076B714A18C4D1A900DE7FD0 /* DOLLoader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = DOLLoader.h; sourceTree = ""; }; 22 | 076B714B18C4D1A900DE7FD0 /* DOLLoader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = DOLLoader.m; sourceTree = ""; }; 23 | /* End PBXFileReference section */ 24 | 25 | /* Begin PBXFrameworksBuildPhase section */ 26 | 076B713618C4D07F00DE7FD0 /* Frameworks */ = { 27 | isa = PBXFrameworksBuildPhase; 28 | buildActionMask = 2147483647; 29 | files = ( 30 | 076B713D18C4D07F00DE7FD0 /* CoreFoundation.framework in Frameworks */, 31 | ); 32 | runOnlyForDeploymentPostprocessing = 0; 33 | }; 34 | /* End PBXFrameworksBuildPhase section */ 35 | 36 | /* Begin PBXGroup section */ 37 | 076B713018C4D07F00DE7FD0 = { 38 | isa = PBXGroup; 39 | children = ( 40 | 076B713E18C4D07F00DE7FD0 /* DOLLoader */, 41 | 076B713F18C4D07F00DE7FD0 /* Supporting Files */, 42 | 076B713B18C4D07F00DE7FD0 /* Frameworks */, 43 | 076B713A18C4D07F00DE7FD0 /* Products */, 44 | ); 45 | sourceTree = ""; 46 | }; 47 | 076B713A18C4D07F00DE7FD0 /* Products */ = { 48 | isa = PBXGroup; 49 | children = ( 50 | 076B713918C4D07F00DE7FD0 /* DOLLoader.hopperLoader */, 51 | ); 52 | name = Products; 53 | sourceTree = ""; 54 | }; 55 | 076B713B18C4D07F00DE7FD0 /* Frameworks */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 076B713C18C4D07F00DE7FD0 /* CoreFoundation.framework */, 59 | ); 60 | name = Frameworks; 61 | sourceTree = ""; 62 | }; 63 | 076B713E18C4D07F00DE7FD0 /* DOLLoader */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 076B714A18C4D1A900DE7FD0 /* DOLLoader.h */, 67 | 076B714B18C4D1A900DE7FD0 /* DOLLoader.m */, 68 | ); 69 | path = DOLLoader; 70 | sourceTree = ""; 71 | }; 72 | 076B713F18C4D07F00DE7FD0 /* Supporting Files */ = { 73 | isa = PBXGroup; 74 | children = ( 75 | 076B714018C4D07F00DE7FD0 /* DOLLoader-Info.plist */, 76 | 076B714118C4D07F00DE7FD0 /* InfoPlist.strings */, 77 | 076B714418C4D07F00DE7FD0 /* DOLLoader-Prefix.pch */, 78 | ); 79 | name = "Supporting Files"; 80 | path = DOL_Loader; 81 | sourceTree = ""; 82 | }; 83 | /* End PBXGroup section */ 84 | 85 | /* Begin PBXNativeTarget section */ 86 | 076B713818C4D07F00DE7FD0 /* DOLLoader */ = { 87 | isa = PBXNativeTarget; 88 | buildConfigurationList = 076B714718C4D07F00DE7FD0 /* Build configuration list for PBXNativeTarget "DOLLoader" */; 89 | buildPhases = ( 90 | 076B713518C4D07F00DE7FD0 /* Sources */, 91 | 076B713618C4D07F00DE7FD0 /* Frameworks */, 92 | 076B713718C4D07F00DE7FD0 /* Resources */, 93 | 07644CA318C4E4D2004F8FE7 /* Run Script (Installation) */, 94 | ); 95 | buildRules = ( 96 | ); 97 | dependencies = ( 98 | ); 99 | name = DOLLoader; 100 | productName = DOL_Loader; 101 | productReference = 076B713918C4D07F00DE7FD0 /* DOLLoader.hopperLoader */; 102 | productType = "com.apple.product-type.bundle"; 103 | }; 104 | /* End PBXNativeTarget section */ 105 | 106 | /* Begin PBXProject section */ 107 | 076B713118C4D07F00DE7FD0 /* Project object */ = { 108 | isa = PBXProject; 109 | attributes = { 110 | LastUpgradeCheck = 0800; 111 | ORGANIZATIONNAME = com.sappharad; 112 | }; 113 | buildConfigurationList = 076B713418C4D07F00DE7FD0 /* Build configuration list for PBXProject "DOLLoader" */; 114 | compatibilityVersion = "Xcode 3.2"; 115 | developmentRegion = English; 116 | hasScannedForEncodings = 0; 117 | knownRegions = ( 118 | en, 119 | ); 120 | mainGroup = 076B713018C4D07F00DE7FD0; 121 | productRefGroup = 076B713A18C4D07F00DE7FD0 /* Products */; 122 | projectDirPath = ""; 123 | projectRoot = ""; 124 | targets = ( 125 | 076B713818C4D07F00DE7FD0 /* DOLLoader */, 126 | ); 127 | }; 128 | /* End PBXProject section */ 129 | 130 | /* Begin PBXResourcesBuildPhase section */ 131 | 076B713718C4D07F00DE7FD0 /* Resources */ = { 132 | isa = PBXResourcesBuildPhase; 133 | buildActionMask = 2147483647; 134 | files = ( 135 | 076B714318C4D07F00DE7FD0 /* InfoPlist.strings in Resources */, 136 | ); 137 | runOnlyForDeploymentPostprocessing = 0; 138 | }; 139 | /* End PBXResourcesBuildPhase section */ 140 | 141 | /* Begin PBXShellScriptBuildPhase section */ 142 | 07644CA318C4E4D2004F8FE7 /* Run Script (Installation) */ = { 143 | isa = PBXShellScriptBuildPhase; 144 | buildActionMask = 2147483647; 145 | files = ( 146 | ); 147 | inputPaths = ( 148 | ); 149 | name = "Run Script (Installation)"; 150 | outputPaths = ( 151 | ); 152 | runOnlyForDeploymentPostprocessing = 0; 153 | shellPath = /bin/sh; 154 | shellScript = "rm -rf \"${INSTALL_PATH}/DOLLoader.hopperLoader\"\nmkdir -p \"${INSTALL_PATH}\"\ncp -rf \"${BUILT_PRODUCTS_DIR}/DOLLoader.hopperLoader\" \"${INSTALL_PATH}\"\n"; 155 | }; 156 | /* End PBXShellScriptBuildPhase section */ 157 | 158 | /* Begin PBXSourcesBuildPhase section */ 159 | 076B713518C4D07F00DE7FD0 /* Sources */ = { 160 | isa = PBXSourcesBuildPhase; 161 | buildActionMask = 2147483647; 162 | files = ( 163 | 076B714C18C4D1A900DE7FD0 /* DOLLoader.m in Sources */, 164 | ); 165 | runOnlyForDeploymentPostprocessing = 0; 166 | }; 167 | /* End PBXSourcesBuildPhase section */ 168 | 169 | /* Begin PBXVariantGroup section */ 170 | 076B714118C4D07F00DE7FD0 /* InfoPlist.strings */ = { 171 | isa = PBXVariantGroup; 172 | children = ( 173 | 076B714218C4D07F00DE7FD0 /* en */, 174 | ); 175 | name = InfoPlist.strings; 176 | sourceTree = ""; 177 | }; 178 | /* End PBXVariantGroup section */ 179 | 180 | /* Begin XCBuildConfiguration section */ 181 | 076B714518C4D07F00DE7FD0 /* Debug */ = { 182 | isa = XCBuildConfiguration; 183 | buildSettings = { 184 | ALWAYS_SEARCH_USER_PATHS = NO; 185 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 186 | CLANG_CXX_LIBRARY = "libc++"; 187 | CLANG_ENABLE_OBJC_ARC = YES; 188 | CLANG_WARN_BOOL_CONVERSION = YES; 189 | CLANG_WARN_CONSTANT_CONVERSION = YES; 190 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 191 | CLANG_WARN_EMPTY_BODY = YES; 192 | CLANG_WARN_ENUM_CONVERSION = YES; 193 | CLANG_WARN_INFINITE_RECURSION = YES; 194 | CLANG_WARN_INT_CONVERSION = YES; 195 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 196 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 197 | CLANG_WARN_UNREACHABLE_CODE = YES; 198 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 199 | COPY_PHASE_STRIP = NO; 200 | ENABLE_STRICT_OBJC_MSGSEND = YES; 201 | GCC_C_LANGUAGE_STANDARD = gnu99; 202 | GCC_DYNAMIC_NO_PIC = NO; 203 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 204 | GCC_NO_COMMON_BLOCKS = YES; 205 | GCC_OPTIMIZATION_LEVEL = 0; 206 | GCC_PREPROCESSOR_DEFINITIONS = ( 207 | "DEBUG=1", 208 | "$(inherited)", 209 | ); 210 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 211 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 212 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 213 | GCC_WARN_UNDECLARED_SELECTOR = YES; 214 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 215 | GCC_WARN_UNUSED_FUNCTION = YES; 216 | GCC_WARN_UNUSED_VARIABLE = YES; 217 | MACOSX_DEPLOYMENT_TARGET = 10.9; 218 | ONLY_ACTIVE_ARCH = YES; 219 | SDKROOT = macosx; 220 | }; 221 | name = Debug; 222 | }; 223 | 076B714618C4D07F00DE7FD0 /* Release */ = { 224 | isa = XCBuildConfiguration; 225 | buildSettings = { 226 | ALWAYS_SEARCH_USER_PATHS = NO; 227 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 228 | CLANG_CXX_LIBRARY = "libc++"; 229 | CLANG_ENABLE_OBJC_ARC = YES; 230 | CLANG_WARN_BOOL_CONVERSION = YES; 231 | CLANG_WARN_CONSTANT_CONVERSION = YES; 232 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 233 | CLANG_WARN_EMPTY_BODY = YES; 234 | CLANG_WARN_ENUM_CONVERSION = YES; 235 | CLANG_WARN_INFINITE_RECURSION = YES; 236 | CLANG_WARN_INT_CONVERSION = YES; 237 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 238 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 239 | CLANG_WARN_UNREACHABLE_CODE = YES; 240 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 241 | COPY_PHASE_STRIP = YES; 242 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 243 | ENABLE_NS_ASSERTIONS = NO; 244 | ENABLE_STRICT_OBJC_MSGSEND = YES; 245 | GCC_C_LANGUAGE_STANDARD = gnu99; 246 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 247 | GCC_NO_COMMON_BLOCKS = YES; 248 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 249 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 250 | GCC_WARN_UNDECLARED_SELECTOR = YES; 251 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 252 | GCC_WARN_UNUSED_FUNCTION = YES; 253 | GCC_WARN_UNUSED_VARIABLE = YES; 254 | MACOSX_DEPLOYMENT_TARGET = 10.9; 255 | SDKROOT = macosx; 256 | }; 257 | name = Release; 258 | }; 259 | 076B714818C4D07F00DE7FD0 /* Debug */ = { 260 | isa = XCBuildConfiguration; 261 | buildSettings = { 262 | COMBINE_HIDPI_IMAGES = YES; 263 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 264 | GCC_PREFIX_HEADER = "DOL_Loader/DOLLoader-Prefix.pch"; 265 | HEADER_SEARCH_PATHS = ( 266 | "$(inherited)", 267 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 268 | "$(SRCROOT)/../include", 269 | ); 270 | INFOPLIST_FILE = "DOL_Loader/DOLLoader-Info.plist"; 271 | INSTALL_PATH = "$(USER_LIBRARY_DIR)/Application Support/Hopper/PlugIns/v4/Loaders"; 272 | PRODUCT_BUNDLE_IDENTIFIER = "com.sappharad.${PRODUCT_NAME:rfc1034identifier}"; 273 | PRODUCT_NAME = DOLLoader; 274 | WRAPPER_EXTENSION = hopperLoader; 275 | }; 276 | name = Debug; 277 | }; 278 | 076B714918C4D07F00DE7FD0 /* Release */ = { 279 | isa = XCBuildConfiguration; 280 | buildSettings = { 281 | COMBINE_HIDPI_IMAGES = YES; 282 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 283 | GCC_PREFIX_HEADER = "DOL_Loader/DOLLoader-Prefix.pch"; 284 | HEADER_SEARCH_PATHS = ( 285 | "$(inherited)", 286 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 287 | "$(SRCROOT)/../include", 288 | ); 289 | INFOPLIST_FILE = "DOL_Loader/DOLLoader-Info.plist"; 290 | INSTALL_PATH = "$(USER_LIBRARY_DIR)/Application Support/Hopper/PlugIns/v4/Loaders"; 291 | PRODUCT_BUNDLE_IDENTIFIER = "com.sappharad.${PRODUCT_NAME:rfc1034identifier}"; 292 | PRODUCT_NAME = DOLLoader; 293 | WRAPPER_EXTENSION = hopperLoader; 294 | }; 295 | name = Release; 296 | }; 297 | /* End XCBuildConfiguration section */ 298 | 299 | /* Begin XCConfigurationList section */ 300 | 076B713418C4D07F00DE7FD0 /* Build configuration list for PBXProject "DOLLoader" */ = { 301 | isa = XCConfigurationList; 302 | buildConfigurations = ( 303 | 076B714518C4D07F00DE7FD0 /* Debug */, 304 | 076B714618C4D07F00DE7FD0 /* Release */, 305 | ); 306 | defaultConfigurationIsVisible = 0; 307 | defaultConfigurationName = Release; 308 | }; 309 | 076B714718C4D07F00DE7FD0 /* Build configuration list for PBXNativeTarget "DOLLoader" */ = { 310 | isa = XCConfigurationList; 311 | buildConfigurations = ( 312 | 076B714818C4D07F00DE7FD0 /* Debug */, 313 | 076B714918C4D07F00DE7FD0 /* Release */, 314 | ); 315 | defaultConfigurationIsVisible = 0; 316 | defaultConfigurationName = Release; 317 | }; 318 | /* End XCConfigurationList section */ 319 | }; 320 | rootObject = 076B713118C4D07F00DE7FD0 /* Project object */; 321 | } 322 | -------------------------------------------------------------------------------- /DOLLoader.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DOLLoader.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /DOLLoader/DOLLoader-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | CFPlugInDynamicRegisterFunction 26 | 27 | CFPlugInDynamicRegistration 28 | NO 29 | CFPlugInFactories 30 | 31 | 00000000-0000-0000-0000-000000000000 32 | MyFactoryFunction 33 | 34 | CFPlugInTypes 35 | 36 | 00000000-0000-0000-0000-000000000000 37 | 38 | 00000000-0000-0000-0000-000000000000 39 | 40 | 41 | CFPlugInUnloadFunction 42 | 43 | NSHumanReadableCopyright 44 | Copyright © 2015. Some rights reserved. 45 | NSPrincipalClass 46 | DOLLoader 47 | 48 | 49 | -------------------------------------------------------------------------------- /DOLLoader/DOLLoader-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /DOLLoader/DOLLoader.h: -------------------------------------------------------------------------------- 1 | // 2 | // DOLLoader.h 3 | // DOLLoader 4 | // 5 | // Created by Paul Kratt on 11/30/2015. 6 | // Copyright (c) 2015. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface DOLLoader : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /DOLLoader/DOLLoader.m: -------------------------------------------------------------------------------- 1 | // 2 | // DOLLoader.m 3 | // DOLLoader 4 | // 5 | // Created by Paul Kratt on 11/30/2015. 6 | // Copyright (c) 2015. 7 | // 8 | 9 | #import "DOLLoader.h" 10 | 11 | #ifdef LINUX 12 | #include 13 | 14 | static int16_t OSReadBigInt16(const void *address, uintptr_t offset) { 15 | return be16toh(*(int16_t *) ((uintptr_t) address + offset)); 16 | } 17 | 18 | static int32_t OSReadBigInt32(const void *address, uintptr_t offset) { 19 | return be32toh(*(int32_t *) ((uintptr_t) address + offset)); 20 | } 21 | 22 | static void OSWriteBigInt32(void *address, uintptr_t offset, int32_t data) { 23 | *(int32_t *) ((uintptr_t) address + offset) = htobe32(data); 24 | } 25 | 26 | #endif 27 | 28 | @implementation DOLLoader { 29 | NSObject *_services; 30 | } 31 | 32 | - (instancetype)initWithHopperServices:(NSObject *)services { 33 | if (self = [super init]) { 34 | _services = services; 35 | } 36 | return self; 37 | } 38 | 39 | - (HopperUUID *)pluginUUID { 40 | return [_services UUIDWithString:@"b1906da2-7650-4db8-91cc-d9073b17aa47"]; 41 | } 42 | 43 | - (HopperPluginType)pluginType { 44 | return Plugin_Loader; 45 | } 46 | 47 | - (NSString *)pluginName { 48 | return @"Gamecube/Wii Executable"; 49 | } 50 | 51 | - (NSString *)pluginDescription { 52 | return @"Dolphin Executable (DOL) Loader"; 53 | } 54 | 55 | - (NSString *)pluginAuthor { 56 | return @"Paul Kratt and Jack Andersen"; 57 | } 58 | 59 | - (NSString *)pluginCopyright { 60 | return @"© Paul Kratt and Jack Andersen"; 61 | } 62 | 63 | - (NSString *)pluginVersion { 64 | return @"0.6.0"; 65 | } 66 | 67 | - (NSString *)commandLineIdentifier { 68 | return @"DOL"; 69 | } 70 | 71 | - (CPUEndianess)endianess { 72 | return CPUEndianess_Big; 73 | } 74 | 75 | - (BOOL)canLoadDebugFiles { 76 | return YES; 77 | } 78 | 79 | // Returns an array of DetectedFileType objects. 80 | - (NSArray *)detectedTypesForData:(NSData *)data { 81 | if ([data length] < 0x100) return @[]; 82 | 83 | const void *bytes = (const void *)[data bytes]; 84 | uint32_t t0offset = OSReadBigInt32(bytes, 0); 85 | uint32_t t0dest = OSReadBigInt32(bytes, 0x48); 86 | uint32_t entryPoint = OSReadBigInt32(bytes, 0xE0); 87 | 88 | //Gamecube RAM range is 0x80000000 to 0x8C000000. 89 | //The Wii has more memory than that, but for the initial release I'm targeting Gamecube specs. 90 | 91 | //Text0 does not need to be at 0x100, but it always is. We're going to use it to identify this as a DOL. 92 | if (t0offset == 0x100 && 93 | t0dest >= 0x80000000 && t0dest <= 0x8C000000 && 94 | entryPoint >= 0x80000000 && entryPoint <= 0x8C000000) { 95 | NSObject *type = [_services detectedType]; 96 | [type setFileDescription:@"Gamecube/Wii Executable"]; 97 | [type setAddressWidth:AW_32bits]; 98 | [type setCpuFamily:@"ppc32"]; 99 | [type setCpuSubFamily:@"gecko"]; 100 | [type setShortDescriptionString:@"gamecube_dol"]; 101 | type.additionalParameters = @[[_services checkboxComponentWithLabel:@"Scan data sections for addresses" checked:YES]]; 102 | return @[type]; 103 | } 104 | 105 | return @[]; 106 | } 107 | 108 | struct SectionRange 109 | { 110 | uint32_t start; 111 | uint32_t length; 112 | uint32_t idx; 113 | }; 114 | 115 | static int SectionSort(const void* e1, const void* e2) 116 | { 117 | return ((const struct SectionRange*)e1)->start - ((const struct SectionRange*)e2)->start; 118 | } 119 | 120 | static const struct SectionRange* FindSectionRange(const struct SectionRange* ranges, uint32_t idx) 121 | { 122 | for(int i=0; i<19; i++){ 123 | if (ranges[i].idx == idx) 124 | return &ranges[i]; 125 | } 126 | return NULL; 127 | } 128 | 129 | - (FileLoaderLoadingStatus)loadData:(NSData *)data usingDetectedFileType:(NSObject *)fileType options:(FileLoaderOptions)options forFile:(NSObject *)file usingCallback:(FileLoadingCallbackInfo)callback { 130 | const void *bytes = (const void *)[data bytes]; 131 | if (OSReadBigInt32(bytes, 0) != 0x100) return DIS_BadFormat; 132 | 133 | /* Sort section ranges by memory offset and clamp lengths to avoid overlap */ 134 | struct SectionRange sections[19]; 135 | for(int i=0; i<18; i++){ 136 | sections[i].start = OSReadBigInt32(bytes, 0x48+(i*4)); 137 | sections[i].length = OSReadBigInt32(bytes, 0x90+(i*4)); 138 | if (!sections[i].start) { 139 | sections[i].start = 0xffffffff; 140 | sections[i].length = 0xffffffff; 141 | } 142 | sections[i].idx = i; 143 | } 144 | uint32_t bssLocation = OSReadBigInt32(bytes, 0xD8); 145 | uint32_t bssLength = OSReadBigInt32(bytes, 0xDC); 146 | sections[18].start = bssLocation; 147 | sections[18].length = bssLength; 148 | if (!sections[18].start) { 149 | sections[18].start = 0xffffffff; 150 | sections[18].length = 0xffffffff; 151 | } 152 | sections[18].idx = 18; 153 | qsort(sections, 19, sizeof(struct SectionRange), SectionSort); 154 | int secCount = 0; 155 | for (int i=0; i<18; i++){ 156 | if (sections[i].start == 0xffffffff) 157 | break; 158 | if (sections[i].start + sections[i].length > sections[i+1].start) 159 | sections[i].length = sections[i+1].start - sections[i].start; 160 | ++secCount; 161 | } 162 | 163 | /* Create sections */ 164 | int lastData = 0; 165 | int lastData2 = 0; 166 | for(int i=0; i<18; i++){ 167 | const struct SectionRange* range = FindSectionRange(sections, i); 168 | uint32_t regionStart = OSReadBigInt32(bytes, 0+(i*4)); 169 | uint32_t regionDest = range->start; 170 | uint32_t regionSize = range->length; 171 | 172 | if (regionStart > 0 && regionDest > 0x80000000 && regionSize > 0) { 173 | NSObject *segment = [file addSegmentAt:regionDest size:regionSize]; 174 | NSObject *section = [segment addSectionAt:regionDest size:regionSize]; 175 | 176 | if (i<=6) { 177 | section.pureCodeSection = YES; 178 | section.containsCode = YES; 179 | segment.readable = YES; 180 | segment.writable = NO; 181 | segment.executable = YES; 182 | if (secCount == 11) { 183 | if (i == 0) { 184 | segment.segmentName = @"INIT"; 185 | section.sectionName = @"init"; 186 | } else if (i == 1) { 187 | segment.segmentName = @"TEXT"; 188 | section.sectionName = @"text"; 189 | } else { 190 | segment.segmentName = [NSString stringWithFormat:@"TEXT%d",i]; 191 | section.sectionName = [NSString stringWithFormat:@"text%d",i]; 192 | } 193 | } else { 194 | segment.segmentName = [NSString stringWithFormat:@"TEXT%d",i]; 195 | section.sectionName = [NSString stringWithFormat:@"text%d",i]; 196 | } 197 | } 198 | else{ 199 | section.pureDataSection = YES; 200 | segment.readable = YES; 201 | segment.writable = NO; 202 | segment.executable = NO; 203 | if (secCount == 11) { 204 | if (i == 7) { 205 | segment.segmentName = @"EXTAB"; 206 | section.sectionName = @"extab"; 207 | } else if (i == 8) { 208 | segment.segmentName = @"EXTABINDEX"; 209 | section.sectionName = @"extabindex"; 210 | } else if (i == 9) { 211 | segment.segmentName = @"CTORS"; 212 | section.sectionName = @"ctors"; 213 | } else if (i == 10) { 214 | segment.segmentName = @"DTORS"; 215 | section.sectionName = @"dtors"; 216 | } else if (i == 11) { 217 | segment.segmentName = @"RODATA"; 218 | section.sectionName = @"rodata"; 219 | } else if (i == 12) { 220 | segment.segmentName = @"DATA"; 221 | section.sectionName = @"data"; 222 | segment.writable = YES; 223 | } else if (i == 13) { 224 | segment.segmentName = @"SDATA"; 225 | section.sectionName = @"sdata"; 226 | segment.writable = YES; 227 | } else if (i == 14) { 228 | segment.segmentName = @"SDATA2"; 229 | section.sectionName = @"sdata2"; 230 | } else { 231 | segment.segmentName = [NSString stringWithFormat:@"DATA%d",i-6]; 232 | section.sectionName = [NSString stringWithFormat:@"data%d",i-6]; 233 | } 234 | } else { 235 | segment.segmentName = [NSString stringWithFormat:@"DATA%d",i-6]; 236 | section.sectionName = [NSString stringWithFormat:@"data%d",i-6]; 237 | } 238 | lastData2 = lastData; 239 | lastData = i; 240 | } 241 | NSLog(@"Create section %@ of %d bytes at [0x%x;] from 0x%x", 242 | section.sectionName, regionSize, regionDest, regionStart); 243 | 244 | NSString *comment = [NSString stringWithFormat:@"\n\nHunk %@\n\n", segment.segmentName]; 245 | [file setComment:comment atVirtualAddress:regionDest reason:CCReason_Automatic]; 246 | 247 | NSData *segmentData = [NSData dataWithBytes:(bytes+regionStart) length:regionSize]; 248 | segment.mappedData = segmentData; 249 | segment.fileOffset = regionStart; 250 | segment.fileLength = regionSize; 251 | section.fileOffset = segment.fileOffset; 252 | section.fileLength = segment.fileLength; 253 | } 254 | } 255 | 256 | /* Create BSS section */ 257 | const struct SectionRange* range = FindSectionRange(sections, 18); 258 | bssLocation = range->start; 259 | bssLength = range->length; 260 | if(bssLocation >= 0x80000000 && bssLocation <= 0x8C000000 && bssLength > 0){ 261 | NSObject *segment = [file addSegmentAt:bssLocation size:bssLength]; 262 | [segment setMappedData:[NSMutableData dataWithLength:bssLength]]; 263 | NSObject *section = [segment addSectionAt:bssLocation size:bssLength]; 264 | segment.segmentName = @"BSS"; 265 | section.sectionName = @"bss"; 266 | section.pureDataSection = YES; 267 | section.zeroFillSection = YES; 268 | segment.readable = YES; 269 | segment.writable = YES; 270 | segment.executable = NO; 271 | NSLog(@"Create section %@ of %d bytes at [0x%x;]", 272 | section.sectionName, bssLength, bssLocation); 273 | } 274 | 275 | /* Detect hidden SBSS section */ 276 | if (lastData && lastData2) { 277 | const struct SectionRange* range1 = FindSectionRange(sections, lastData2); 278 | const struct SectionRange* range2 = FindSectionRange(sections, lastData); 279 | /* Gap between contiguous small data sections */ 280 | if (range2 - range1 == 1 && range1->start + range1->length < range2->start) { 281 | uint32_t sbssLocation = range1->start + range1->length; 282 | uint32_t sbssLength = range2->start - sbssLocation; 283 | NSObject *segment = [file addSegmentAt:sbssLocation size:sbssLength]; 284 | [segment setMappedData:[NSMutableData dataWithLength:sbssLength]]; 285 | NSObject *section = [segment addSectionAt:sbssLocation size:sbssLength]; 286 | segment.segmentName = @"SBSS"; 287 | section.sectionName = @"sbss"; 288 | section.pureDataSection = YES; 289 | section.zeroFillSection = YES; 290 | segment.readable = YES; 291 | segment.writable = YES; 292 | segment.executable = NO; 293 | NSLog(@"Create section %@ of %d bytes at [0x%x;]", 294 | section.sectionName, bssLength, bssLocation); 295 | } 296 | } 297 | 298 | file.cpuFamily = @"ppc32"; 299 | file.cpuSubFamily = @"gecko"; 300 | [file setAddressSpaceWidthInBits:32]; 301 | 302 | uint32_t entryPoint = OSReadBigInt32(bytes, 0xE0); 303 | [file addEntryPoint:entryPoint]; 304 | 305 | if (((NSObject*)fileType.additionalParameters[0]).isChecked) { 306 | for (NSObject *seg in file.segments) { 307 | if (seg.readable && !seg.executable) { 308 | for (uint64_t offset = 0; offset < seg.fileLength; offset += 4) { 309 | uint32_t data = OSReadBigInt32(bytes, seg.fileOffset + offset); 310 | if (data >= 0x80000000 && data <= 0x8C000000) { 311 | Address addr = seg.startAddress + offset; 312 | [file setType:Type_Int32 atVirtualAddress:addr forLength:4]; 313 | [file setFormat:Format_Address forArgument:0 atVirtualAddress:addr]; 314 | [seg addReferencesToAddress:data fromAddress:addr]; 315 | } 316 | } 317 | } 318 | } 319 | } 320 | 321 | return DIS_OK; 322 | } 323 | 324 | - (void)fixupRebasedFile:(NSObject *)file withSlide:(int64_t)slide originalFileData:(NSData *)fileData { 325 | 326 | } 327 | 328 | - (FileLoaderLoadingStatus)loadDebugData:(NSData *)data forFile:(NSObject *)file usingCallback:(FileLoadingCallbackInfo)callback { 329 | char* mutData = malloc(data.length + 1); 330 | [data getBytes:mutData length:data.length]; 331 | mutData[data.length] = '\0'; 332 | const char* sep = "\n"; 333 | const char* sep2 = " "; 334 | char *line, *word, *brkt, *brkb; 335 | int wordIdx; 336 | for (line = strtok_r(mutData, sep, &brkt); 337 | line; 338 | line = strtok_r(NULL, sep, &brkt)) 339 | { 340 | Address address; 341 | const char* type; 342 | long arrCount; 343 | 344 | for (word = strtok_r(line, sep2, &brkb), wordIdx = 0; 345 | word; 346 | word = strtok_r(NULL, sep2, &brkb), ++wordIdx) 347 | { 348 | switch (wordIdx) 349 | { 350 | case 0: 351 | address = (Address)strtoul(word, NULL, 16); 352 | break; 353 | case 1: 354 | type = word; 355 | break; 356 | case 2: 357 | arrCount = strtol(word, NULL, 16); 358 | break; 359 | } 360 | if (wordIdx == 2) { 361 | word += strlen(word) + 1; 362 | break; 363 | } 364 | } 365 | 366 | if (!strcmp(type, "FUNC")) { 367 | if (![file hasProcedureAt:address]) 368 | [file makeProcedureAt:address]; 369 | } else if (!strcmp(type, "STR")) { 370 | [file setType:Type_ASCII atVirtualAddress:address forLength:arrCount]; 371 | } else if (!strcmp(type, "WSTR")) { 372 | [file setType:Type_Unicode atVirtualAddress:address forLength:arrCount]; 373 | } else if (!strcmp(type, "BYTE")) { 374 | [file setType:Type_Int8 atVirtualAddress:address forLength:(arrCount ? arrCount : 1) * 1]; 375 | } else if (!strcmp(type, "WORD")) { 376 | [file setType:Type_Int16 atVirtualAddress:address forLength:(arrCount ? arrCount : 1) * 2]; 377 | } else if (!strcmp(type, "DWORD")) { 378 | [file setType:Type_Int32 atVirtualAddress:address forLength:(arrCount ? arrCount : 1) * 4]; 379 | } else if (!strcmp(type, "QWORD")) { 380 | [file setType:Type_Int64 atVirtualAddress:address forLength:(arrCount ? arrCount : 1) * 8]; 381 | } else if (!strcmp(type, "FLOAT")) { 382 | [file setType:Type_Int32 atVirtualAddress:address forLength:(arrCount ? arrCount : 1) * 4]; 383 | [file setFormat:Format_Float forArgument:0 atVirtualAddress:address]; 384 | } else if (!strcmp(type, "DOUBLE")) { 385 | [file setType:Type_Int64 atVirtualAddress:address forLength:(arrCount ? arrCount : 1) * 8]; 386 | [file setFormat:Format_Float forArgument:0 atVirtualAddress:address]; 387 | } else if (!strcmp(type, "LVAR")) { 388 | NSObject *proc = [file procedureAt:address]; 389 | if (!proc) 390 | proc = [file makeProcedureAt:address]; 391 | [proc setVariableName:@(word) forDisplacement:arrCount]; 392 | continue; 393 | } else if (!strcmp(type, "COMM")) { 394 | [file setInlineComment:@(word) atVirtualAddress:address reason:CCReason_User]; 395 | continue; 396 | } 397 | [file setName:@(word) forVirtualAddress:address reason:NCReason_User]; 398 | } 399 | free(mutData); 400 | return DIS_OK; 401 | } 402 | 403 | - (NSData *)extractFromData:(NSData *)data usingDetectedFileType:(NSObject *)fileType returnAdjustOffset:(uint64_t *)adjustOffset { 404 | return nil; 405 | } 406 | 407 | @end 408 | -------------------------------------------------------------------------------- /DOLLoader/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | include $(GNUSTEP_MAKEFILES)/common.make 2 | 3 | COMMON_OBJC_FLAGS = -I../include -DLINUX -Wno-format -fblocks -fobjc-nonfragile-abi -fobjc-arc -O3 4 | 5 | BUNDLE_NAME = PPCCPU DOLLoader RELLinker 6 | 7 | PPCCPU_OBJC_FILES = PPCCPU/PPCCPU.m PPCCPU/PPCCtx.m PPCCPU/ppcd/ppcd.m 8 | PPCCPU_OBJCFLAGS=$(COMMON_OBJC_FLAGS) 9 | 10 | DOL_Loader_OBJC_FILES = DOLLoader/DOLLoader.m 11 | DOL_Loader_OBJCFLAGS=$(COMMON_OBJC_FLAGS) 12 | 13 | REL_Linker_OBJC_FILES = RELLinker/RELLinker.m 14 | REL_Linker_OBJCFLAGS=$(COMMON_OBJC_FLAGS) 15 | 16 | include $(GNUSTEP_MAKEFILES)/bundle.make 17 | 18 | -------------------------------------------------------------------------------- /PPCCPU.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 07F966D818C5BA1200B394AF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 07F966D718C5BA1200B394AF /* CoreFoundation.framework */; }; 11 | 07F966DE18C5BA1200B394AF /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 07F966DC18C5BA1200B394AF /* InfoPlist.strings */; }; 12 | 07F966E718C5BA6500B394AF /* PPCCPU.m in Sources */ = {isa = PBXBuildFile; fileRef = 07F966E618C5BA6500B394AF /* PPCCPU.m */; }; 13 | 07F966EB18C5BAFE00B394AF /* PPCCtx.m in Sources */ = {isa = PBXBuildFile; fileRef = 07F966EA18C5BAFE00B394AF /* PPCCtx.m */; }; 14 | 9E4002FA1BED8FBE0063E512 /* ppcd.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E4002F71BED8FBE0063E512 /* ppcd.m */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | 07F966D418C5BA1200B394AF /* PPCCPU.hopperCPU */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PPCCPU.hopperCPU; sourceTree = BUILT_PRODUCTS_DIR; }; 19 | 07F966D718C5BA1200B394AF /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 20 | 07F966DB18C5BA1200B394AF /* PPCCPU-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "PPCCPU-Info.plist"; sourceTree = ""; }; 21 | 07F966DD18C5BA1200B394AF /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 22 | 07F966DF18C5BA1200B394AF /* PPCCPU-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = "PPCCPU-Prefix.pch"; sourceTree = ""; }; 23 | 07F966E518C5BA6500B394AF /* PPCCPU.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PPCCPU.h; sourceTree = ""; }; 24 | 07F966E618C5BA6500B394AF /* PPCCPU.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PPCCPU.m; sourceTree = ""; }; 25 | 07F966E918C5BAFE00B394AF /* PPCCtx.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = PPCCtx.h; sourceTree = ""; }; 26 | 07F966EA18C5BAFE00B394AF /* PPCCtx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = PPCCtx.m; sourceTree = ""; }; 27 | 9E4002F51BED8FBE0063E512 /* CommonDefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = CommonDefs.h; path = ppcd/CommonDefs.h; sourceTree = ""; }; 28 | 9E4002F61BED8FBE0063E512 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; name = main.cpp; path = ppcd/main.cpp; sourceTree = ""; }; 29 | 9E4002F71BED8FBE0063E512 /* ppcd.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = ppcd.m; path = ppcd/ppcd.m; sourceTree = ""; }; 30 | 9E4002F81BED8FBE0063E512 /* ppcd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = ppcd.h; path = ppcd/ppcd.h; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 07F966D118C5BA1200B394AF /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | 07F966D818C5BA1200B394AF /* CoreFoundation.framework in Frameworks */, 39 | ); 40 | runOnlyForDeploymentPostprocessing = 0; 41 | }; 42 | /* End PBXFrameworksBuildPhase section */ 43 | 44 | /* Begin PBXGroup section */ 45 | 07F966CB18C5BA1200B394AF = { 46 | isa = PBXGroup; 47 | children = ( 48 | 07F966D918C5BA1200B394AF /* PPCCPU */, 49 | 07F966DA18C5BA1200B394AF /* Supporting Files */, 50 | 07F966D618C5BA1200B394AF /* Frameworks */, 51 | 07F966D518C5BA1200B394AF /* Products */, 52 | ); 53 | sourceTree = ""; 54 | }; 55 | 07F966D518C5BA1200B394AF /* Products */ = { 56 | isa = PBXGroup; 57 | children = ( 58 | 07F966D418C5BA1200B394AF /* PPCCPU.hopperCPU */, 59 | ); 60 | name = Products; 61 | sourceTree = ""; 62 | }; 63 | 07F966D618C5BA1200B394AF /* Frameworks */ = { 64 | isa = PBXGroup; 65 | children = ( 66 | 07F966D718C5BA1200B394AF /* CoreFoundation.framework */, 67 | ); 68 | name = Frameworks; 69 | sourceTree = ""; 70 | }; 71 | 07F966D918C5BA1200B394AF /* PPCCPU */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 9E4002F41BED8FB60063E512 /* ppcd */, 75 | 07F966E518C5BA6500B394AF /* PPCCPU.h */, 76 | 07F966E618C5BA6500B394AF /* PPCCPU.m */, 77 | 07F966E918C5BAFE00B394AF /* PPCCtx.h */, 78 | 07F966EA18C5BAFE00B394AF /* PPCCtx.m */, 79 | ); 80 | path = PPCCPU; 81 | sourceTree = ""; 82 | }; 83 | 07F966DA18C5BA1200B394AF /* Supporting Files */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 07F966DB18C5BA1200B394AF /* PPCCPU-Info.plist */, 87 | 07F966DC18C5BA1200B394AF /* InfoPlist.strings */, 88 | 07F966DF18C5BA1200B394AF /* PPCCPU-Prefix.pch */, 89 | ); 90 | name = "Supporting Files"; 91 | path = PPCCPU; 92 | sourceTree = ""; 93 | }; 94 | 9E4002F41BED8FB60063E512 /* ppcd */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 9E4002F51BED8FBE0063E512 /* CommonDefs.h */, 98 | 9E4002F61BED8FBE0063E512 /* main.cpp */, 99 | 9E4002F71BED8FBE0063E512 /* ppcd.m */, 100 | 9E4002F81BED8FBE0063E512 /* ppcd.h */, 101 | ); 102 | name = ppcd; 103 | sourceTree = ""; 104 | }; 105 | /* End PBXGroup section */ 106 | 107 | /* Begin PBXNativeTarget section */ 108 | 07F966D318C5BA1200B394AF /* PPCCPU */ = { 109 | isa = PBXNativeTarget; 110 | buildConfigurationList = 07F966E218C5BA1200B394AF /* Build configuration list for PBXNativeTarget "PPCCPU" */; 111 | buildPhases = ( 112 | 07F966D018C5BA1200B394AF /* Sources */, 113 | 07F966D118C5BA1200B394AF /* Frameworks */, 114 | 07F966D218C5BA1200B394AF /* Resources */, 115 | 07F966E818C5BA8B00B394AF /* Run Script (Installation) */, 116 | ); 117 | buildRules = ( 118 | ); 119 | dependencies = ( 120 | ); 121 | name = PPCCPU; 122 | productName = PPCCPU; 123 | productReference = 07F966D418C5BA1200B394AF /* PPCCPU.hopperCPU */; 124 | productType = "com.apple.product-type.bundle"; 125 | }; 126 | /* End PBXNativeTarget section */ 127 | 128 | /* Begin PBXProject section */ 129 | 07F966CC18C5BA1200B394AF /* Project object */ = { 130 | isa = PBXProject; 131 | attributes = { 132 | LastUpgradeCheck = 0800; 133 | ORGANIZATIONNAME = com.sappharad; 134 | }; 135 | buildConfigurationList = 07F966CF18C5BA1200B394AF /* Build configuration list for PBXProject "PPCCPU" */; 136 | compatibilityVersion = "Xcode 3.2"; 137 | developmentRegion = English; 138 | hasScannedForEncodings = 0; 139 | knownRegions = ( 140 | en, 141 | ); 142 | mainGroup = 07F966CB18C5BA1200B394AF; 143 | productRefGroup = 07F966D518C5BA1200B394AF /* Products */; 144 | projectDirPath = ""; 145 | projectRoot = ""; 146 | targets = ( 147 | 07F966D318C5BA1200B394AF /* PPCCPU */, 148 | ); 149 | }; 150 | /* End PBXProject section */ 151 | 152 | /* Begin PBXResourcesBuildPhase section */ 153 | 07F966D218C5BA1200B394AF /* Resources */ = { 154 | isa = PBXResourcesBuildPhase; 155 | buildActionMask = 2147483647; 156 | files = ( 157 | 07F966DE18C5BA1200B394AF /* InfoPlist.strings in Resources */, 158 | ); 159 | runOnlyForDeploymentPostprocessing = 0; 160 | }; 161 | /* End PBXResourcesBuildPhase section */ 162 | 163 | /* Begin PBXShellScriptBuildPhase section */ 164 | 07F966E818C5BA8B00B394AF /* Run Script (Installation) */ = { 165 | isa = PBXShellScriptBuildPhase; 166 | buildActionMask = 2147483647; 167 | files = ( 168 | ); 169 | inputPaths = ( 170 | ); 171 | name = "Run Script (Installation)"; 172 | outputPaths = ( 173 | ); 174 | runOnlyForDeploymentPostprocessing = 0; 175 | shellPath = /bin/sh; 176 | shellScript = "rm -rf \"${INSTALL_PATH}/PPCCPU.hopperCPU\"\nmkdir -p \"${INSTALL_PATH}\"\ncp -rf \"${BUILT_PRODUCTS_DIR}/PPCCPU.hopperCPU\" \"${INSTALL_PATH}\"\n"; 177 | }; 178 | /* End PBXShellScriptBuildPhase section */ 179 | 180 | /* Begin PBXSourcesBuildPhase section */ 181 | 07F966D018C5BA1200B394AF /* Sources */ = { 182 | isa = PBXSourcesBuildPhase; 183 | buildActionMask = 2147483647; 184 | files = ( 185 | 9E4002FA1BED8FBE0063E512 /* ppcd.m in Sources */, 186 | 07F966E718C5BA6500B394AF /* PPCCPU.m in Sources */, 187 | 07F966EB18C5BAFE00B394AF /* PPCCtx.m in Sources */, 188 | ); 189 | runOnlyForDeploymentPostprocessing = 0; 190 | }; 191 | /* End PBXSourcesBuildPhase section */ 192 | 193 | /* Begin PBXVariantGroup section */ 194 | 07F966DC18C5BA1200B394AF /* InfoPlist.strings */ = { 195 | isa = PBXVariantGroup; 196 | children = ( 197 | 07F966DD18C5BA1200B394AF /* en */, 198 | ); 199 | name = InfoPlist.strings; 200 | sourceTree = ""; 201 | }; 202 | /* End PBXVariantGroup section */ 203 | 204 | /* Begin XCBuildConfiguration section */ 205 | 07F966E018C5BA1200B394AF /* Debug */ = { 206 | isa = XCBuildConfiguration; 207 | buildSettings = { 208 | ALWAYS_SEARCH_USER_PATHS = NO; 209 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 210 | CLANG_CXX_LIBRARY = "libc++"; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_WARN_BOOL_CONVERSION = YES; 213 | CLANG_WARN_CONSTANT_CONVERSION = YES; 214 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 215 | CLANG_WARN_EMPTY_BODY = YES; 216 | CLANG_WARN_ENUM_CONVERSION = YES; 217 | CLANG_WARN_INT_CONVERSION = YES; 218 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 219 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 220 | COPY_PHASE_STRIP = NO; 221 | GCC_C_LANGUAGE_STANDARD = gnu99; 222 | GCC_DYNAMIC_NO_PIC = NO; 223 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 224 | GCC_OPTIMIZATION_LEVEL = 0; 225 | GCC_PREPROCESSOR_DEFINITIONS = ( 226 | "DEBUG=1", 227 | "$(inherited)", 228 | ); 229 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 230 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 231 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 232 | GCC_WARN_UNDECLARED_SELECTOR = YES; 233 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 234 | GCC_WARN_UNUSED_FUNCTION = YES; 235 | GCC_WARN_UNUSED_VARIABLE = YES; 236 | MACOSX_DEPLOYMENT_TARGET = 10.9; 237 | ONLY_ACTIVE_ARCH = YES; 238 | SDKROOT = macosx; 239 | }; 240 | name = Debug; 241 | }; 242 | 07F966E118C5BA1200B394AF /* Release */ = { 243 | isa = XCBuildConfiguration; 244 | buildSettings = { 245 | ALWAYS_SEARCH_USER_PATHS = NO; 246 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 247 | CLANG_CXX_LIBRARY = "libc++"; 248 | CLANG_ENABLE_OBJC_ARC = YES; 249 | CLANG_WARN_BOOL_CONVERSION = YES; 250 | CLANG_WARN_CONSTANT_CONVERSION = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INT_CONVERSION = YES; 255 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 256 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 257 | COPY_PHASE_STRIP = YES; 258 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 259 | ENABLE_NS_ASSERTIONS = NO; 260 | GCC_C_LANGUAGE_STANDARD = gnu99; 261 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 262 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 263 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 264 | GCC_WARN_UNDECLARED_SELECTOR = YES; 265 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 266 | GCC_WARN_UNUSED_FUNCTION = YES; 267 | GCC_WARN_UNUSED_VARIABLE = YES; 268 | MACOSX_DEPLOYMENT_TARGET = 10.9; 269 | SDKROOT = macosx; 270 | }; 271 | name = Release; 272 | }; 273 | 07F966E318C5BA1200B394AF /* Debug */ = { 274 | isa = XCBuildConfiguration; 275 | buildSettings = { 276 | COMBINE_HIDPI_IMAGES = YES; 277 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 278 | GCC_PREFIX_HEADER = "PPCCPU/PPCCPU-Prefix.pch"; 279 | GCC_PREPROCESSOR_DEFINITIONS = "DEBUG=1"; 280 | HEADER_SEARCH_PATHS = ( 281 | "$(inherited)", 282 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 283 | "$(SRCROOT)/../include", 284 | "$(SRCROOT)/PPCCPU", 285 | ); 286 | INFOPLIST_FILE = "PPCCPU/PPCCPU-Info.plist"; 287 | INSTALL_PATH = "$(USER_LIBRARY_DIR)/Application Support/Hopper/PlugIns/v4/CPUs"; 288 | PRODUCT_BUNDLE_IDENTIFIER = com.sappharad.PPCCPU; 289 | PRODUCT_NAME = "$(TARGET_NAME)"; 290 | WRAPPER_EXTENSION = hopperCPU; 291 | }; 292 | name = Debug; 293 | }; 294 | 07F966E418C5BA1200B394AF /* Release */ = { 295 | isa = XCBuildConfiguration; 296 | buildSettings = { 297 | COMBINE_HIDPI_IMAGES = YES; 298 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 299 | GCC_PREFIX_HEADER = "PPCCPU/PPCCPU-Prefix.pch"; 300 | GCC_PREPROCESSOR_DEFINITIONS = ""; 301 | HEADER_SEARCH_PATHS = ( 302 | "$(inherited)", 303 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 304 | "$(SRCROOT)/../include", 305 | "$(SRCROOT)/PPCCPU", 306 | ); 307 | INFOPLIST_FILE = "PPCCPU/PPCCPU-Info.plist"; 308 | INSTALL_PATH = "$(USER_LIBRARY_DIR)/Application Support/Hopper/PlugIns/v4/CPUs"; 309 | PRODUCT_BUNDLE_IDENTIFIER = com.sappharad.PPCCPU; 310 | PRODUCT_NAME = "$(TARGET_NAME)"; 311 | WRAPPER_EXTENSION = hopperCPU; 312 | }; 313 | name = Release; 314 | }; 315 | /* End XCBuildConfiguration section */ 316 | 317 | /* Begin XCConfigurationList section */ 318 | 07F966CF18C5BA1200B394AF /* Build configuration list for PBXProject "PPCCPU" */ = { 319 | isa = XCConfigurationList; 320 | buildConfigurations = ( 321 | 07F966E018C5BA1200B394AF /* Debug */, 322 | 07F966E118C5BA1200B394AF /* Release */, 323 | ); 324 | defaultConfigurationIsVisible = 0; 325 | defaultConfigurationName = Release; 326 | }; 327 | 07F966E218C5BA1200B394AF /* Build configuration list for PBXNativeTarget "PPCCPU" */ = { 328 | isa = XCConfigurationList; 329 | buildConfigurations = ( 330 | 07F966E318C5BA1200B394AF /* Debug */, 331 | 07F966E418C5BA1200B394AF /* Release */, 332 | ); 333 | defaultConfigurationIsVisible = 0; 334 | defaultConfigurationName = Release; 335 | }; 336 | /* End XCConfigurationList section */ 337 | }; 338 | rootObject = 07F966CC18C5BA1200B394AF /* Project object */; 339 | } 340 | -------------------------------------------------------------------------------- /PPCCPU.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PPCCPU.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /PPCCPU/PPCCPU-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | CFPlugInDynamicRegisterFunction 26 | 27 | CFPlugInDynamicRegistration 28 | NO 29 | CFPlugInFactories 30 | 31 | 00000000-0000-0000-0000-000000000000 32 | MyFactoryFunction 33 | 34 | CFPlugInTypes 35 | 36 | 00000000-0000-0000-0000-000000000000 37 | 38 | 00000000-0000-0000-0000-000000000000 39 | 40 | 41 | CFPlugInUnloadFunction 42 | 43 | NSHumanReadableCopyright 44 | Copyright © 2015 PK and Others. All rights reserved. 45 | NSPrincipalClass 46 | PPCCPU 47 | 48 | 49 | -------------------------------------------------------------------------------- /PPCCPU/PPCCPU-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /PPCCPU/PPCCPU.h: -------------------------------------------------------------------------------- 1 | // 2 | // PPCCPU.h 3 | // PPCCPU 4 | // 5 | // Created by copy/pasting an example on 11/06/2015. 6 | // Copyright (c) 2015 PK and others. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "ppcd/CommonDefs.h" 12 | 13 | @interface PPCCPU : NSObject 14 | 15 | - (NSObject *)hopperServices; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /PPCCPU/PPCCPU.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPCCPU.m 3 | // PPCCPU 4 | // 5 | // Created by copy/pasting an example on 11/06/2015. 6 | // Copyright (c) 2015 PK and others. All rights reserved. 7 | // 8 | 9 | #import "PPCCPU.h" 10 | #import "PPCCtx.h" 11 | 12 | #ifdef LINUX 13 | #include 14 | 15 | static int16_t OSReadBigInt16(const void *address, uintptr_t offset) { 16 | return be16toh(*(int16_t *) ((uintptr_t) address + offset)); 17 | } 18 | 19 | static int32_t OSReadBigInt32(const void *address, uintptr_t offset) { 20 | return be32toh(*(int32_t *) ((uintptr_t) address + offset)); 21 | } 22 | 23 | static void OSWriteBigInt32(void *address, uintptr_t offset, int32_t data) { 24 | *(int32_t *) ((uintptr_t) address + offset) = htobe32(data); 25 | } 26 | 27 | #endif 28 | 29 | @implementation PPCCPU { 30 | NSObject *_services; 31 | } 32 | 33 | - (instancetype)initWithHopperServices:(NSObject *)services { 34 | if (self = [super init]) { 35 | _services = services; 36 | } 37 | return self; 38 | } 39 | 40 | - (NSObject *)hopperServices { 41 | return _services; 42 | } 43 | 44 | - (Class)cpuContextClass { 45 | return [PPCCtx class]; 46 | } 47 | 48 | - (NSObject *)buildCPUContextForFile:(NSObject *)file { 49 | return [[PPCCtx alloc] initWithCPU:self andFile:file]; 50 | } 51 | 52 | - (HopperUUID *)pluginUUID { 53 | return [_services UUIDWithString:@"b9d7440a-2ad6-465b-a1e7-8696cf571a69"]; 54 | } 55 | 56 | - (HopperPluginType)pluginType { 57 | return Plugin_CPU; 58 | } 59 | 60 | - (NSString *)pluginName { 61 | return @"PowerPC Gecko"; 62 | } 63 | 64 | - (NSString *)pluginDescription { 65 | return @"PowerPC Gecko CPU support"; 66 | } 67 | 68 | - (NSString *)pluginAuthor { 69 | return @"Paul Kratt and Jack Andersen - Based on code by org, whoever that is"; 70 | } 71 | 72 | - (NSString *)pluginCopyright { 73 | return @"© Paul Kratt and Jack Andersen"; 74 | } 75 | 76 | - (NSArray *)cpuFamilies { 77 | return @[@"ppc32"]; 78 | } 79 | 80 | - (NSString *)pluginVersion { 81 | return @"0.0.2"; 82 | } 83 | 84 | - (NSString *)commandLineIdentifier { 85 | return @"Gecko"; 86 | } 87 | 88 | - (NSArray *)cpuSubFamiliesForFamily:(NSString *)family { 89 | if ([family isEqualToString:@"ppc32"]) return @[@"gecko"]; 90 | return nil; 91 | } 92 | 93 | - (int)addressSpaceWidthInBitsForCPUFamily:(NSString *)family andSubFamily:(NSString *)subFamily { 94 | if ([family isEqualToString:@"ppc32"] && [subFamily isEqualToString:@"gecko"]) return 32; 95 | return 0; 96 | } 97 | 98 | - (CPUEndianess)endianess { 99 | return CPUEndianess_Big; 100 | } 101 | 102 | - (NSUInteger)syntaxVariantCount { 103 | return 1; 104 | } 105 | 106 | - (NSUInteger)cpuModeCount { 107 | return 1; 108 | } 109 | 110 | - (NSArray *)syntaxVariantNames { 111 | return @[@"generic"]; 112 | } 113 | 114 | - (NSArray *)cpuModeNames { 115 | return @[@"generic"]; 116 | } 117 | 118 | - (NSString *)framePointerRegisterNameForFile:(NSObject*)file cpuMode:(uint8_t)cpuMode { 119 | return nil; 120 | } 121 | 122 | - (NSUInteger)registerClassCount { 123 | return RegClass_PPC_Cnt; 124 | } 125 | 126 | - (NSUInteger)registerCountForClass:(RegClass)reg_class { 127 | switch (reg_class) { 128 | case RegClass_CPUState: return 1; 129 | case RegClass_GeneralPurposeRegister: return 32; 130 | case RegClass_FPRegister: return 32; 131 | case RegClass_PPC_Cnt: return 1; 132 | case RegClass_PPC_CondReg: return 8; 133 | default: break; 134 | } 135 | return 0; 136 | } 137 | 138 | - (BOOL)registerIndexIsStackPointer:(NSUInteger)reg ofClass:(RegClass)reg_class cpuMode:(uint8_t)cpuMode file:(NSObject *)file { 139 | return reg_class == RegClass_GeneralPurposeRegister && reg == 1; 140 | } 141 | 142 | - (BOOL)registerIndexIsFrameBasePointer:(NSUInteger)reg ofClass:(RegClass)reg_class cpuMode:(uint8_t)cpuMode file:(NSObject *)file { 143 | return NO; 144 | } 145 | 146 | - (BOOL)registerIndexIsProgramCounter:(NSUInteger)reg cpuMode:(uint8_t)cpuMode file:(NSObject *)file { 147 | return NO; 148 | } 149 | 150 | - (BOOL)registerHasSideEffectForIndex:(NSUInteger)reg andClass:(RegClass)reg_class { 151 | return NO; 152 | } 153 | 154 | - (NSString *)registerIndexToString:(NSUInteger)reg ofClass:(RegClass)reg_class withBitSize:(NSUInteger)size position:(DisasmPosition)position andSyntaxIndex:(NSUInteger)syntaxIndex { 155 | switch (reg_class) { 156 | case RegClass_CPUState: return @"CCR"; 157 | case RegClass_GeneralPurposeRegister: return [NSString stringWithFormat:@"r%lu", (unsigned long)reg]; 158 | case RegClass_FPRegister: return [NSString stringWithFormat:@"f%lu", (unsigned long)reg]; 159 | case RegClass_PPC_Cnt: return @"count"; 160 | case RegClass_PPC_CondReg: return [NSString stringWithFormat:@"cr%lu", (unsigned long)reg]; 161 | default: break; 162 | } 163 | return nil; 164 | } 165 | 166 | - (NSString *)cpuRegisterStateMaskToString:(uint32_t)cpuState { 167 | return @""; 168 | } 169 | 170 | - (NSUInteger)translateOperandIndex:(NSUInteger)index operandCount:(NSUInteger)count accordingToSyntax:(uint8_t)syntaxIndex { 171 | return index; 172 | } 173 | 174 | - (NSData *)nopWithSize:(NSUInteger)size andMode:(NSUInteger)cpuMode forFile:(NSObject *)file { 175 | // Instruction size is always a multiple of 4 176 | if (size & 3) return nil; 177 | NSMutableData *nopArray = [[NSMutableData alloc] initWithCapacity:size]; 178 | [nopArray setLength:size]; 179 | uint16_t *ptr = (uint16_t *)[nopArray mutableBytes]; 180 | for (NSUInteger i=0; i 10 | #import 11 | 12 | @class PPCCPU; 13 | 14 | @interface PPCCtx : NSObject 15 | 16 | - (instancetype)initWithCPU:(PPCCPU *)cpu andFile:(NSObject *)file; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /PPCCPU/PPCCtx.m: -------------------------------------------------------------------------------- 1 | // 2 | // PPCCtx.m 3 | // PPCCPU 4 | // 5 | // Created by copy/pasting an example on 11/06/2015. 6 | // Copyright (c) 2015 PK and others. All rights reserved. 7 | // 8 | 9 | #import "PPCCtx.h" 10 | #import "PPCCPU.h" 11 | #import 12 | #import 13 | #import 14 | #import "ppcd/CommonDefs.h" 15 | #import "ppcd/ppcd.h" 16 | 17 | struct TypeSet { 18 | Address addr; 19 | u32 size; 20 | ArgFormat format; 21 | }; 22 | 23 | @implementation PPCCtx { 24 | PPCCPU *_cpu; 25 | NSObject *_file; 26 | bool _trackingLis; 27 | int32_t lisArr[32]; 28 | double mulhwArr[32]; 29 | int32_t dividendArr[32]; 30 | int32_t stackDisp; 31 | int32_t indexBaseArr[32]; 32 | int32_t indexBaseCTR; 33 | int32_t lastCmplwi; 34 | Address foundSDA, foundSDA2; 35 | NSMutableDictionary* localLabels; 36 | 37 | uint64_t typesToSetCapacity; 38 | uint64_t typesToSetCount; 39 | struct TypeSet* typesToSet; 40 | } 41 | 42 | - (instancetype)initWithCPU:(PPCCPU *)cpu andFile:(NSObject *)file { 43 | if (self = [super init]) { 44 | _cpu = cpu; 45 | _file = file; 46 | _trackingLis = false; 47 | for (int i = 0; i < 32; ++i) { 48 | lisArr[i] = ~0; 49 | mulhwArr[i] = 0.0; 50 | dividendArr[i] = ~0; 51 | indexBaseArr[i] = ~0; 52 | } 53 | indexBaseCTR = ~0; 54 | stackDisp = 0; 55 | lastCmplwi = 0; 56 | foundSDA = BAD_ADDRESS; 57 | foundSDA2 = BAD_ADDRESS; 58 | localLabels = [NSMutableDictionary new]; 59 | 60 | typesToSetCapacity = 256; 61 | typesToSetCount = 0; 62 | typesToSet = malloc(sizeof(struct TypeSet) * typesToSetCapacity); 63 | } 64 | return self; 65 | } 66 | 67 | - (void)dealloc { 68 | free(typesToSet); 69 | } 70 | 71 | - (void)addTypeToSet:(Address)addr size:(u32)size format:(ArgFormat)format { 72 | if (typesToSetCount == typesToSetCapacity) { 73 | typesToSetCapacity *= 2; 74 | typesToSet = realloc(typesToSet, sizeof(struct TypeSet) * typesToSetCapacity); 75 | } 76 | struct TypeSet* storage = &typesToSet[typesToSetCount]; 77 | storage->addr = addr; 78 | storage->size = size; 79 | storage->format = format; 80 | ++typesToSetCount; 81 | } 82 | 83 | - (NSObject *)cpuDefinition { 84 | return _cpu; 85 | } 86 | 87 | - (void)initDisasmStructure:(DisasmStruct *)disasm withSyntaxIndex:(NSUInteger)syntaxIndex { 88 | bzero(disasm, sizeof(DisasmStruct)); 89 | } 90 | 91 | // Analysis 92 | 93 | - (Address)adjustCodeAddress:(Address)address { 94 | // Instructions are always aligned to a multiple of 4. 95 | return address & ~3; 96 | } 97 | 98 | - (uint8_t)cpuModeFromAddress:(Address)address { 99 | return 0; 100 | } 101 | 102 | - (BOOL)addressForcesACPUMode:(Address)address { 103 | return NO; 104 | } 105 | 106 | - (Address)nextAddressToTryIfInstructionFailedToDecodeAt:(Address)address forCPUMode:(uint8_t)mode { 107 | return ((address & ~3) + 4); 108 | } 109 | 110 | - (int)isNopAt:(Address)address { 111 | uint32_t word = [_file readUInt32AtVirtualAddress:address]; 112 | return (word == 0x60000000) ? 4 : 0; 113 | } 114 | 115 | - (BOOL)hasProcedurePrologAt:(Address)address { 116 | // procedures usually begin with a "stwu r1, -X(r1)" or "blr" instruction 117 | uint32_t word = [_file readUInt32AtVirtualAddress:address]; 118 | return (word & 0xffff8000) == 0x94218000 || word == 0x4e800020; 119 | } 120 | 121 | - (NSUInteger)detectedPaddingLengthAt:(Address)address { 122 | NSUInteger len = 0; 123 | Address endAddr = _file.lastSegment.endAddress; 124 | while (address < endAddr && [_file readUInt32AtVirtualAddress:address] == 0) { 125 | address += 4; 126 | len += 4; 127 | } 128 | return len; 129 | } 130 | 131 | - (void)analysisBeginsAt:(Address)entryPoint { 132 | //printf("analysisBeginsAt\n"); 133 | NSObject *ctors = [_file sectionNamed:@"ctors"]; 134 | if (ctors) { 135 | for (Address addr = ctors.startAddress; addr < ctors.endAddress; addr += 4) { 136 | [_file setType:Type_Int32 atVirtualAddress:addr forLength:4]; 137 | [_file setFormat:Format_Address forArgument:0 atVirtualAddress:addr]; 138 | } 139 | } 140 | NSObject *dtors = [_file sectionNamed:@"dtors"]; 141 | if (dtors) { 142 | for (Address addr = dtors.startAddress; addr < dtors.endAddress; addr += 4) { 143 | [_file setType:Type_Int32 atVirtualAddress:addr forLength:4]; 144 | [_file setFormat:Format_Address forArgument:0 atVirtualAddress:addr]; 145 | } 146 | } 147 | } 148 | 149 | - (void)procedureAnalysisBeginsForProcedure:(NSObject *)procedure atEntryPoint:(Address)entryPoint { 150 | //printf("procedureAnalysisBeginsForProcedure %p\n", procedure); 151 | _trackingLis = true; 152 | } 153 | 154 | - (void)performProcedureAnalysis:(NSObject *)procedure basicBlock:(NSObject *)basicBlock disasm:(DisasmStruct *)disasm { 155 | //printf("performProcedureAnalysis %p\n", procedure); 156 | } 157 | 158 | static NSString* MakeNumericComment(u32 val) 159 | { 160 | for (int i = 0; i < 4; ++i) { 161 | u8 b = (u8)(val >> (i * 8)); 162 | if (b < 32 || b > 126) 163 | return [NSString stringWithFormat:@"0x%08X", val]; 164 | } 165 | u32 bigVal = __builtin_bswap32(val); 166 | return [NSString stringWithFormat:@"0x%08X '%.4s'", val, (char*)&bigVal]; 167 | } 168 | 169 | static ByteType TypeForSize(u32 size) 170 | { 171 | switch (size) 172 | { 173 | case 1: 174 | default: 175 | return Type_Int8; 176 | case 2: 177 | return Type_Int16; 178 | case 4: 179 | return Type_Int32; 180 | case 8: 181 | return Type_Int64; 182 | } 183 | } 184 | 185 | - (void)performInstructionSpecificAnalysis:(DisasmStruct *)disasm forProcedure:(NSObject *)procedure inSegment:(NSObject *)segment { 186 | //printf("performInstructionSpecificAnalysis %08X %s %p\n", (u32)disasm->virtualAddr, disasm->instruction.mnemonic, procedure); 187 | 188 | // LIS/ADDI resolved address 189 | for (int i = 0; i < DISASM_MAX_OPERANDS; ++i) { 190 | DisasmOperand *operand = disasm->operand + i; 191 | if (operand->userData[0] & DISASM_PPC_OPER_LIS_ADDI) { 192 | if ([_file segmentForVirtualAddress:(u32)operand->userData[1]]) 193 | { 194 | [segment addReferencesToAddress:(u32)operand->userData[1] fromAddress:disasm->virtualAddr]; 195 | } 196 | else 197 | { 198 | [_file setInlineComment:MakeNumericComment((u32)operand->userData[1]) 199 | atVirtualAddress:disasm->virtualAddr reason:CCReason_Automatic]; 200 | } 201 | 202 | // SDA/SDA2 symbol synthesis 203 | if (disasm->operand[0].type & DISASM_BUILD_REGISTER_INDEX_MASK(13) && segment == _file.firstSegment) 204 | foundSDA = (u32)operand->userData[1]; 205 | else if (disasm->operand[0].type & DISASM_BUILD_REGISTER_INDEX_MASK(2) && segment == _file.firstSegment) 206 | foundSDA2 = (u32)operand->userData[1]; 207 | break; 208 | } 209 | } 210 | 211 | // Handle MULHW 212 | if (disasm->operand[2].userData[0] & DISASM_PPC_OPER_MULHW) { 213 | int32_t divConstant = (int32_t)disasm->operand[2].userData[1]; 214 | if (divConstant != ~0) { 215 | double factor = (double)divConstant / (double)(1LL << 32LL); 216 | mulhwArr[GetRegisterIndex(disasm->operand[0].type)] = factor; 217 | dividendArr[GetRegisterIndex(disasm->operand[0].type)] = GetRegisterIndex(disasm->operand[2].type); 218 | double rounded = round(1.0 / factor); 219 | if (fabs(rounded - 1.0 / factor) < 0.0001) 220 | [_file setInlineComment:[NSString stringWithFormat:@"divide by %g", rounded] 221 | atVirtualAddress:disasm->virtualAddr reason:CCReason_Automatic]; 222 | } 223 | } else if (!strcmp(disasm->instruction.mnemonic, "add")) { 224 | int32_t dividend = dividendArr[GetRegisterIndex(disasm->operand[1].type)]; 225 | if (dividend == GetRegisterIndex(disasm->operand[2].type)) { 226 | double factor = mulhwArr[GetRegisterIndex(disasm->operand[1].type)]; 227 | factor += 1.0; 228 | mulhwArr[GetRegisterIndex(disasm->operand[0].type)] = factor; 229 | dividendArr[GetRegisterIndex(disasm->operand[0].type)] = dividend; 230 | double rounded = round(1.0 / factor); 231 | if (fabs(rounded - 1.0 / factor) < 0.0001) 232 | [_file setInlineComment:[NSString stringWithFormat:@"divide by %g", rounded] 233 | atVirtualAddress:disasm->virtualAddr reason:CCReason_Automatic]; 234 | } 235 | } else if (!strcmp(disasm->instruction.mnemonic, "srawi")) { 236 | int32_t dividend = dividendArr[GetRegisterIndex(disasm->operand[1].type)]; 237 | if (dividend != ~0) { 238 | double factor = mulhwArr[GetRegisterIndex(disasm->operand[1].type)]; 239 | factor /= (double)(1LL << disasm->operand[2].immediateValue); 240 | mulhwArr[GetRegisterIndex(disasm->operand[0].type)] = factor; 241 | double rounded = round(1.0 / factor); 242 | if (fabs(rounded - 1.0 / factor) < 0.0001) 243 | [_file setInlineComment:[NSString stringWithFormat:@"divide by %g", rounded] 244 | atVirtualAddress:disasm->virtualAddr reason:CCReason_Automatic]; 245 | } 246 | } 247 | 248 | // Stack register handling 249 | if (disasm->instruction.userData & DISASM_PPC_INST_LOAD_STORE && 250 | disasm->operand[2].type & DISASM_BUILD_REGISTER_INDEX_MASK(1)) { 251 | if (disasm->operand[0].type & DISASM_BUILD_REGISTER_INDEX_MASK(1) && 252 | !strcmp(disasm->instruction.mnemonic, "stwu")) { 253 | stackDisp = (int32_t)disasm->operand[1].immediateValue; 254 | [procedure setVariableName:@"BPpush" forDisplacement:disasm->operand[1].immediateValue]; 255 | } else { 256 | int32_t imm = (int32_t)disasm->operand[1].immediateValue + stackDisp; 257 | if (imm < 0) { 258 | [procedure setVariableName:[NSString stringWithFormat:@"var_%X", -imm] forDisplacement:disasm->operand[1].immediateValue]; 259 | } else { 260 | if (imm == 4 && disasm->instruction.mnemonic[0] == 's') 261 | [procedure setVariableName:@"LRpush" forDisplacement:disasm->operand[1].immediateValue]; 262 | else if (imm == 4 && disasm->instruction.mnemonic[0] == 'l') 263 | [procedure setVariableName:@"LR" forDisplacement:disasm->operand[1].immediateValue]; 264 | else 265 | [procedure setVariableName:[NSString stringWithFormat:@"arg_%X", imm] forDisplacement:disasm->operand[1].immediateValue]; 266 | } 267 | } 268 | } else if (disasm->instruction.userData & DISASM_PPC_INST_ADDI && 269 | disasm->operand[0].type & DISASM_BUILD_REGISTER_INDEX_MASK(1) && 270 | disasm->operand[1].type & DISASM_BUILD_REGISTER_INDEX_MASK(1)) { 271 | stackDisp += (int32_t)disasm->operand[2].immediateValue; 272 | [procedure setVariableName:@"BPpop" forDisplacement:disasm->operand[2].immediateValue]; 273 | } 274 | 275 | // Load/store handling 276 | if (disasm->instruction.userData & DISASM_PPC_INST_LOAD_STORE && 277 | disasm->instruction.addressValue && disasm->operand[2].size) { 278 | ArgFormat format = Format_Hexadecimal; 279 | if (!strcmp(disasm->instruction.mnemonic, "lwz") || !strcmp(disasm->instruction.mnemonic, "stw")) { 280 | uint32_t data = [_file readInt32AtVirtualAddress:disasm->instruction.addressValue]; 281 | if (data >= 0x80000000 && data <= 0x8C000000) 282 | format = Format_Address; 283 | } else if (!strncmp(disasm->instruction.mnemonic, "lf", 2) || !strncmp(disasm->instruction.mnemonic, "stf", 2)) 284 | format = Format_Float; 285 | [self addTypeToSet:disasm->instruction.addressValue size:disasm->operand[2].size format:format]; 286 | } 287 | 288 | // Indexed load/store handling 289 | if (disasm->instruction.userData & DISASM_PPC_INST_INDEXED_LOAD_STORE) { 290 | Address baseAddr = disasm->operand[2].userData[1]; 291 | indexBaseArr[GetRegisterIndex(disasm->operand[0].type)] = (u32)baseAddr; 292 | } 293 | 294 | if (!strcmp(disasm->instruction.mnemonic, "mtctr")) { 295 | indexBaseCTR = indexBaseArr[GetRegisterIndex(disasm->operand[0].type)]; 296 | } else if (!strcmp(disasm->instruction.mnemonic, "cmplwi")) { 297 | lastCmplwi = (s32)disasm->operand[1].immediateValue; 298 | } 299 | } 300 | 301 | - (void)updateProcedureAnalysis:(DisasmStruct *)disasm { 302 | //printf("updateProcedureAnalysis %s\n", disasm->instruction.mnemonic); 303 | } 304 | 305 | - (void)procedureAnalysisContinuesOnBasicBlock:(NSObject *)basicBlock { 306 | //printf("procedureAnalysisContinuesOnBasicBlock\n"); 307 | } 308 | 309 | - (void)procedureAnalysisOfPrologForProcedure:(NSObject *)procedure atEntryPoint:(Address)entryPoint { 310 | //printf("procedureAnalysisOfPrologForProcedure %p\n", procedure); 311 | } 312 | 313 | - (void)procedureAnalysisOfEpilogForProcedure:(NSObject *)procedure atEntryPoint:(Address)entryPoint { 314 | //printf("procedureAnalysisOfEpilogForProcedure %p\n", procedure); 315 | } 316 | 317 | - (void)procedureAnalysisEndedForProcedure:(NSObject *)procedure atEntryPoint:(Address)entryPoint { 318 | //printf("procedureAnalysisEndedForProcedure %p\n", procedure); 319 | _trackingLis = false; 320 | for (int i = 0; i < 32; ++i) { 321 | lisArr[i] = ~0; 322 | mulhwArr[i] = 0.0; 323 | dividendArr[i] = ~0; 324 | indexBaseArr[i] = ~0; 325 | } 326 | lastCmplwi = 0; 327 | indexBaseCTR = ~0; 328 | stackDisp = 0; 329 | 330 | // SDA/SDA2 symbol synthesis 331 | Address maxSDA = BAD_ADDRESS; 332 | if (foundSDA != BAD_ADDRESS && foundSDA2 != BAD_ADDRESS) 333 | maxSDA = MAX(foundSDA, foundSDA2); 334 | else if (foundSDA != BAD_ADDRESS) 335 | maxSDA = foundSDA; 336 | else if (foundSDA2 != BAD_ADDRESS) 337 | maxSDA = foundSDA2; 338 | 339 | if (maxSDA != BAD_ADDRESS) { 340 | if (![_file segmentForVirtualAddress:maxSDA]) { 341 | Address startAddr = _file.lastSegment.endAddress; 342 | NSObject *seg = [_file addSegmentAt:startAddr toExcludedAddress:maxSDA + 4]; 343 | seg.segmentName = @"SBSS2"; 344 | seg.readable = YES; 345 | seg.writable = NO; 346 | seg.executable = NO; 347 | NSObject *sec = [seg addSectionAt:startAddr toExcludedAddress:maxSDA + 4]; 348 | sec.sectionName = @"sbss2"; 349 | sec.pureDataSection = YES; 350 | sec.zeroFillSection = YES; 351 | } 352 | } 353 | 354 | if (foundSDA != BAD_ADDRESS) { 355 | [_file setName:@"_SDA_BASE_" forVirtualAddress:foundSDA reason:NCReason_Import]; 356 | [[_cpu hopperServices] logMessage:[NSString stringWithFormat:@"Found _SDA_BASE_ (r13): 0x%08X", (u32)foundSDA]]; 357 | } 358 | 359 | if (foundSDA2 != BAD_ADDRESS) { 360 | [_file setName:@"_SDA2_BASE_" forVirtualAddress:foundSDA2 reason:NCReason_Import]; 361 | [[_cpu hopperServices] logMessage:[NSString stringWithFormat:@"Found _SDA2_BASE_ (r2): 0x%08X", (u32)foundSDA2]]; 362 | } 363 | } 364 | 365 | - (void)analysisEnded { 366 | //printf("analysisEnded\n"); 367 | for (id addr in localLabels) { 368 | NSObject *procedure = [_file procedureAt:(u32)[addr unsignedIntegerValue]]; 369 | id val = [localLabels objectForKey:addr]; 370 | if ([procedure addressOfLocalLabel:val] == BAD_ADDRESS) 371 | [procedure setLocalLabel:val atAddress:(u32)[addr unsignedIntegerValue]]; 372 | else 373 | [_file setComment:val atVirtualAddress:(u32)[addr unsignedIntegerValue] reason:CCReason_Automatic]; 374 | } 375 | [localLabels removeAllObjects]; 376 | 377 | for (uint64_t i = 0; i < typesToSetCount; ++i) { 378 | struct TypeSet* storage = &typesToSet[i]; 379 | [_file setType:TypeForSize(storage->size) atVirtualAddress:(u32)storage->addr forLength:storage->size]; 380 | [_file setFormat:storage->format forArgument:0 atVirtualAddress:(u32)storage->addr]; 381 | } 382 | typesToSetCount = 0; 383 | } 384 | 385 | - (Address)getThunkDestinationForInstructionAt:(Address)address { 386 | return BAD_ADDRESS; 387 | } 388 | 389 | - (void)resetDisassembler { 390 | 391 | } 392 | 393 | - (uint8_t)estimateCPUModeAtVirtualAddress:(Address)address { 394 | return 0; 395 | } 396 | 397 | - (int)disassembleSingleInstruction:(DisasmStruct *)disasm usingProcessorMode:(NSUInteger)mode { 398 | DisasmStruct working; 399 | memcpy(&working, disasm, sizeof(working)); 400 | working.instruction.branchType = DISASM_BRANCH_NONE; 401 | working.instruction.addressValue = 0; 402 | working.instruction.userData = 0; 403 | for (int i=0; i *)branches forProcedure:(NSObject *)procedure basicBlock:(NSObject *)basicBlock ofSegment:(NSObject *)segment calledAddresses:(NSMutableArray *)calledAddresses callsites:(NSMutableArray *)callSitesAddresses { 473 | //printf("performBranchesAnalysis %08X %s %p\n", (u32)disasm->virtualAddr, disasm->instruction.mnemonic, procedure); 474 | 475 | // Switch statement 476 | if (indexBaseCTR != ~0 && !strcmp(disasm->instruction.mnemonic, "bctr") && lastCmplwi) { 477 | uint32_t offset = 0; 478 | Address addr = [_file readUInt32AtVirtualAddress:(u32)indexBaseCTR]; 479 | NSMutableDictionary* labelDict = [NSMutableDictionary dictionaryWithCapacity:lastCmplwi+1]; 480 | for (int32_t i = 0; i <= lastCmplwi; ++i) { 481 | NSMutableArray* arr = [labelDict objectForKey:@(addr)]; 482 | if (!arr) { 483 | arr = [NSMutableArray new]; 484 | [labelDict setObject:arr forKey:@(addr)]; 485 | } 486 | [arr addObject:@(offset / 4)]; 487 | offset += 4; 488 | addr = [_file readUInt32AtVirtualAddress:(u32)indexBaseCTR + offset]; 489 | } 490 | [_file setType:Type_Int32 atVirtualAddress:(u32)indexBaseCTR forLength:offset]; 491 | [_file setName:[NSString stringWithFormat:@"jump table for 0x%08X", (u32)disasm->virtualAddr] 492 | forVirtualAddress:(u32)indexBaseCTR reason:NCReason_Automatic]; 493 | NSUInteger maxTargetCount = 0; 494 | for (id addr in labelDict) { 495 | [branches addObject:addr]; 496 | NSMutableArray* arr = [labelDict objectForKey:addr]; 497 | maxTargetCount = MAX(maxTargetCount, arr.count); 498 | } 499 | NSUInteger maxDupeCount = 0; 500 | for (id addr in labelDict) { 501 | [branches addObject:addr]; 502 | NSMutableArray* arr = [labelDict objectForKey:addr]; 503 | if (arr.count == maxTargetCount) 504 | ++maxDupeCount; 505 | } 506 | for (id addr in labelDict) { 507 | NSMutableArray* arr = [labelDict objectForKey:addr]; 508 | if (maxDupeCount == 1 && arr.count == maxTargetCount) 509 | [localLabels setObject:@"default" forKey:addr]; 510 | else { 511 | NSArray* sorted = [arr sortedArrayUsingSelector:@selector(compare:)]; 512 | NSMutableString* str = [NSMutableString stringWithString:@"case "]; 513 | int prev = -1; 514 | bool inRange = false; 515 | for (id idx in sorted) { 516 | int this = [idx intValue]; 517 | if (prev == -1) { 518 | [str appendFormat:@"%d", this]; 519 | } else if (this == prev) { 520 | } else if (this == prev + 1) { 521 | inRange = true; 522 | } else { 523 | if (inRange) { 524 | inRange = false; 525 | [str appendFormat:@"-%d", prev]; 526 | } 527 | [str appendFormat:@",%d", this]; 528 | } 529 | prev = this; 530 | } 531 | if (inRange) { 532 | inRange = false; 533 | [str appendFormat:@"-%d", prev]; 534 | } 535 | [localLabels setObject:str forKey:addr]; 536 | } 537 | } 538 | *next = disasm->virtualAddr + 4; 539 | return; 540 | } 541 | 542 | if (disasm->instruction.branchType == DISASM_BRANCH_CALL) { 543 | [callSitesAddresses addObject:@(disasm->instruction.addressValue)]; 544 | *next = disasm->virtualAddr + 4; 545 | } else if (disasm->instruction.branchType == DISASM_BRANCH_RET) { 546 | *next = BAD_ADDRESS; 547 | } else { 548 | [branches addObject:@(disasm->instruction.addressValue)]; 549 | *next = disasm->virtualAddr + 4; 550 | } 551 | //printf("%08X NEXT %08X %d\n", disasm->virtualAddr, *next, disasm->instruction.branchType); 552 | } 553 | 554 | // Printing 555 | 556 | - (NSObject *)buildMnemonicString:(DisasmStruct *)disasm inFile:(NSObject *)file { 557 | NSObject *services = _cpu.hopperServices; 558 | NSObject *line = [services blankASMLine]; 559 | [line appendMnemonic:@(disasm->instruction.mnemonic)]; 560 | return line; 561 | } 562 | 563 | static RegClass GetRegisterClass(DisasmOperandType type) 564 | { 565 | for (int i = 0; i < DISASM_MAX_REG_CLASSES; ++i) 566 | if (type & DISASM_BUILD_REGISTER_CLS_MASK(i)) 567 | return i; 568 | return -1; 569 | } 570 | 571 | static int GetRegisterIndex(DisasmOperandType type) 572 | { 573 | for (int i = 0; i < DISASM_MAX_REG_INDEX; ++i) 574 | if (type & DISASM_BUILD_REGISTER_INDEX_MASK(i)) 575 | return i; 576 | return -1; 577 | } 578 | 579 | - (NSObject *)buildOperandString:(DisasmStruct *)disasm forOperandIndex:(NSUInteger)operandIndex inFile:(NSObject *)file raw:(BOOL)raw { 580 | if (operandIndex >= DISASM_MAX_OPERANDS) return nil; 581 | DisasmOperand *operand = disasm->operand + operandIndex; 582 | if (operand->type == DISASM_OPERAND_NO_OPERAND) return nil; 583 | 584 | // Get the format requested by the user 585 | ArgFormat format = [file formatForArgument:operandIndex atVirtualAddress:disasm->virtualAddr]; 586 | 587 | NSObject *services = _cpu.hopperServices; 588 | NSObject *line = [services blankASMLine]; 589 | 590 | if (operand->type & DISASM_OPERAND_CONSTANT_TYPE) { 591 | // Local variable 592 | if ((format == Format_Default || format == Format_StackVariable)) { 593 | if ((operandIndex == 1 && disasm->instruction.userData & DISASM_PPC_INST_LOAD_STORE && 594 | disasm->operand[2].type & DISASM_BUILD_REGISTER_INDEX_MASK(1)) || 595 | (operandIndex == 2 && disasm->instruction.userData & DISASM_PPC_INST_ADDI && 596 | disasm->operand[1].type & DISASM_BUILD_REGISTER_INDEX_MASK(1))) { 597 | NSObject *proc = [file procedureAt:disasm->virtualAddr]; 598 | if (proc) { 599 | NSString *variableName = [proc variableNameForDisplacement:operand->immediateValue]; 600 | if (variableName) { 601 | [line appendVariableName:variableName withDisplacement:operand->immediateValue]; 602 | [line setIsOperand:operandIndex startingAtIndex:0]; 603 | return line; 604 | } 605 | } 606 | } else if (operandIndex == 2 && disasm->instruction.userData & DISASM_PPC_INST_SUBI && 607 | disasm->operand[1].type & DISASM_BUILD_REGISTER_INDEX_MASK(1)) { 608 | NSObject *proc = [file procedureAt:disasm->virtualAddr]; 609 | if (proc) { 610 | NSString *variableName = [proc variableNameForDisplacement:-operand->immediateValue]; 611 | if (variableName) { 612 | [line appendVariableName:variableName withDisplacement:-operand->immediateValue]; 613 | [line setIsOperand:operandIndex startingAtIndex:0]; 614 | return line; 615 | } 616 | } 617 | } 618 | } 619 | 620 | if (format == Format_Default) { 621 | if (disasm->instruction.addressValue != 0 && !(disasm->instruction.userData & DISASM_PPC_INST_LOAD_STORE)) { 622 | format = Format_Address; 623 | } 624 | else if (operandIndex <= 2 && 625 | disasm->instruction.userData & (DISASM_PPC_INST_ADDI | DISASM_PPC_INST_SUBI | DISASM_PPC_INST_LOAD_STORE)) { 626 | format = Format_Hexadecimal | Format_Signed; 627 | } 628 | else { 629 | if (operand->userData[0] & DISASM_PPC_OPER_IMM_HEX || llabs(operand->immediateValue) > 255) 630 | format = Format_Hexadecimal | Format_Signed; 631 | else 632 | format = Format_Decimal | Format_Signed; 633 | } 634 | } 635 | [line append:[file formatNumber:operand->immediateValue 636 | at:disasm->virtualAddr usingFormat:format 637 | andBitSize:32]]; 638 | } 639 | else if (operand->type & DISASM_OPERAND_REGISTER_TYPE || operand->type & DISASM_OPERAND_MEMORY_TYPE) { 640 | RegClass regCls = GetRegisterClass(operand->type); 641 | int regIdx = GetRegisterIndex(operand->type); 642 | [line appendRegister:[_cpu registerIndexToString:regIdx 643 | ofClass:regCls 644 | withBitSize:32 645 | position:DISASM_LOWPOSITION 646 | andSyntaxIndex:file.userRequestedSyntaxIndex] 647 | ofClass:regCls 648 | andIndex:regIdx]; 649 | } 650 | else if (operand->type & DISASM_OPERAND_OTHER) { 651 | [line appendRegister:@(operand->userString + 8)]; 652 | } 653 | 654 | [line setIsOperand:operandIndex startingAtIndex:0]; 655 | 656 | return line; 657 | } 658 | 659 | static const char* CRNames[] = 660 | { 661 | "lt", 662 | "gt", 663 | "eq", 664 | "so" 665 | }; 666 | 667 | - (NSObject *)buildCompleteOperandString:(DisasmStruct *)disasm inFile:(NSObject *)file raw:(BOOL)raw { 668 | NSObject *services = _cpu.hopperServices; 669 | 670 | NSObject *line = [services blankASMLine]; 671 | 672 | int op_index = 0; 673 | 674 | if (disasm->instruction.userData & DISASM_PPC_INST_LOAD_STORE) 675 | { 676 | NSObject *part = [self buildOperandString:disasm forOperandIndex:0 inFile:file raw:raw]; 677 | if (part == nil) return line; 678 | [line append:part]; 679 | [line appendRawString:@", "]; 680 | 681 | part = [self buildOperandString:disasm forOperandIndex:1 inFile:file raw:raw]; 682 | if (part == nil) return line; 683 | [line append:part]; 684 | [line appendRawString:@"("]; 685 | 686 | part = [self buildOperandString:disasm forOperandIndex:2 inFile:file raw:raw]; 687 | if (part == nil) return line; 688 | [line append:part]; 689 | [line appendRawString:@")"]; 690 | 691 | op_index = 3; 692 | } 693 | 694 | for (; op_index *part = [self buildOperandString:disasm forOperandIndex:op_index inFile:file raw:raw]; 696 | if (part == nil) break; 697 | if (op_index) [line appendRawString:@", "]; 698 | [line append:part]; 699 | 700 | // RLWIMI comment 701 | DisasmOperand *operand = disasm->operand + op_index; 702 | if (operand->userData[0] & DISASM_PPC_OPER_RLWIMI) { 703 | int ra = GetRegisterIndex(disasm->operand[0].type); 704 | int rs = GetRegisterIndex(disasm->operand[1].type); 705 | int sh = (int)operand->userData[1]; 706 | int mb = (int)operand->userData[2]; 707 | int me = (int)operand->userData[3]; 708 | if (sh == 0) { 709 | [line appendComment:[NSString stringWithFormat:@" # r%d = r%d & 0x%08X", ra, rs, MASK32VAL(mb, me)]]; 710 | } else if (me + sh > 31) { 711 | // Actually a shift right 712 | [line appendComment:[NSString stringWithFormat:@" # r%d = (r%d >> %d) & 0x%08X", ra, rs, 32 - sh, MASK32VAL(mb, me)]]; 713 | } else { 714 | [line appendComment:[NSString stringWithFormat:@" # r%d = (r%d << %d) & 0x%08X", ra, rs, sh, MASK32VAL(mb, me)]]; 715 | } 716 | } 717 | } 718 | 719 | if (!strcmp(disasm->instruction.mnemonic, "cror")) { 720 | [line appendComment:[NSString stringWithFormat:@" # %s = %s | %s", 721 | CRNames[GetRegisterIndex(disasm->operand[0].type) & 0x3], 722 | CRNames[GetRegisterIndex(disasm->operand[1].type) & 0x3], 723 | CRNames[GetRegisterIndex(disasm->operand[2].type) & 0x3]]]; 724 | } 725 | 726 | return line; 727 | } 728 | 729 | // Decompiler 730 | 731 | - (BOOL)canDecompileProcedure:(NSObject *)procedure { 732 | return NO; 733 | } 734 | 735 | - (Address)skipHeader:(NSObject *)basicBlock ofProcedure:(NSObject *)procedure { 736 | return basicBlock.from; 737 | } 738 | 739 | - (Address)skipFooter:(NSObject *)basicBlock ofProcedure:(NSObject *)procedure { 740 | return basicBlock.to; 741 | } 742 | 743 | - (ASTNode *)rawDecodeArgumentIndex:(int)argIndex 744 | ofDisasm:(DisasmStruct *)disasm 745 | ignoringWriteMode:(BOOL)ignoreWrite 746 | usingDecompiler:(Decompiler *)decompiler { 747 | return nil; 748 | } 749 | 750 | - (ASTNode *)decompileInstructionAtAddress:(Address)a 751 | disasm:(DisasmStruct *)d 752 | addNode_p:(BOOL *)addNode_p 753 | usingDecompiler:(Decompiler *)decompiler { 754 | return nil; 755 | } 756 | 757 | // Assembler 758 | 759 | - (NSData *)assembleRawInstruction:(NSString *)instr atAddress:(Address)addr forFile:(NSObject *)file withCPUMode:(uint8_t)cpuMode usingSyntaxVariant:(NSUInteger)syntax error:(NSError **)error { 760 | return nil; 761 | } 762 | 763 | - (BOOL)instructionCanBeUsedToExtractDirectMemoryReferences:(DisasmStruct *)disasmStruct { 764 | return YES; 765 | } 766 | 767 | - (BOOL)instructionOnlyLoadsAddress:(DisasmStruct *)disasmStruct { 768 | return NO; 769 | } 770 | 771 | - (BOOL)instructionMayBeASwitchStatement:(DisasmStruct *)disasmStruct { 772 | return !strcmp(disasmStruct->instruction.mnemonic, "bctr"); 773 | } 774 | 775 | - (BOOL)instructionConditionCPUModeAtTargetAddress:(DisasmStruct *)disasmStruct resultCPUMode:(uint8_t *)cpuMode { 776 | return NO; 777 | } 778 | 779 | - (BOOL)instructionManipulatesFloat:(DisasmStruct *)disasmStruct { 780 | return NO; 781 | } 782 | 783 | @end 784 | -------------------------------------------------------------------------------- /PPCCPU/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /PPCCPU/ppcd/CommonDefs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef WIN32 4 | #define WINDOWS 5 | #endif 6 | 7 | /* 8 | * General Data Types. 9 | */ 10 | 11 | #include 12 | 13 | typedef int8_t s8; 14 | typedef int16_t s16; 15 | typedef int32_t s32; 16 | typedef uint8_t u8; 17 | typedef uint16_t u16; 18 | typedef uint32_t u32; 19 | typedef float f32; 20 | typedef double f64; 21 | 22 | typedef uint64_t u64; 23 | typedef int64_t s64; 24 | 25 | #include 26 | 27 | HP_BEGIN_DECL_ENUM(NSUInteger, PPCRegClass) { 28 | RegClass_FPRegister = RegClass_FirstUserClass, 29 | RegClass_PPC_Cnt, 30 | RegClass_PPC_CondReg, 31 | RegClass_SPRegister, 32 | RegClass_TBRegister 33 | } 34 | HP_END_DECL_ENUM(PPCRegClass); 35 | 36 | HP_BEGIN_DECL_ENUM(NSUInteger, PPCIncrement) { 37 | INCR_NoIncrement, 38 | INCR_Postincrement, 39 | INCR_Predecrement 40 | } 41 | HP_END_DECL_ENUM(PPCIncrement); 42 | 43 | // Hopper user instruction flags 44 | #define DISASM_PPC_INST_NONE 0x0 45 | #define DISASM_PPC_INST_BRANCH_SET_LINK_REGISTER 0x1 46 | #define DISASM_PPC_INST_BRANCH_TO_LINK_REGISTER 0x2 47 | #define DISASM_PPC_INST_BRANCH_TO_COUNT_REGISTER 0x4 48 | #define DISASM_PPC_INST_LOAD_STORE 0x8 49 | #define DISASM_PPC_INST_INDEXED_LOAD_STORE 0x10 50 | #define DISASM_PPC_INST_ADDI 0x20 51 | #define DISASM_PPC_INST_SUBI 0x40 52 | 53 | // Hopper user operand flags 54 | #define DISASM_PPC_OPER_NONE 0x0 55 | #define DISASM_PPC_OPER_IMM_HEX 0x1 56 | #define DISASM_PPC_OPER_LIS_ADDI 0x2 57 | #define DISASM_PPC_OPER_RLWIMI 0x4 58 | #define DISASM_PPC_OPER_MULHW 0x8 59 | 60 | static inline u32 MASK32VAL(u32 b, u32 e) 61 | { 62 | u32 mask = ((u32)0xffffffff >> (b)) ^ (((e) >= 31) ? 0 : ((u32)0xffffffff) >> ((e) + 1)); 63 | return ((b) > (e)) ? (~mask) : (mask); 64 | } 65 | 66 | static inline u64 MASK64VAL(u32 b, u32 e) 67 | { 68 | u64 mask = ((u64)0xffffffffffffffff >> (b)) ^ (((e) >= 63) ? 0 : ((u64)0xffffffffffffffff) >> ((e) + 1)); 69 | return ((b) > (e)) ? (~mask) : (mask); 70 | } 71 | 72 | #define FASTCALL __fastcall 73 | #define INLINE __inline 74 | -------------------------------------------------------------------------------- /PPCCPU/ppcd/main.cpp: -------------------------------------------------------------------------------- 1 | // PowerPC disassembler. 2 | 3 | #include 4 | 5 | #include "Commondefs.h" 6 | #include "ppcd.h" 7 | 8 | #define MODULE_NAME "ppcd-generic32" 9 | #define MODULE_VER 0.03 10 | 11 | static void usage(void) 12 | { 13 | printf ("PPCD PowerPC Disassembler. Version %.2f\n", MODULE_VER); 14 | printf ("(c) 2007, org.\n\n"); 15 | printf ("Use: %s \n", MODULE_NAME); 16 | } 17 | 18 | static u32 FASTCALL Swap32(u32 data) 19 | { 20 | return ((data >> 24) ) | 21 | ((data >> 8) & 0x0000ff00) | 22 | ((data << 8) & 0x00ff0000) | 23 | ((data << 24) ); 24 | } 25 | 26 | /*int main (int argc, char **argv) 27 | { 28 | PPCD_CB disa; 29 | FILE *f; 30 | u32 pc = 0x80000000, instr; 31 | 32 | if (argc <= 1) 33 | { 34 | usage (); 35 | return (0); 36 | } 37 | 38 | f = fopen (argv[1], "rb"); 39 | if (f == NULL) return (0); 40 | 41 | while (!feof(f)) 42 | { 43 | fread (&instr, 1, 4, f); 44 | 45 | disa.pc = pc; 46 | disa.instr = Swap32 (instr); 47 | PPCDisasm (&disa); 48 | 49 | printf ("%08X %08X %-12s%-30s\n", pc, instr, disa.mnemonic, disa.operands); 50 | pc += 4; 51 | } 52 | 53 | fclose (f); 54 | return (1); 55 | } 56 | */ -------------------------------------------------------------------------------- /PPCCPU/ppcd/ppcd.h: -------------------------------------------------------------------------------- 1 | // See some documentation in CPP file. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | // Instruction class 8 | #define PPC_DISA_OTHER 0x0000 // No additional information 9 | #define PPC_DISA_64 0x0001 // 64-bit architecture only 10 | #define PPC_DISA_INTEGER 0x0002 // Integer-type instruction 11 | #define PPC_DISA_BRANCH 0x0004 // Branch instruction 12 | #define PPC_DISA_LDST 0x0008 // Load-store instruction 13 | #define PPC_DISA_STRING 0x0010 // Load-store string/multiple 14 | #define PPC_DISA_FPU 0x0020 // Floating-point instruction 15 | #define PPC_DISA_OEA 0x0040 // Supervisor level 16 | #define PPC_DISA_OPTIONAL 0x0200 // Optional 17 | #define PPC_DISA_BRIDGE 0x0400 // Optional 64-bit bridge 18 | #define PPC_DISA_SPECIFIC 0x0800 // Implementation-specific 19 | #define PPC_DISA_ILLEGAL 0x1000 // Illegal 20 | #define PPC_DISA_SIMPLIFIED 0x8000 // Simplified mnemonic is used 21 | 22 | typedef struct PPCD_CB 23 | { 24 | u64 pc; // Program counter (input) 25 | u32 instr; // Instruction (input) 26 | char mnemonic[16]; // Instruction mnemonic. 27 | char operands[64]; // Instruction operands. 28 | u32 immed; // Immediate value (displacement for load/store, immediate operand for arithm./logic). 29 | int r[4]; // Index value for operand registers and immediates. 30 | u64 target; // Target address for branch instructions / Mask for RLWINM-like instructions 31 | int iclass; // One or combination of PPC_DISA_* flags. 32 | DisasmStruct *disasm; // Interface with Hopper 33 | int opIdx; // Temporary operand index tracker for Hopper 34 | s32* lisArr; // Keeps values of previously-encountered LIS instructions to resolve with ADDI 35 | } PPCD_CB; 36 | 37 | void PPCDisasm(PPCD_CB *disa); 38 | char* PPCDisasmSimple(u64 pc, u32 instr); 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DOL Loader 2 | This repository contains a plugin for Hopper Disassembler called DOLLoader which loads Gamecube and Wii .DOL files. The plugin *does not* use Hopper's built-in PPC disassembler due to unsupported Gecko instructions. Instead, this repository also contains a full PPC plugin replacement to add Gecko functionality and some environmental awareness of the GameCube/Wii. Both plugins have been successfully tested on macOS and Linux. 3 | 4 | ![metroid prime switch statement](https://jackoalan.github.io/HopperPPC-Plugin/MetroidPrimeSwitchStatement.png "A switch statement from Metroid Prime") 5 | 6 | # PPCCPU Plugin 7 | The repository also contains a plugin which adds support for the Gecko variant of the IBM PowerPC 750. This CPU reports itself as `ppc32/gecko` and will only work with 32-bit PPC binaries. In addition to supporting Gecko instructions, linker-defined symbols left for the CodeWarrior C runtime are automatically discovered and used to aid in resolving SDA memory accesses via `r13` and `r2`. Sections are named according to the GameCube's common linker configuration (init, extab, text, data, rodata, sdata, sdata2, etc). The PPC plugin also performs inline stack variable printing for load/store/addi instructions relative to `r1`. 8 | 9 | # REL Linker 10 | The repository also contains a tool which links REL files appended to the DOL within Hopper. To use this, open one or more REL files (shipped with the DOL) using File > Load Additional Binary. You have the option to specify where in virtual memory the REL gets mapped. Hopper's default value is typically fine; but make sure it's 32-byte aligned. The other options may be left at their defaults. Next, run the linker from Tool Plugins > Link REL Segments. When this completes, you may browse the new sections using Navigate > Show Section List... 11 | 12 | # Acknowledgements 13 | Both plugins originally thrown together by Paul Kratt. 14 | 15 | Forked by Jack Andersen and fleshed out with the following: 16 | - Support for Hopper 4.3+ 17 | - Directly integrated PPCD (no second-stage parsing of instructions) 18 | - Proper syntax-colored operands 19 | - Instruction simplifications for `rlwinm` (slwi, clrlwi, extlwi, etc) 20 | - Automatic comments rendering `rlwinm` as a C expression 21 | - Automatic comments for division by constant 22 | - Inline stack variables 23 | - Jump table parsing and case label generation 24 | - Section names and permissions based on the GameCube's CodeWarrior toolchain 25 | - REL Linking 26 | - Linux makefile 27 | 28 | See commit history for other contributions. 29 | 30 | The PPC disassembly code is based on PPCD by org. 31 | Original code: https://code.google.com/p/ppcd/ 32 | The license for that is listed as "free opensource". This is too. 33 | 34 | The DOL loading code is original, but the file format is very simple. 35 | 36 | # macOS install 37 | 1. Checkout or download this repository into a subfolder of your Hopper SDK download so the XCode projects can find the includes. 38 | 2. Open each XCode Project. 39 | 3. Build each XCode Project. 40 | 4. That's it. Building the plugins will install them into the plugins directory. 41 | 42 | # Linux install 43 | 1. Checkout or download this repository into a subfolder of your Hopper SDK download. 44 | 2. Follow the instructions in "SDK Documentation.pdf" to get a GNUstep build environment established for your user. 45 | 3. Setup the GNUstep build environment for your shell by running `. "gnustep-Linux-/share/GNUstep/Makefiles/GNUstep.sh"` 46 | 4. `cd` into the HopperPPC-Plugin directory and run `make` 47 | 5. Create symlinks for Hopper to discover the bundles: 48 | ```sh 49 | mkdir -p ~/GNUstep/Library/ApplicationSupport/Hopper/PlugIns/v4/{CPUs,Loaders,Tools} 50 | ln -s $(pwd)/PPCCPU.bundle ~/GNUstep/Library/ApplicationSupport/Hopper/PlugIns/v4/CPUs/PPCCPU.hopperCPU 51 | ln -s $(pwd)/DOL_Loader.bundle ~/GNUstep/Library/ApplicationSupport/Hopper/PlugIns/v4/Loaders/DOL_Loader.hopperLoader 52 | ln -s $(pwd)/REL_Linker.bundle ~/GNUstep/Library/ApplicationSupport/Hopper/PlugIns/v4/Tools/REL_Linker.hopperTool 53 | ``` 54 | -------------------------------------------------------------------------------- /RELLinker.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 07AC766718C8CB9A007D5414 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 07AC766618C8CB9A007D5414 /* CoreFoundation.framework */; }; 11 | 07AC766D18C8CB9A007D5414 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 07AC766B18C8CB9A007D5414 /* InfoPlist.strings */; }; 12 | 07AC767618C8CBC8007D5414 /* RELLinker.m in Sources */ = {isa = PBXBuildFile; fileRef = 07AC767518C8CBC8007D5414 /* RELLinker.m */; }; 13 | 07AC767918C8CED6007D5414 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 07AC767818C8CED6007D5414 /* Cocoa.framework */; }; 14 | /* End PBXBuildFile section */ 15 | 16 | /* Begin PBXFileReference section */ 17 | 07AC766318C8CB9A007D5414 /* RELLinker.hopperTool */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RELLinker.hopperTool; sourceTree = BUILT_PRODUCTS_DIR; }; 18 | 07AC766618C8CB9A007D5414 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; 19 | 07AC766A18C8CB9A007D5414 /* RELLinker-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "RELLinker-Info.plist"; sourceTree = ""; }; 20 | 07AC766C18C8CB9A007D5414 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 21 | 07AC766E18C8CB9A007D5414 /* RELLinker-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "RELLinker-Prefix.pch"; sourceTree = ""; }; 22 | 07AC767418C8CBC8007D5414 /* RELLinker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RELLinker.h; sourceTree = ""; }; 23 | 07AC767518C8CBC8007D5414 /* RELLinker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RELLinker.m; sourceTree = ""; }; 24 | 07AC767818C8CED6007D5414 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; 25 | /* End PBXFileReference section */ 26 | 27 | /* Begin PBXFrameworksBuildPhase section */ 28 | 07AC766018C8CB9A007D5414 /* Frameworks */ = { 29 | isa = PBXFrameworksBuildPhase; 30 | buildActionMask = 2147483647; 31 | files = ( 32 | 07AC767918C8CED6007D5414 /* Cocoa.framework in Frameworks */, 33 | 07AC766718C8CB9A007D5414 /* CoreFoundation.framework in Frameworks */, 34 | ); 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXFrameworksBuildPhase section */ 38 | 39 | /* Begin PBXGroup section */ 40 | 07AC765A18C8CB9A007D5414 = { 41 | isa = PBXGroup; 42 | children = ( 43 | 07AC766818C8CB9A007D5414 /* RELLinker */, 44 | 07AC766918C8CB9A007D5414 /* Supporting Files */, 45 | 07AC766518C8CB9A007D5414 /* Frameworks */, 46 | 07AC766418C8CB9A007D5414 /* Products */, 47 | ); 48 | sourceTree = ""; 49 | }; 50 | 07AC766418C8CB9A007D5414 /* Products */ = { 51 | isa = PBXGroup; 52 | children = ( 53 | 07AC766318C8CB9A007D5414 /* RELLinker.hopperTool */, 54 | ); 55 | name = Products; 56 | sourceTree = ""; 57 | }; 58 | 07AC766518C8CB9A007D5414 /* Frameworks */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 07AC767818C8CED6007D5414 /* Cocoa.framework */, 62 | 07AC766618C8CB9A007D5414 /* CoreFoundation.framework */, 63 | ); 64 | name = Frameworks; 65 | sourceTree = ""; 66 | }; 67 | 07AC766818C8CB9A007D5414 /* RELLinker */ = { 68 | isa = PBXGroup; 69 | children = ( 70 | 07AC767418C8CBC8007D5414 /* RELLinker.h */, 71 | 07AC767518C8CBC8007D5414 /* RELLinker.m */, 72 | ); 73 | path = RELLinker; 74 | sourceTree = ""; 75 | }; 76 | 07AC766918C8CB9A007D5414 /* Supporting Files */ = { 77 | isa = PBXGroup; 78 | children = ( 79 | 07AC766A18C8CB9A007D5414 /* RELLinker-Info.plist */, 80 | 07AC766B18C8CB9A007D5414 /* InfoPlist.strings */, 81 | 07AC766E18C8CB9A007D5414 /* RELLinker-Prefix.pch */, 82 | ); 83 | name = "Supporting Files"; 84 | path = REL_Linker; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXNativeTarget section */ 90 | 07AC766218C8CB9A007D5414 /* RELLinker */ = { 91 | isa = PBXNativeTarget; 92 | buildConfigurationList = 07AC767118C8CB9A007D5414 /* Build configuration list for PBXNativeTarget "RELLinker" */; 93 | buildPhases = ( 94 | 07AC765F18C8CB9A007D5414 /* Sources */, 95 | 07AC766018C8CB9A007D5414 /* Frameworks */, 96 | 07AC766118C8CB9A007D5414 /* Resources */, 97 | 07AC767718C8CBF4007D5414 /* Run Script (Installation) */, 98 | ); 99 | buildRules = ( 100 | ); 101 | dependencies = ( 102 | ); 103 | name = RELLinker; 104 | productName = SampleTool; 105 | productReference = 07AC766318C8CB9A007D5414 /* RELLinker.hopperTool */; 106 | productType = "com.apple.product-type.bundle"; 107 | }; 108 | /* End PBXNativeTarget section */ 109 | 110 | /* Begin PBXProject section */ 111 | 07AC765B18C8CB9A007D5414 /* Project object */ = { 112 | isa = PBXProject; 113 | attributes = { 114 | LastUpgradeCheck = 0900; 115 | ORGANIZATIONNAME = com.sappharad; 116 | }; 117 | buildConfigurationList = 07AC765E18C8CB9A007D5414 /* Build configuration list for PBXProject "RELLinker" */; 118 | compatibilityVersion = "Xcode 3.2"; 119 | developmentRegion = English; 120 | hasScannedForEncodings = 0; 121 | knownRegions = ( 122 | en, 123 | ); 124 | mainGroup = 07AC765A18C8CB9A007D5414; 125 | productRefGroup = 07AC766418C8CB9A007D5414 /* Products */; 126 | projectDirPath = ""; 127 | projectRoot = ""; 128 | targets = ( 129 | 07AC766218C8CB9A007D5414 /* RELLinker */, 130 | ); 131 | }; 132 | /* End PBXProject section */ 133 | 134 | /* Begin PBXResourcesBuildPhase section */ 135 | 07AC766118C8CB9A007D5414 /* Resources */ = { 136 | isa = PBXResourcesBuildPhase; 137 | buildActionMask = 2147483647; 138 | files = ( 139 | 07AC766D18C8CB9A007D5414 /* InfoPlist.strings in Resources */, 140 | ); 141 | runOnlyForDeploymentPostprocessing = 0; 142 | }; 143 | /* End PBXResourcesBuildPhase section */ 144 | 145 | /* Begin PBXShellScriptBuildPhase section */ 146 | 07AC767718C8CBF4007D5414 /* Run Script (Installation) */ = { 147 | isa = PBXShellScriptBuildPhase; 148 | buildActionMask = 2147483647; 149 | files = ( 150 | ); 151 | inputPaths = ( 152 | ); 153 | name = "Run Script (Installation)"; 154 | outputPaths = ( 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | shellPath = /bin/sh; 158 | shellScript = "rm -rf \"${INSTALL_PATH}/RELLinker.hopperTool\"\nmkdir -p \"${INSTALL_PATH}\"\ncp -rf \"${BUILT_PRODUCTS_DIR}/RELLinker.hopperTool\" \"${INSTALL_PATH}\"\n"; 159 | }; 160 | /* End PBXShellScriptBuildPhase section */ 161 | 162 | /* Begin PBXSourcesBuildPhase section */ 163 | 07AC765F18C8CB9A007D5414 /* Sources */ = { 164 | isa = PBXSourcesBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | 07AC767618C8CBC8007D5414 /* RELLinker.m in Sources */, 168 | ); 169 | runOnlyForDeploymentPostprocessing = 0; 170 | }; 171 | /* End PBXSourcesBuildPhase section */ 172 | 173 | /* Begin PBXVariantGroup section */ 174 | 07AC766B18C8CB9A007D5414 /* InfoPlist.strings */ = { 175 | isa = PBXVariantGroup; 176 | children = ( 177 | 07AC766C18C8CB9A007D5414 /* en */, 178 | ); 179 | name = InfoPlist.strings; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXVariantGroup section */ 183 | 184 | /* Begin XCBuildConfiguration section */ 185 | 07AC766F18C8CB9A007D5414 /* Debug */ = { 186 | isa = XCBuildConfiguration; 187 | buildSettings = { 188 | ALWAYS_SEARCH_USER_PATHS = NO; 189 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 190 | CLANG_CXX_LIBRARY = "libc++"; 191 | CLANG_ENABLE_OBJC_ARC = YES; 192 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 193 | CLANG_WARN_BOOL_CONVERSION = YES; 194 | CLANG_WARN_COMMA = YES; 195 | CLANG_WARN_CONSTANT_CONVERSION = YES; 196 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 197 | CLANG_WARN_EMPTY_BODY = YES; 198 | CLANG_WARN_ENUM_CONVERSION = YES; 199 | CLANG_WARN_INFINITE_RECURSION = YES; 200 | CLANG_WARN_INT_CONVERSION = YES; 201 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 202 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 203 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 204 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 205 | CLANG_WARN_STRICT_PROTOTYPES = YES; 206 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 207 | CLANG_WARN_UNREACHABLE_CODE = YES; 208 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 209 | COPY_PHASE_STRIP = NO; 210 | ENABLE_STRICT_OBJC_MSGSEND = YES; 211 | ENABLE_TESTABILITY = YES; 212 | GCC_C_LANGUAGE_STANDARD = gnu99; 213 | GCC_DYNAMIC_NO_PIC = NO; 214 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 215 | GCC_NO_COMMON_BLOCKS = YES; 216 | GCC_OPTIMIZATION_LEVEL = 0; 217 | GCC_PREPROCESSOR_DEFINITIONS = ( 218 | "DEBUG=1", 219 | "$(inherited)", 220 | ); 221 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 222 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 223 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 224 | GCC_WARN_UNDECLARED_SELECTOR = YES; 225 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 226 | GCC_WARN_UNUSED_FUNCTION = YES; 227 | GCC_WARN_UNUSED_VARIABLE = YES; 228 | MACOSX_DEPLOYMENT_TARGET = 10.9; 229 | ONLY_ACTIVE_ARCH = YES; 230 | SDKROOT = macosx; 231 | }; 232 | name = Debug; 233 | }; 234 | 07AC767018C8CB9A007D5414 /* Release */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 239 | CLANG_CXX_LIBRARY = "libc++"; 240 | CLANG_ENABLE_OBJC_ARC = YES; 241 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 242 | CLANG_WARN_BOOL_CONVERSION = YES; 243 | CLANG_WARN_COMMA = YES; 244 | CLANG_WARN_CONSTANT_CONVERSION = YES; 245 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 246 | CLANG_WARN_EMPTY_BODY = YES; 247 | CLANG_WARN_ENUM_CONVERSION = YES; 248 | CLANG_WARN_INFINITE_RECURSION = YES; 249 | CLANG_WARN_INT_CONVERSION = YES; 250 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 251 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 252 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 253 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 254 | CLANG_WARN_STRICT_PROTOTYPES = YES; 255 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 256 | CLANG_WARN_UNREACHABLE_CODE = YES; 257 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 258 | COPY_PHASE_STRIP = YES; 259 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 260 | ENABLE_NS_ASSERTIONS = NO; 261 | ENABLE_STRICT_OBJC_MSGSEND = YES; 262 | GCC_C_LANGUAGE_STANDARD = gnu99; 263 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 264 | GCC_NO_COMMON_BLOCKS = YES; 265 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 266 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 267 | GCC_WARN_UNDECLARED_SELECTOR = YES; 268 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 269 | GCC_WARN_UNUSED_FUNCTION = YES; 270 | GCC_WARN_UNUSED_VARIABLE = YES; 271 | MACOSX_DEPLOYMENT_TARGET = 10.9; 272 | SDKROOT = macosx; 273 | }; 274 | name = Release; 275 | }; 276 | 07AC767218C8CB9A007D5414 /* Debug */ = { 277 | isa = XCBuildConfiguration; 278 | buildSettings = { 279 | COMBINE_HIDPI_IMAGES = YES; 280 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 281 | GCC_PREFIX_HEADER = "REL_Linker/RELLinker-prefix.pch"; 282 | HEADER_SEARCH_PATHS = ( 283 | "$(inherited)", 284 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 285 | "$(SRCROOT)/../include", 286 | ); 287 | INFOPLIST_FILE = "REL_Linker/RELLinker-Info.plist"; 288 | INSTALL_PATH = "$(USER_LIBRARY_DIR)/Application Support/Hopper/PlugIns/v4/Tools"; 289 | PRODUCT_BUNDLE_IDENTIFIER = com.sappharad.RELLinker; 290 | PRODUCT_NAME = "$(TARGET_NAME)"; 291 | WRAPPER_EXTENSION = hopperTool; 292 | }; 293 | name = Debug; 294 | }; 295 | 07AC767318C8CB9A007D5414 /* Release */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | COMBINE_HIDPI_IMAGES = YES; 299 | GCC_PRECOMPILE_PREFIX_HEADER = YES; 300 | GCC_PREFIX_HEADER = "REL_Linker/RELLinker-prefix.pch"; 301 | HEADER_SEARCH_PATHS = ( 302 | "$(inherited)", 303 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 304 | "$(SRCROOT)/../include", 305 | ); 306 | INFOPLIST_FILE = "REL_Linker/RELLinker-Info.plist"; 307 | INSTALL_PATH = "$(USER_LIBRARY_DIR)/Application Support/Hopper/PlugIns/v4/Tools"; 308 | PRODUCT_BUNDLE_IDENTIFIER = com.sappharad.RELLinker; 309 | PRODUCT_NAME = "$(TARGET_NAME)"; 310 | WRAPPER_EXTENSION = hopperTool; 311 | }; 312 | name = Release; 313 | }; 314 | /* End XCBuildConfiguration section */ 315 | 316 | /* Begin XCConfigurationList section */ 317 | 07AC765E18C8CB9A007D5414 /* Build configuration list for PBXProject "RELLinker" */ = { 318 | isa = XCConfigurationList; 319 | buildConfigurations = ( 320 | 07AC766F18C8CB9A007D5414 /* Debug */, 321 | 07AC767018C8CB9A007D5414 /* Release */, 322 | ); 323 | defaultConfigurationIsVisible = 0; 324 | defaultConfigurationName = Release; 325 | }; 326 | 07AC767118C8CB9A007D5414 /* Build configuration list for PBXNativeTarget "RELLinker" */ = { 327 | isa = XCConfigurationList; 328 | buildConfigurations = ( 329 | 07AC767218C8CB9A007D5414 /* Debug */, 330 | 07AC767318C8CB9A007D5414 /* Release */, 331 | ); 332 | defaultConfigurationIsVisible = 0; 333 | defaultConfigurationName = Release; 334 | }; 335 | /* End XCConfigurationList section */ 336 | }; 337 | rootObject = 07AC765B18C8CB9A007D5414 /* Project object */; 338 | } 339 | -------------------------------------------------------------------------------- /RELLinker.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /RELLinker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RELLinker/RELLinker-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | BNDL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | CFPlugInDynamicRegisterFunction 26 | 27 | CFPlugInDynamicRegistration 28 | NO 29 | CFPlugInFactories 30 | 31 | 00000000-0000-0000-0000-000000000000 32 | MyFactoryFunction 33 | 34 | CFPlugInTypes 35 | 36 | 00000000-0000-0000-0000-000000000000 37 | 38 | 00000000-0000-0000-0000-000000000000 39 | 40 | 41 | CFPlugInUnloadFunction 42 | 43 | NSHumanReadableCopyright 44 | Copyright © 2018 Jack Andersen. All rights reserved. 45 | NSPrincipalClass 46 | RELLinker 47 | 48 | 49 | -------------------------------------------------------------------------------- /RELLinker/RELLinker-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header 3 | // 4 | // The contents of this file are implicitly included at the beginning of every source file. 5 | // 6 | 7 | #ifdef __OBJC__ 8 | #import 9 | #endif 10 | -------------------------------------------------------------------------------- /RELLinker/RELLinker.h: -------------------------------------------------------------------------------- 1 | // 2 | // RELLinker.h 3 | // RELLinker 4 | // 5 | // Created by Jack Andersen on 18/03/2018. 6 | // Copyright (c) 2018 Jack Andersen. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface RELLinker : NSObject 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /RELLinker/RELLinker.m: -------------------------------------------------------------------------------- 1 | // 2 | // RELLinker.m 3 | // RELLinker 4 | // 5 | // Created by Jack Andersen on 18/03/2018. 6 | // Copyright (c) 2018 Jack Andersen. All rights reserved. 7 | // 8 | 9 | #import "RELLinker.h" 10 | #import 11 | #import 12 | #import 13 | #import 14 | #import 15 | #import 16 | #import 17 | #import 18 | #import 19 | #import 20 | 21 | #ifdef __APPLE__ 22 | #define YES_CONSTANT 1000 23 | #else 24 | #define YES_CONSTANT 1 25 | #endif 26 | 27 | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ 28 | static inline int16_t _bswap16(int16_t v) { 29 | return __builtin_bswap16(v); 30 | } 31 | static inline int32_t _bswap32(int32_t v) { 32 | return __builtin_bswap32(v); 33 | } 34 | #else 35 | static inline int16_t _bswap16(int16_t v) { 36 | return v; 37 | } 38 | static inline int32_t _bswap32(int32_t v) { 39 | return v; 40 | } 41 | #endif 42 | 43 | struct relhdr_info 44 | { 45 | uint32_t module_id; // in .rso or .rel, not in .sel 46 | 47 | // in .rso or .rel or .sel 48 | uint32_t prev; 49 | uint32_t next; 50 | uint32_t num_sections; 51 | uint32_t section_offset; // points to section_entry* 52 | uint32_t name_offset; 53 | uint32_t name_size; 54 | uint32_t version; 55 | }; 56 | 57 | struct relhdr 58 | { 59 | struct relhdr_info info; 60 | 61 | // version 1 62 | uint32_t bss_size; 63 | uint32_t rel_offset; 64 | uint32_t import_offset; 65 | uint32_t import_size; // size in bytes 66 | 67 | // Section ids containing functions 68 | uint8_t prolog_section; 69 | uint8_t epilog_section; 70 | uint8_t unresolved_section; 71 | uint8_t bss_section; 72 | 73 | uint32_t prolog_offset; 74 | uint32_t epilog_offset; 75 | uint32_t unresolved_offset; 76 | 77 | // version 2 78 | uint32_t align; 79 | uint32_t bss_align; 80 | 81 | // version 3 82 | uint32_t fix_size; 83 | }; 84 | 85 | struct rel_section_entry 86 | { 87 | uint32_t file_offset; 88 | uint32_t size; 89 | }; 90 | 91 | struct rel_import_entry 92 | { 93 | uint32_t module_id; // module id, maps to id in relhdr_info, 0 = base application 94 | uint32_t offset; 95 | }; 96 | 97 | enum PPCRelocationType 98 | { 99 | R_PPC_NONE, 100 | R_PPC_ADDR32, 101 | R_PPC_ADDR24, 102 | R_PPC_ADDR16, 103 | R_PPC_ADDR16_LO, 104 | R_PPC_ADDR16_HI, 105 | R_PPC_ADDR16_HA, 106 | R_PPC_ADDR14, 107 | R_PPC_ADDR14_BRTAKEN, 108 | R_PPC_ADDR14_BRNTAKEN, 109 | R_PPC_REL24, 110 | R_PPC_REL14, 111 | R_PPC_REL14_BRTAKEN, 112 | R_PPC_REL14_BRNTAKEN, 113 | R_DOLPHIN_NOP = 201, 114 | R_DOLPHIN_SECTION = 202, 115 | R_DOLPHIN_END = 203 116 | }; 117 | 118 | struct rel_relocation_entry 119 | { 120 | uint16_t advancement; 121 | uint8_t type; 122 | uint8_t section; 123 | uint32_t offset; 124 | }; 125 | 126 | @implementation RELLinker { 127 | NSObject *_services; 128 | } 129 | 130 | - (NSArray *)toolMenuDescription { 131 | return @[ 132 | @{HPM_TITLE: @"Link REL Segments", 133 | HPM_SELECTOR: @"linkREL:"}, 134 | ]; 135 | } 136 | 137 | - (void)_prelinkREL:(NSObject*)segment file:(NSObject*)file { 138 | const void *bytes = segment.mappedData.bytes; 139 | 140 | // Initial metadata 141 | segment.readable = YES; 142 | segment.writable = YES; 143 | segment.executable = YES; 144 | const struct relhdr *header = bytes; 145 | uint32_t moduleId = _bswap32(header->info.module_id); 146 | uint32_t version = _bswap32(header->info.version); 147 | uint32_t bssAlign = 32; 148 | if (version >= 2) 149 | bssAlign = _bswap32(header->bss_align); 150 | uint32_t bssSize = _bswap32(header->bss_size); 151 | segment.segmentName = [NSString stringWithFormat:@"REL%u", moduleId]; 152 | [_services.currentDocument beginToWait:[NSString stringWithFormat:@"Pre-Linking %@", segment.segmentName]]; 153 | 154 | // BSS starts here 155 | NSUInteger bssStart = (file.lastSegment.endAddress + bssAlign - 1) / bssAlign * bssAlign; 156 | 157 | // Advertize REL sections to Hopper 158 | const struct rel_section_entry *sections = bytes + _bswap32(header->info.section_offset); 159 | uint32_t numSections = _bswap32(header->info.num_sections); 160 | for (int i = 0; i < numSections; ++i) { 161 | const struct rel_section_entry *section = §ions[i]; 162 | uint32_t fileOffset = _bswap32(section->file_offset); 163 | uint32_t sectionSize = _bswap32(section->size); 164 | if (fileOffset == 0 && sectionSize == bssSize) { 165 | if (bssSize == 0) 166 | continue; 167 | NSObject *seg = [file addSegmentAt:bssStart size:bssSize]; 168 | seg.readable = YES; 169 | seg.writable = YES; 170 | seg.executable = NO; 171 | [seg setMappedData:[NSMutableData dataWithLength:bssSize]]; 172 | seg.segmentName = [NSString stringWithFormat:@"BSS%u", moduleId]; 173 | NSObject *sec = [seg addSectionAt:bssStart size:bssSize]; 174 | sec.sectionName = [NSString stringWithFormat:@"bss%u", moduleId]; 175 | sec.zeroFillSection = YES; 176 | } else if (sectionSize != 0) { 177 | NSObject *sec = [segment addSectionAt:segment.startAddress + (fileOffset & 0xfffffffe) size:sectionSize]; 178 | if (fileOffset & 0x1) { 179 | sec.pureCodeSection = YES; 180 | sec.containsCode = YES; 181 | sec.sectionName = [NSString stringWithFormat:@"text%u_%u", moduleId, i]; 182 | } else { 183 | sec.pureDataSection = YES; 184 | sec.sectionName = [NSString stringWithFormat:@"data%u_%u", moduleId, i]; 185 | } 186 | } 187 | } 188 | } 189 | 190 | - (void)recursiveMakeProcedures:(NSObject*)proc file:(NSObject*)file { 191 | if (!proc) 192 | return; 193 | NSArray *callees = [proc.allCallees copy]; 194 | for (NSObject *cref in callees) { 195 | if (![file hasProcedureAt:cref.to]) 196 | [self recursiveMakeProcedures:[file makeProcedureAt:cref.to] file:file]; 197 | } 198 | } 199 | 200 | - (void)_linkREL:(NSObject*)segment file:(NSObject*)file { 201 | [_services.currentDocument beginToWait:[NSString stringWithFormat:@"Linking %@", segment.segmentName]]; 202 | NSMutableData* mutable = [segment.mappedData mutableCopy]; 203 | void *bytes = mutable.mutableBytes; 204 | 205 | // Initial metadata 206 | const struct relhdr *header = bytes; 207 | uint32_t moduleId = _bswap32(header->info.module_id); 208 | uint32_t version = _bswap32(header->info.version); 209 | uint32_t bssAlign = 32; 210 | if (version >= 2) 211 | bssAlign = _bswap32(header->bss_align); 212 | uint32_t bssSize = _bswap32(header->bss_size); 213 | 214 | // Advertize REL sections to Hopper 215 | const struct rel_section_entry *sections = bytes + _bswap32(header->info.section_offset); 216 | uint32_t numSections = _bswap32(header->info.num_sections); 217 | NSMutableArray *textSections = [NSMutableArray arrayWithCapacity:4]; 218 | NSMutableArray *dataSections = [NSMutableArray arrayWithCapacity:8]; 219 | for (int i = 0; i < numSections; ++i) { 220 | const struct rel_section_entry *section = §ions[i]; 221 | uint32_t fileOffset = _bswap32(section->file_offset); 222 | uint32_t sectionSize = _bswap32(section->size); 223 | if (fileOffset == 0 && sectionSize == bssSize) { 224 | } else if (sectionSize != 0) { 225 | NSObject *sec = [file sectionForVirtualAddress:segment.startAddress + (fileOffset & 0xfffffffe)]; 226 | if (fileOffset & 0x1) 227 | [textSections addObject:sec]; 228 | else 229 | [dataSections addObject:sec]; 230 | } 231 | } 232 | 233 | // Enumerate imports and relocations 234 | const struct rel_import_entry *imports = bytes + _bswap32(header->import_offset); 235 | uint32_t numImports = _bswap32(header->import_size) / 8; 236 | for (int i = 0; i < numImports; ++i) { 237 | const struct rel_import_entry *import = &imports[i]; 238 | uint32_t moduleId = _bswap32(import->module_id); 239 | NSObject *moduleSeg = nil; 240 | NSObject *moduleBss = nil; 241 | const struct rel_section_entry *moduleSections = NULL; 242 | if (moduleId) { 243 | moduleSeg = [file segmentNamed:[NSString stringWithFormat:@"REL%u", moduleId]]; 244 | moduleBss = [file segmentNamed:[NSString stringWithFormat:@"BSS%u", moduleId]]; 245 | if (moduleSeg) { 246 | const void *moduleBytes = moduleSeg.mappedData.bytes; 247 | const struct relhdr *moduleHeader = moduleBytes; 248 | moduleSections = moduleBytes + _bswap32(moduleHeader->info.section_offset); 249 | } 250 | } 251 | const struct rel_relocation_entry *relocation = bytes + _bswap32(import->offset); 252 | void *codePtr = NULL; 253 | for (bool done = false; !done; ++relocation) { 254 | uint16_t advancement = _bswap16(relocation->advancement); 255 | uint32_t offset = _bswap32(relocation->offset); 256 | if (moduleSeg) { 257 | const struct rel_section_entry *modSec = &moduleSections[relocation->section]; 258 | uint32_t fileOffset = _bswap32(modSec->file_offset); 259 | if (!fileOffset) 260 | offset += moduleBss.startAddress; 261 | else 262 | offset += moduleSeg.startAddress + (fileOffset & 0xfffffffe); 263 | } 264 | codePtr += advancement; 265 | Address thisAddr = segment.startAddress + (codePtr - bytes); 266 | //[[_services currentDocument] logInfoMessage:[NSString stringWithFormat:@"RELOC %08X %d %08X", (uint32_t)thisAddr, relocation->type, offset]]; 267 | switch (relocation->type) { 268 | default: 269 | break; 270 | case R_PPC_ADDR32: 271 | *(uint32_t*)codePtr = _bswap32(offset); 272 | break; 273 | case R_PPC_ADDR24: { 274 | uint32_t inst = _bswap32(*(uint32_t*)codePtr); 275 | inst &= 0xFC000003; 276 | inst |= offset & 0x3FFFFFC; 277 | *(uint32_t*)codePtr = _bswap32(inst); 278 | break; 279 | } 280 | case R_PPC_ADDR16: 281 | *(uint16_t*)codePtr = _bswap16((uint16_t)offset); 282 | break; 283 | case R_PPC_ADDR16_LO: 284 | *(uint16_t*)codePtr = _bswap16((uint16_t)offset); 285 | break; 286 | case R_PPC_ADDR16_HI: 287 | *(uint16_t*)codePtr = _bswap16((uint16_t)(offset >> 16)); 288 | break; 289 | case R_PPC_ADDR16_HA: 290 | if (offset & 0x8000) 291 | *(uint16_t*)codePtr = _bswap16((uint16_t)((offset >> 16) + 1)); 292 | else 293 | *(uint16_t*)codePtr = _bswap16((uint16_t)(offset >> 16)); 294 | break; 295 | case R_PPC_ADDR14: { 296 | uint32_t inst = _bswap32(*(uint32_t*)codePtr); 297 | inst &= 0xFFFF0003; 298 | inst |= offset & 0xFFFC; 299 | *(uint32_t*)codePtr = _bswap32(inst); 300 | break; 301 | } 302 | case R_PPC_ADDR14_BRTAKEN: { 303 | uint32_t inst = _bswap32(*(uint32_t*)codePtr); 304 | inst &= 0xFFDF0003; 305 | inst |= (offset & 0xFFFC) | 0x200000; 306 | *(uint32_t*)codePtr = _bswap32(inst); 307 | break; 308 | } 309 | case R_PPC_ADDR14_BRNTAKEN: { 310 | uint32_t inst = _bswap32(*(uint32_t*)codePtr); 311 | inst &= 0xFFDF0003; 312 | inst |= offset & 0xFFFC; 313 | *(uint32_t*)codePtr = _bswap32(inst); 314 | break; 315 | } 316 | case R_PPC_REL24: { 317 | uint32_t inst = _bswap32(*(uint32_t*)codePtr); 318 | inst &= 0xFC000003; 319 | inst |= ((int64_t)offset - (int64_t)thisAddr) & 0x3FFFFFC; 320 | *(uint32_t*)codePtr = _bswap32(inst); 321 | break; 322 | } 323 | case R_PPC_REL14: { 324 | uint32_t inst = _bswap32(*(uint32_t*)codePtr); 325 | inst &= 0xFFFF0003; 326 | inst |= ((int64_t)offset - (int64_t)thisAddr) & 0xFFFC; 327 | *(uint32_t*)codePtr = _bswap32(inst); 328 | break; 329 | } 330 | case R_PPC_REL14_BRTAKEN: { 331 | uint32_t inst = _bswap32(*(uint32_t*)codePtr); 332 | inst &= 0xFFFF0003; 333 | inst |= (((int64_t)offset - (int64_t)thisAddr) & 0xFFFC) | 0x200000; 334 | *(uint32_t*)codePtr = _bswap32(inst); 335 | break; 336 | } 337 | case R_PPC_REL14_BRNTAKEN: { 338 | uint32_t inst = _bswap32(*(uint32_t*)codePtr); 339 | inst &= 0xFFDF0003; 340 | inst |= ((int64_t)offset - (int64_t)thisAddr) & 0xFFFC; 341 | *(uint32_t*)codePtr = _bswap32(inst); 342 | break; 343 | } 344 | case R_DOLPHIN_SECTION: { 345 | const struct rel_section_entry *section = §ions[relocation->section]; 346 | uint32_t fileOffset = _bswap32(section->file_offset); 347 | codePtr = bytes + (fileOffset & 0xfffffffe); 348 | break; 349 | } 350 | case R_DOLPHIN_END: 351 | done = true; 352 | break; 353 | } 354 | } 355 | } 356 | 357 | [_services.currentDocument beginToWait:[NSString stringWithFormat:@"Analyzing %@", segment.segmentName]]; 358 | 359 | // Apply relocated data 360 | [segment setMappedData:mutable]; 361 | 362 | // Prolog 363 | if (header->prolog_section) { 364 | NSObject *sec = [file sectionNamed: 365 | [NSString stringWithFormat:@"text%u_%u", moduleId, header->prolog_section]]; 366 | if (sec) { 367 | uint32_t offset = _bswap32(header->prolog_offset); 368 | Address addr = sec.startAddress + offset; 369 | [self recursiveMakeProcedures:[file makeProcedureAt:addr] file:file]; 370 | [file setName:[NSString stringWithFormat:@"_prolog%u", moduleId] forVirtualAddress:addr 371 | reason:NCReason_Import]; 372 | } 373 | } 374 | 375 | // Epilog 376 | if (header->epilog_section) { 377 | NSObject *sec = [file sectionNamed: 378 | [NSString stringWithFormat:@"text%u_%u", moduleId, header->epilog_section]]; 379 | if (sec) { 380 | uint32_t offset = _bswap32(header->epilog_offset); 381 | Address addr = sec.startAddress + offset; 382 | [self recursiveMakeProcedures:[file makeProcedureAt:addr] file:file]; 383 | [file setName:[NSString stringWithFormat:@"_epilog%u", moduleId] forVirtualAddress:addr 384 | reason:NCReason_Import]; 385 | } 386 | } 387 | 388 | // Unresolved 389 | if (header->unresolved_section) { 390 | NSObject *sec = [file sectionNamed: 391 | [NSString stringWithFormat:@"text%u_%u", moduleId, header->unresolved_section]]; 392 | if (sec) { 393 | uint32_t offset = _bswap32(header->unresolved_offset); 394 | Address addr = sec.startAddress + offset; 395 | [self recursiveMakeProcedures:[file makeProcedureAt:addr] file:file]; 396 | [file setName:[NSString stringWithFormat:@"_unresolved%u", moduleId] forVirtualAddress:addr 397 | reason:NCReason_Import]; 398 | } 399 | } 400 | 401 | // Analyze text sections 402 | NSObject *context = [file buildCPUContext]; 403 | for (NSObject *sec in textSections) { 404 | for (Address addr = sec.startAddress; addr < sec.endAddress;) { 405 | NSUInteger padding = [context detectedPaddingLengthAt:addr]; 406 | if (padding) { 407 | [file setType:Type_Align atVirtualAddress:addr forLength:padding]; 408 | addr += padding; 409 | } 410 | if (![file hasProcedureAt:addr]) 411 | [self recursiveMakeProcedures:[file makeProcedureAt:addr] file:file]; 412 | addr += 4; 413 | } 414 | } 415 | 416 | // Analyze data sections 417 | for (NSObject *sec in dataSections) { 418 | for (uint64_t offset = 0; offset < sec.fileLength; offset += 4) { 419 | uint32_t data = _bswap32(*(uint32_t*)(bytes + sec.fileOffset + offset)); 420 | if (data >= 0x80000000 && data <= 0x8C000000) { 421 | Address addr = sec.startAddress + offset; 422 | [file setType:Type_Int32 atVirtualAddress:addr forLength:4]; 423 | [file setFormat:Format_Address forArgument:0 atVirtualAddress:addr]; 424 | [segment addReferencesToAddress:data fromAddress:addr]; 425 | } 426 | } 427 | } 428 | } 429 | 430 | - (void)linkREL:(id)sender { 431 | dispatch_async(dispatch_get_main_queue(), ^{ 432 | NSObject *doc = [_services currentDocument]; 433 | [doc beginToWait:@"Finding REL Data"]; 434 | NSMutableArray *linkedSegments = [NSMutableArray new]; 435 | bool found = false; 436 | for (NSObject *seg in doc.disassembledFile.segments) { 437 | if ([seg.segmentName isEqualToString:@"unnamed segment"]) { 438 | const void *bytes = seg.mappedData.bytes; 439 | const struct relhdr *header = bytes; 440 | uint32_t module_id = _bswap32(header->info.module_id); 441 | if (module_id == 0 || _bswap32(header->info.prev) != 0 || _bswap32(header->info.next) != 0) 442 | continue; 443 | uint32_t version = _bswap32(header->info.version); 444 | if (version != 1 && version != 2 && version != 3) 445 | continue; 446 | 447 | uint32_t sectionInfoOff = _bswap32(header->info.section_offset); 448 | uint32_t importTableOff = _bswap32(header->import_offset); 449 | if (sectionInfoOff >= seg.mappedData.length || importTableOff >= seg.mappedData.length) 450 | continue; 451 | 452 | // Valid REL 453 | NSInteger result = [doc displayAlertWithMessageText:@"Detected REL" 454 | defaultButton:@"Yes" 455 | alternateButton:@"No" 456 | otherButton:nil 457 | informativeText: 458 | [NSString stringWithFormat:@"Found REL data at 0x%" PRIX64 ". Link?", seg.startAddress]]; 459 | found = true; 460 | if (result == YES_CONSTANT) 461 | [linkedSegments addObject:seg]; 462 | } 463 | } 464 | 465 | if (!found) { 466 | [doc displayAlertWithMessageText:@"Unnamed REL segment not detected" 467 | defaultButton:@"OK" 468 | alternateButton:nil 469 | otherButton:nil 470 | informativeText:@"Unable to find \"unnamed segment\" containing REL data"]; 471 | [doc endWaiting]; 472 | return; 473 | } 474 | 475 | for (NSObject *seg in linkedSegments) { 476 | // First pass creates Hopper sections 477 | [self _prelinkREL:seg file:doc.disassembledFile]; 478 | } 479 | 480 | for (NSObject *seg in linkedSegments) { 481 | // Second pass resolves relocations from all candidate REL modules 482 | [self _linkREL:seg file:doc.disassembledFile]; 483 | } 484 | 485 | [doc endWaiting]; 486 | }); 487 | } 488 | 489 | - (instancetype)initWithHopperServices:(NSObject *)services { 490 | if (self = [super init]) { 491 | _services = services; 492 | } 493 | return self; 494 | } 495 | 496 | - (HopperUUID *)pluginUUID { 497 | return [_services UUIDWithString:@"42056020-EB6F-469F-8E78-425CC767B145"]; 498 | } 499 | 500 | - (HopperPluginType)pluginType { 501 | return Plugin_Tool; 502 | } 503 | 504 | - (NSString *)pluginName { 505 | return @"REL Linker"; 506 | } 507 | 508 | - (NSString *)pluginDescription { 509 | return @"Dolphin Relocatable (REL) Linker"; 510 | } 511 | 512 | - (NSString *)pluginAuthor { 513 | return @"Jack Andersen"; 514 | } 515 | 516 | - (NSString *)pluginCopyright { 517 | return @"© Jack Andersen"; 518 | } 519 | 520 | - (NSString *)pluginVersion { 521 | return @"0.0.1"; 522 | } 523 | 524 | - (NSString *)commandLineIdentifier { 525 | return @"REL"; 526 | } 527 | 528 | @end 529 | -------------------------------------------------------------------------------- /RELLinker/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | --------------------------------------------------------------------------------