__attribute((used, section("__DATA,"#sectname" ")))

在 Objective-C/C/C++ 中,__attribute((used, section("__DATA,"#sectname" "))) 是一个 编译器属性(GCC/Clang 扩展),用于将变量或函数 强制保留在指定的二进制段(section)中,主要用于底层开发(如静态数据注册、二进制分析、动态链接等场景)。

一、属性拆解与作用

1. __attribute((...))

这是 GCC/Clang 编译器提供的扩展语法,用于向编译器传递额外的编译指令(如变量存储位置、函数属性等)。

2. used 子属性

  • 作用:强制编译器保留该符号(变量 / 函数),即使它在代码中没有被显式使用。
  • 背景:编译器默认会优化掉 “未使用的符号”(如未被引用的全局变量),used 可以阻止这种优化,确保符号被保留在最终的二进制文件中。

3. section("__DATA,"#sectname" ") 子属性

  • 作用:将符号存储到二进制文件的 指定段(section) 中。
  • __DATA 是 Mach-O 二进制格式(iOS/macOS 可执行文件格式)中的一个 段(segment),用于存储数据(与存储代码的 __TEXT 段对应)。
  • sectname 是 宏字符串化操作,表示段内的 “子段(section name)”,例如 #sectname 为 mysection 时,最终段名是 __DATA,mysection。
  • 段名格式必须严格遵循 __SEGMENT,section(注意逗号后无空格,末尾可带空格但会被忽略),否则编译器可能无法识别。

二、使用场景与示例

原理:通过在编译时期将函数存储到App二进制的段(Segment)中的节(Section)中。在运行时的某些时期(比如App启动后、tabbarVC/VC初始化后等时机)将函数从二进制中取出并调用函数。以达到替换系统Load函数的效果。主要用于App的启动优化。

1、声明

SegmentManager.h
#import <Foundation/Foundation.h>

typedef void (*LoadRegisterCallback)(void);

#define KLoadRegisterSegmentName "__DATA"
#define kLoadRegisterSectionName "__register_load"
#define KLoadRegister_Data __attribute((used, section(KLoadRegisterSegmentName "," kLoadRegisterSectionName )))

// 编译保存Load
#define AppLoadRegister(loadName)  \
static void LoadRegister##loadName();\
static LoadRegisterCallback varLoadRegister##loadName KLoadRegister_Data = LoadRegister##loadName;\
static void LoadRegister##loadName


NS_ASSUME_NONNULL_BEGIN

@interface SegmentManager : NSObject

+ (void)registerLoad;

@end

NS_ASSUME_NONNULL_END
SegmentManager.m
#import "SegmentManager.h"
#import <objc/runtime.h>
#import <objc/message.h>
#include <mach-o/getsect.h>
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
#include <dlfcn.h>

static void LoadRegisterRun(const char * segmentName,const char *sectionName){
    Dl_info info;
    int ret = dladdr(LoadRegisterRun, &info);
    if (ret == 0) {
        // fatal error
    }
    
#ifndef __LP64__
    const struct mach_header *mhp = (struct mach_header*)info.dli_fbase;
    unsigned long size = 0;
    uint32_t *memory = (uint32_t*)getsectiondata(mhp, segmentName, sectionName, & size);
#else
    const struct mach_header_64 *mhp = (struct mach_header_64*)info.dli_fbase;
    unsigned long size = 0;
    uint64_t *memory = (uint64_t*)getsectiondata(mhp, segmentName, sectionName, & size);
#endif
    
    if(size == 0){
        return;
    }
    for(int idx = 0; idx < size/sizeof(void*); ++idx){
        LoadRegisterCallback func = (LoadRegisterCallback)memory[idx];
        NSLog(@"2、获取存储在二进制段segment中section名为“__register_load”中的函数,并执行函数");
        func();
    }
}

@implementation SegmentManager

+ (void)registerLoad {
    LoadRegisterRun(KLoadRegisterSegmentName,kLoadRegisterSectionName);
}

@end

2、使用

ViewController.m

#import "ViewController.h"
#import "SegmentManager.h"

@interface ViewController ()

@end

@implementation ViewController

AppLoadRegister(ViewController)() {
   // 类似于load方法
    NSLog(@"3、在函数响应方法中,加载需要放在load里的代码");
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // 注册load方法
    NSLog(@"1、在合适的时机注册load方法");
    [SegmentManager registerLoad];
}

@end

3、打印结果

1、在合适的时机注册load方法
2、获取存储在二进制段segment中section名为“__register_load”中的函数,并执行函数
3、在函数响应方法中,加载需要放在load里的代码

github 源码:EXESegment

三、关键说明

  • Mach-O 段与节:在 iOS/macOS 的 Mach-O 格式中,二进制文件由多个 segment(段)组成,每个段包含多个 section(节)。__DATA 段用于存储可读写数据,__TEXT 段用于存储代码(只读)。自定义段必须放在 __DATA 或其他可读写段中(若放 __TEXT 会因只读导致无法修改)。
  • used 的必要性:若变量未被显式引用,编译器会默认将其优化掉(即使指定了 section)。used 属性强制保留变量,确保它被写入目标段。
  • 运行时访问:可通过系统 API(如 getsectbyname、getsectiondata)在运行时获取自定义段的地址和大小,从而遍历段内数据(如示例中的回调注册)。
  • 局限性:
    仅适用于 GCC/Clang 编译器(iOS/macOS 开发默认使用 Clang),不兼容 MSVC 等其他编译器。
    自定义段中的数据必须是 编译期可知的静态数据(全局变量、静态变量),无法动态添加。

四、常见用途

  • 静态注册:如插件、回调、命令表等,无需手动调用注册函数,编译时自动写入段,运行时遍历初始化。
  • 二进制标记:在二进制中嵌入版本号、构建信息等元数据,可通过工具(如 otool)直接读取。
  • 底层钩子:配合动态链接器(dyld)实现启动时初始化(如 __mod_init_func 段的原理类似)。

五、总结

__attribute((used, section("__DATA,"#sectname"))) 是 iOS/macOS 底层开发中用于 静态数据段管理 的强大工具,通过将数据强制存储到自定义段,实现运行时的批量访问和初始化,常见于系统框架、插件化、静态注册等场景。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容