iOS 寄存器编程引起的编译问题

2021-06-21  本文已影响0人  算命的李老师

一、寄存器编程代码

1.实例

在某些组件运用到了寄存器相关编程,目的是为了调出当前运行堆栈,部分应用代码如下:

uintptr_t xol_mach_linkRegister(mcontext_t const machineContext){
#if defined(__i386__) || defined(__x86_64__)
    return 0;
#endif
#if defined(__arm64__) || defined(__arm__)
    return machineContext->__ss.__lr;
#endif
    return 0;
}

直接拿到 __ss 对应结构体看到 (以手机真机运行环境举例)

_STRUCT_MCONTEXT64
{
    _STRUCT_ARM_EXCEPTION_STATE64   __es;
    _STRUCT_ARM_THREAD_STATE64      __ss;
    _STRUCT_ARM_NEON_STATE64        __ns;
};

打开 _STRUCT_ARM_THREAD_STATE64 看到

#define _STRUCT_ARM_THREAD_STATE64 struct __darwin_arm_thread_state64
_STRUCT_ARM_THREAD_STATE64
{
    __uint64_t __x[29]; /* General purpose registers x0-x28 */
    __uint64_t __fp;    /* Frame pointer x29 */
    __uint64_t __lr;    /* Link register x30 */
    __uint64_t __sp;    /* Stack pointer x31 */
    __uint64_t __pc;    /* Program counter */
    __uint32_t __cpsr;  /* Current program status register */
    __uint32_t __pad;   /* Same size for 32-bit or 64-bit clients */
};

项目里用到的属性有

    __uint64_t __fp;    /* Frame pointer x29 */
    __uint64_t __lr;    /* Link register x30 */
    __uint64_t __sp;    /* Stack pointer x31 */
    __uint64_t __pc;    /* Program counter */
2. 寄存器对应设备:

解释一下几个宏:
i386 & x86 : 电脑模拟器
__arm64__ : 现有的终端设备 (iPhone 12等)
__arm__:包含armv7,armv7s,(iphone4,ipad2等)

本组件不支持armv7与模拟器环境,但是由于是平台化app,各团队能修改的权限有限,如果主工程支持armv7,那么就要支持对应的打包环境。

二、打包之路

1.发布组件

pod lib lint 不通过

不同编译环境下的不同头文件路径:

由于对不同编译环境的支持会引用到不同的宏,如:
arm

//文件路径 iOS 14.4 -> user/include/i386/_mcontext.h
_STRUCT_MCONTEXT64
{
    _STRUCT_ARM_EXCEPTION_STATE64   __es;
    _STRUCT_ARM_THREAD_STATE64      __ss;
    _STRUCT_ARM_NEON_STATE64        __ns;
};

i386

//文件路径 Simulator iOS 14.4 -> user/include/i386/_mcontext.h
_STRUCT_MCONTEXT32
{
    _STRUCT_X86_EXCEPTION_STATE32   __es;
    _STRUCT_X86_THREAD_STATE32      __ss;
    _STRUCT_X86_FLOAT_STATE32       __fs;
};

对应的 __ss能指向的寄存器名称也不一样

//文件路径 iOS 14.4 -> user/include/mach/arm/_structs.h
// arm64
_STRUCT_ARM_THREAD_STATE64
{
    __uint64_t __x[29]; /* General purpose registers x0-x28 */
    __uint64_t __fp;    /* Frame pointer x29 */
    __uint64_t __lr;    /* Link register x30 */
    __uint64_t __sp;    /* Stack pointer x31 */
    __uint64_t __pc;    /* Program counter */
    __uint32_t __cpsr;  /* Current program status register */
    __uint32_t __pad;   /* Same size for 32-bit or 64-bit clients */
};

// i386
//文件路径 Simulator iOS 14.4 -> user/include/mach/i386/_structs.h
_STRUCT_X86_THREAD_STATE32
{
    unsigned int    __eax;
    unsigned int    __ebx;
    unsigned int    __ecx;
    unsigned int    __edx;
    unsigned int    __edi;
    unsigned int    __esi;
    unsigned int    __ebp;
    unsigned int    __esp;
    unsigned int    __ss;
    unsigned int    __eflags;
    unsigned int    __eip;
    unsigned int    __cs;
    unsigned int    __ds;
    unsigned int    __es;
    unsigned int    __fs;
    unsigned int    __gs;
};

所以项目里代码写法:

#if defined(__i386__) || defined(__x86_64__)
    return 0;
#endif
#if defined(__arm64__) || defined(__arm__)
    return machineContext->__ss.__pc;
#endif
报错:pod lib lint

pod lib lint 时模拟器无法找到__arm64__环境

解决方案:在podspec文件加上
s.libraries = 'stdc++'

2.打包framework

pod 成功后,由于是平台化开发,所以主模块需要打包framework,主模块又引入了当前组件,当前组件引起报错

报错: 平台framework打包报错

error: no member named '__pc' in 'struct __darwin_arm_thread_state64' error: no member named '__lr' in 'struct __darwin_arm_thread_state64' error: no member named '__fp' in 'struct __darwin_arm_thread_state64' error: no member named '__sp' in 'struct __darwin_arm_thread_state64'

原因:虽然开发过程中编译和运行没有问题,但是,jenkins打包环境下__darwin_arm_thread_state64环境下找不到__ss.__pc这种指定
,因为打包服务器集成了armv7armv7s等不同指令集架构

解决方案:
通过查看源码发现了这些宏

//文件路径 iOS 14.4 -> user/include/mach/arm/_structs.h
#define __darwin_arm_thread_state64_get_pc(ts) \
    ((ts).__pc)
#define __darwin_arm_thread_state64_get_lr(ts) \
    ((ts).__lr)
#define __darwin_arm_thread_state64_get_sp(ts) \
    ((ts).__sp)
#define __darwin_arm_thread_state64_get_fp(ts) \
    ((ts).__fp)

return machineContext->__ss.__lr;引用改为
return __darwin_arm_thread_state64_get_sp(machineContext->__ss);

发布版本,framework打包通过

3.打包测试包

主工程包不需要打模块framework,此时又引起主工程打包服务器编译问题

报错:jenkins主工程包报错
Undefined symbols for architecture armv7:
  "___darwin_arm_thread_state64_get_pc", referenced from:
      +[XXX xxx:] in libXXX.a(xxxxxx.o)
  "___darwin_arm_thread_state64_get_lr",  referenced from:
      +[XXX xxx:] in libXXX.a(xxxxxx.o)
  "___darwin_arm_thread_state64_get_fp",  referenced from:
      +[XXX xxx:] in libXXX.a(xxxxxx.o)
ld: symbol(s) not found for architecture armv7
clang: error: linker command failed with exit code 1 (use -v to see invocation)

意思是主工程支持了armv7,打包服务器会编译所有的处理器环境,在armv7环境下,找不到对应的宏

解决方案:
去掉armv7环境下对相关宏的引用,代码改为:

#if defined(__arm64__)
//    return machineContext->__ss.__sp;
    return __darwin_arm_thread_state64_get_sp(machineContext->__ss);
#endif
    return 0;

这种改法实际上是懒人改法,因为该组件不需要对armv7环境设备负责,所以在arm64环境下,正常引用对应宏,在其他环境下,直接返回0;

总结

上一篇下一篇

猜你喜欢

热点阅读