class-dump,分析可执行/app文件的函数定义

本文通过使用class-dump工具解析GKDemo应用,详细展示了应用目录结构、类定义及协议接口,揭示了Objective-C类及其属性、方法的内部实现。

class-dump是一个开源项目,可以从http://www.codethecode.com/projects/class-dump/下载。

下面是我对自己写的一个demo程序,GKDemo.app用class-dump解析后得到的结果:

note: /Users/tonys/Library/Developer/Xcode/DerivedData/GKDemo-bgpwxsribkdlyphduyucdubnqbed/Build/Products/Debug-iphoneos/GKDemo.app是app所在的目录。

nicktest:Work tonys$ ./class-dump  /Users/tonys/Library/Developer/Xcode/DerivedData/GKDemo-bgpwxsribkdlyphduyucdubnqbed/Build/Products/Debug-iphoneos/GKDemo.app
/*
 *     Generated by class-dump 3.3.4 (64 bit).
 *
 *     class-dump is Copyright (C) 1997-1998, 2000-2001, 2004-2011 by Steve Nygard.
 */


#pragma mark Named Structures


struct CGPoint {
    float _field1;
    float _field2;
};


struct CGRect {
    struct CGPoint _field1;
    struct CGSize _field2;
};


struct CGSize {
    float _field1;
    float _field2;
};


#pragma mark -


/*
 * File: /Users/tonys/Library/Developer/Xcode/DerivedData/GKDemo-bgpwxsribkdlyphduyucdubnqbed/Build/Products/Debug-iphoneos/GKDemo.app/GKDemo
 * UUID: 15E9B62A-A4F6-3717-BE4C-997222F9F9DB
 * Arch: arm v7 (armv7)
 *       Minimum iOS version: 4.3.0
 *
 *       Objective-C Garbage Collection: Unsupported
 */


@protocol GKPeerPickerControllerDelegate <NSObject>


@optional
- (void)peerPickerControllerDidCancel:(id)arg1;
- (void)peerPickerController:(id)arg1 didConnectPeer:(id)arg2 toSession:(id)arg3;
- (id)peerPickerController:(id)arg1 sessionForConnectionType:(unsigned int)arg2;
- (void)peerPickerController:(id)arg1 didSelectConnectionType:(unsigned int)arg2;
@end


@protocol GKSessionDelegate <NSObject>


@optional
- (void)session:(id)arg1 didFailWithError:(id)arg2;
- (void)session:(id)arg1 connectionWithPeerFailed:(id)arg2 withError:(id)arg3;
- (void)session:(id)arg1 didReceiveConnectionRequestFromPeer:(id)arg2;
- (void)session:(id)arg1 peer:(id)arg2 didChangeState:(int)arg3;
@end


@protocol NSObject
- (id)debugDescription;
- (id)description;
- (unsigned int)retainCount;
- (id)autorelease;
- (oneway void)release;
- (id)retain;
- (BOOL)respondsToSelector:(SEL)arg1;
- (BOOL)conformsToProtocol:(id)arg1;
- (BOOL)isMemberOfClass:(Class)arg1;
- (BOOL)isKindOfClass:(Class)arg1;
- (BOOL)isProxy;
- (id)performSelector:(SEL)arg1 withObject:(id)arg2 withObject:(id)arg3;
- (id)performSelector:(SEL)arg1 withObject:(id)arg2;
- (id)performSelector:(SEL)arg1;
- (struct _NSZone *)zone;
- (id)self;
- (Class)class;
- (Class)superclass;
- (unsigned int)hash;
- (BOOL)isEqual:(id)arg1;
@end


@protocol UIApplicationDelegate <NSObject>


@optional
@property(retain, nonatomic) UIWindow *window;
- (void)applicationProtectedDataDidBecomeAvailable:(id)arg1;
- (void)applicationProtectedDataWillBecomeUnavailable:(id)arg1;
- (void)applicationWillEnterForeground:(id)arg1;
- (void)applicationDidEnterBackground:(id)arg1;
- (void)application:(id)arg1 didReceiveLocalNotification:(id)arg2;
- (void)application:(id)arg1 didReceiveRemoteNotification:(id)arg2;
- (void)application:(id)arg1 didFailToRegisterForRemoteNotificationsWithError:(id)arg2;
- (void)application:(id)arg1 didRegisterForRemoteNotificationsWithDeviceToken:(id)arg2;
- (void)application:(id)arg1 didChangeStatusBarFrame:(struct CGRect)arg2;
- (void)application:(id)arg1 willChangeStatusBarFrame:(struct CGRect)arg2;
- (void)application:(id)arg1 didChangeStatusBarOrientation:(int)arg2;
- (void)application:(id)arg1 willChangeStatusBarOrientation:(int)arg2 duration:(double)arg3;
- (void)applicationSignificantTimeChange:(id)arg1;
- (void)applicationWillTerminate:(id)arg1;
- (void)applicationDidReceiveMemoryWarning:(id)arg1;
- (BOOL)application:(id)arg1 openURL:(id)arg2 sourceApplication:(id)arg3 annotation:(id)arg4;
- (BOOL)application:(id)arg1 handleOpenURL:(id)arg2;
- (void)applicationWillResignActive:(id)arg1;
- (void)applicationDidBecomeActive:(id)arg1;
- (BOOL)application:(id)arg1 didFinishLaunchingWithOptions:(id)arg2;
- (void)applicationDidFinishLaunching:(id)arg1;
@end


@interface __ARCLite__
{
    Class isa;
}


+ (void)load;


@end


@interface AppDelegate : UIResponder <UIApplicationDelegate>
{
    UIWindow *_window;
    ViewController *_viewController;
}


@property(retain, nonatomic) ViewController *viewController; // @synthesize viewController=_viewController;
@property(retain, nonatomic) UIWindow *window; // @synthesize window=_window;
- (void).cxx_destruct;
- (void)applicationWillTerminate:(id)arg1;
- (void)applicationDidBecomeActive:(id)arg1;
- (void)applicationWillEnterForeground:(id)arg1;
- (void)applicationDidEnterBackground:(id)arg1;
- (void)applicationWillResignActive:(id)arg1;
- (BOOL)application:(id)arg1 didFinishLaunchingWithOptions:(id)arg2;


@end


@interface ViewController : UIViewController <GKPeerPickerControllerDelegate, GKSessionDelegate>
{
    NSMutableArray *_touchesValue;
    NSMutableArray *_peerTouchesValue;
    GKPeerPickerController *_gamePickerController;
    GKSession *_gameSession;
}


@property(retain, nonatomic) GKSession *gameSession; // @synthesize gameSession=_gameSession;
@property(retain, nonatomic) GKPeerPickerController *gamePickerController; // @synthesize gamePickerController=_gamePickerController;
@property(retain, nonatomic) NSMutableArray *peerTouchesValue; // @synthesize peerTouchesValue=_peerTouchesValue;
@property(retain, nonatomic) NSMutableArray *touchesValue; // @synthesize touchesValue=_touchesValue;
- (void).cxx_destruct;
- (void)receiveData:(id)arg1 fromPeer:(id)arg2 inSession:(id)arg3 context:(void *)arg4;
- (void)session:(id)arg1 peer:(id)arg2 didChangeState:(int)arg3;
- (void)session:(id)arg1 didReceiveConnectionRequestFromPeer:(id)arg2;
- (void)peerPickerControllerDidCancel:(id)arg1;
- (void)peerPickerController:(id)arg1 didConnectPeer:(id)arg2 toSession:(id)arg3;
- (id)peerPickerController:(id)arg1 sessionForConnectionType:(unsigned int)arg2;
- (void)peerPickerController:(id)arg1 didSelectConnectionType:(unsigned int)arg2;
- (void)startGame:(id)arg1;
- (id)methodSignatureForSelector:(SEL)arg1;
- (void)oneParamMethod:(id)arg1 second:(id)arg2;
- (void)forwardInvocation:(id)arg1;
- (BOOL)shouldAutorotateToInterfaceOrientation:(int)arg1;
- (void)viewDidDisappear:(BOOL)arg1;
- (void)viewWillDisappear:(BOOL)arg1;
- (void)viewDidAppear:(BOOL)arg1;
- (void)viewWillAppear:(BOOL)arg1;
- (void)touchesCancelled:(id)arg1 withEvent:(id)arg2;
- (void)touchesEnded:(id)arg1 withEvent:(id)arg2;
- (void)touchesMoved:(id)arg1 withEvent:(id)arg2;
- (void)touchesBegan:(id)arg1 withEvent:(id)arg2;
- (void)updateTouches;
- (void)addTouchesValue:(struct CGPoint)arg1;
- (void)viewDidUnload;
- (void)viewDidLoad;
- (void)didReceiveMemoryWarning;


@end


@interface MyObject : NSObject
{
}


- (void)didnotexistmethod:(id)arg1 second:(unsigned int)arg2;


@end

<think>我们正在处理一个Android原生库加载时发生的段错误(SIGSEGV),具体错误是SIGSEGVSEGV_ACCERR。这通常表示内存访问权限问题,即尝试访问了没有权限的内存地址。错误发生在加载libSdkyclx_clx.so时。 步骤分析: 1. **理解错误**:SIGSEGV(信号11)表示无效的内存访问。SEGV_ACCERR子错误码表示进程试图访问其没有权限访问的内存地址(例如,试图写入只读内存)。2.**发生时机**:错误发生在加载共享库(.so文件)时。这通常意味着在加载过程中,动态链接器(如`ld-android.so`)在解析库的符号或重定位时遇到了问题。 3. **可能原因**:-**库文件损坏**:下载或构建的.so文件可能已损坏。-**ABI不匹配**:库是为不同的CPU架构(如arm64-v8a, armeabi-v7a)编译的,而设备是另一种架构。- **内存布局问题**:可能是由于地址空间布局随机化(ASLR)或其他内存保护机制导致的问题。-**权限问题**:.so文件可能没有正确的文件权限,或者存储位置不可访问。-**依赖问题**:该库依赖的其他库缺失或版本不匹配。- **代码问题**:库中的代码可能存在错误,例如在初始化过程中访问了无效内存。 4. **调试方法**:-**检查日志**:除了崩溃日志外,查看`logcat`输出的完整日志,寻找加载库时的警告或错误。- **检查文件**:验证.so文件的完整性(如md5sum)、文件权限和位置(是否在正确的ABI目录下,如`lib/arm64-v8a`)。- **使用调试工具**:使用`addr2line`或`ndk-stack`将崩溃地址转换为源代码位置。需要带调试符号的库版本(unstripped.so)。-**复现步骤**:尝试在模拟器或不同设备上复现,以确定是否与特定设备相关。5.**解决方案**:-**确保ABI匹配**:检查应用的`build.gradle`中`abiFilters`设置,确保包含设备支持的ABI,并且.so文件放在对应的jniLibs目录下。-**更新库**:如果可能,尝试更新该库到最新版本,或联系库的提供者报告问题。-**检查依赖**:使用`readelf -d libSdkyclx_clx.so`查看该库的依赖,确保所有依赖库都存在且兼容。- **内存分析**:如果问题随机出现,可能与内存破坏有关。可以使用AddressSanitizer(ASan)进行内存错误检测。具体操作:###1.检查ABI确保你的应用打包了正确的ABI。在`build.gradle`中: ```groovyandroid{defaultConfig{ndk{abiFilters 'arm64-v8a','armeabi-v7a' //根据你的库支持的架构设置}} }```###2.使用ndk-stack解析崩溃日志将崩溃日志保存到文件(例如`crash.log`),然后运行: ```bashndk-stack-sympath/to/your/project/obj/local/arm64-v8a/ -dump crash.log``` 注意:`-sym`参数指向包含带符号的.so文件的目录(通常是编译后obj/local下的对应ABI目录)。 ###3.检查库的依赖使用NDK中的`readelf`(在NDK目录的toolchains下)检查库的依赖:```bash$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-readelf-dlibSdkyclx_clx.so``` 查看`(NEEDED)`项,确保这些依赖库都存在。###4.使用AddressSanitizer在`build.gradle`中启用ASan:```groovyandroid {defaultConfig {externalNativeBuild{cmake{arguments "-DANDROID_STL=c++_shared","-DANDROID_TOOLCHAIN=clang"cppFlags "-fsanitize=address-fno-omit-frame-pointer"}}packagingOptions {doNotStrip'**.so'//保留调试符号}}} ```然后重新构建并运行应用。ASan会在发生内存错误时输出详细日志。 ###5.检查文件权限和完整性确认.so文件在APK中正确打包,并且在设备上解压后具有可执行权限。可以使用`adb shell`检查设备上该文件的权限:```bashadbshell ls-l/data/app/your.package.name/lib/arm64/```如果以上步骤无法解决问题,可能需要库的提供者协助,或者提供更详细的崩溃堆栈。相关问题: 1.如何分析Android原生库崩溃的其他常见信号(如SIGABRT, SIGILL)? 2.如何为Android应用启用AddressSanitizer进行内存错误检测?3.如何获取带符号的崩溃堆栈(symbolicated stacktrace)?</think>### Android Fatal signal 11 (SIGSEGV) SEGV_ACCERR 分析 当加载 `libSdkyclx_clx.so` 时出现 `SIGSEGV SEGV_ACCERR` 错误,表示**内存访问权限冲突**。这是原生代码(C/C++)崩溃的常见原因,通常发生在以下场景: ```mermaid graph TD A[SIGSEGV SEGV_ACCERR] --> B[内存访问权限错误] B --> C{可能原因} C --> D[尝试写入只读内存] C --> E[访问未映射的内存区域] C --> F[内存对齐问题] C --> G[库文件损坏] ``` #### 常见原因及解决方案 1. **内存权限冲突** - **现象**:尝试修改只读内存(如代码段)或访问未映射地址 - **检查**: ```bash # 查看内存映射权限 adb shell cat /proc/<pid>/maps | grep libSdkyclx_clx ``` 输出示例: ``` 70000000-7000c000 r-xp # r-xp 表示只读可执行 ``` 2. **库文件问题** - **ABI不匹配**:确保 `.so` 文件与设备CPU架构一致(arm64-v8a/armeabi-v7a) - **文件损坏**: ```bash # 检查库文件完整性 adb shell md5sum /data/app/.../lib/arm64/libSdkyclx_clx.so ``` 与编译后的原始文件MD5对比 3. **初始化代码缺陷** - 静态变量初始化期间访问无效内存 - JNI_OnLoad 函数中的错误操作 - 解决方案:使用 **AddressSanitizer (ASan)** 检测: ```gradle // build.gradle android { defaultConfig { externalNativeBuild { cmake { arguments "-DANDROID_ARM_MODE=arm", "-DANDROID_STL=c++_shared" cppFlags "-fsanitize=address -fno-omit-frame-pointer" } } } } ``` 4. **线程竞争** - 多线程同时初始化库导致的竞态条件 - 解决方案:添加互斥锁保护初始化逻辑 #### 诊断步骤 1. **获取完整堆栈** ```bash adb logcat -b crash | grep "Fatal signal" ``` 关键输出示例: ``` backtrace: #00 pc 0003c450 /data/app/.../lib/arm64/libSdkyclx_clx.so (BuildId: ...) #01 pc 00012345 /data/app/.../lib/arm64/libSdkyclx_clx.so (foo_init+112) ``` 2. **符号解析** ```bash # 使用 ndk-stack 解析地址 adb logcat | $NDK/ndk-stack -sym project/obj/local/arm64-v8a/ ``` 3. **检查内存权限** ```c // 在可疑代码处添加检查 #include <sys/mman.h> void* addr = ...; int prot = PROT_READ | PROT_WRITE; // 检查所需权限 if (mprotect(addr, size, prot) == -1) { __android_log_print("Permission denied at %p", addr); } ``` #### 预防措施 - 使用 **mmap 替代 malloc** 分配对齐内存: ```c void* alloc_aligned(size_t size) { return mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); } ``` - 启用 **CFI(控制流完整性)**: ```gradle externalNativeBuild { cmake { cppFlags "-flto -fsanitize=cfi" } } ``` > **关键提示**:SEGV_ACCERR 常发生在 ARMv8 设备上,因严格的内存对齐要求导致。建议使用 `memalign` 分配 64 字节对齐内存[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值