iOS工程11符号的作用续

一、符号

1.符号结构体

Symbol Table 通过两个load commands:

  • LC_SYMTAB:当前Mach-O中的符号表信息。
  • LC_DYSYMTAB:描述动态链接器使用其他的Symbol Table信息。

用来描述 Symbol Table 的大小和位置,以及其他元数据。

LC_SYMTAB

用来描述该文件的符号表。不论是静态链接器还是动态链接器在此文件时,都要使用该load command。调试器也可以使用该load command找到调试信息。

symtab_command

定义LC_SYMTAB价值命令具体属性。在usr/include/mach-o/loader.h中定义:

arduino 复制代码
struct symtab_command {

// 共有属性。指明当前描述的加载命令,当前被设置为LC_SYMTAB
uint32_t cmd;

// 共有属性。指明加载命令的大小,当前被设置为sizeof(symtab_command)
uint32_t cmdsize;

// 表示从文件开始到symbol table所在位置的偏移量。symbol table用[nlist]来表示
uint32_t symoff;

// 符号表内符号的数量
uint32_t nsyms;

// 表示从文件开始到string table所在位置的偏移量。
uint32_t stroff;

// 表示string table大小(以byte为单位)
uint32_t strsize;

};

nlist

定义符号的具体表示含义:

arduino 复制代码
struct nlist {

// 表示该符号在string table的索引
    union {

    // 在Mach-O中不使用此字段
    char *n_name;
    // 索引
    long n_strx;
    } n_un;

unsigned char n_type; /* type flag, see below */
unsigned char n_sect; /* section number or NO_SECT */
short n_desc; /* see <mach-o/stab.h> */
unsigned long n_value; /* value of this symbol (or stab offset) */

};

n_type 1字节通过,通过四位掩码保存数据:

  1. N_STAB(0xe0):如果当前的n_type 包含这三位中任何一位,则该符号为(调试表符号stab)。在这种情况下,整个n_type整个字段将被解释为(stab value)。请参阅usr/include/mach-o/stab.h以获取有效的stab value。

  2. N_PEXT(0x10):如果当前的n_type 包含此位。则将此符号标记为私有外部符号。(private_extern (visibility=hidden)) ,只在程序内可引用和访问。当前通过静态链接器链接的时候,不要将其转换成静态符号(可以通过 ld 的 -keep_private_externs 关闭静态链接器的这种行为 )。

  3. N_TYPE(0x0e):如果当前的n_type 包含此位。则使用预先定义的符号类型。

  4. N_EXT(0x01):如果当前的n_type 包含此位。则符号为外部符号.该符号在该文件的外部定义或者在该文件中定义,但可以在其他文件中使用。

N_TYPE字段的值包含:

  1. N_UNDF(0x0):该符号未定义。未定义符号是在当前模块中引用,但是被定义在其他模块中的符号。n_sect 字段设置为NO_SECT.
  2. N_ABS(0x2):该符号是绝对符号。链接器不会更改绝对符号的值。n_sect字段设置为NO_SECT.
  3. N_SECT(0xe) :该符号在n_sect中指定的段号中定义。
  4. N_PBUD(0xc):该符号未定义,镜像使用该符号的预绑定值。n_sect 字段设置为NO_SECT.
  5. N_INDR(0xa):该符号定义为与另一个符号相同。n_value字段是string table中的索引,用于指定另一个符号的名称。链接该符号时,此符号和另一个符号都具有相同的定义类型和值。

nm命令

打印 nlist 结构的符号表(symbol table)

常用nm命令参数 说明
nm -pa a.o
-a: 显示符号表的所有内容
-g: 显示全局符号
-p: 不排序。显示符号表本来的顺序
-r: 逆转顺序
-u: 显示未定义符号
-m: 显示N_SECT类型的符号(Mach-O符号)显示

实际用法: lldb:

2.macho修改

编译静态库sh

bash 复制代码
SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk
FILE_NAME=NYCat

echo "-----开始进入StaticLibrary"
# cd
pushd .

clang -x objective-c \
-target x86_64-apple-macos12.0 \
-fobjc-arc \
-isysroot $SYSROOT \
-c ${FILE_NAME}.m -o ${FILE_NAME}.o

ar -rc lib${FILE_NAME}.a ${FILE_NAME}.o  

echo "-----开始退出StaticLibrary"

popd

zip --> .符号表 --> .a (全局符号) 开始编译静态库

OC符号冲突

在链接的时候找到对应的.a库,在工程配置Config.xcconfig

ini 复制代码
// 1
HEADER_SEARCH_PATHS = ${SRCROOT}/lib

// 2. 在哪?.a .dylib
LIBRARY_SEARCH_PATHS = ${SRCROOT}/lib


// 3. 搜索那个静态库?lib .a .dylib
// ld --> linker --> 权限 --> 强制两个库
// 4. -ObjC
// 5. category --> ld --> 优化(静态库死代码剥离) Cocoapods
OTHER_LDFLAGS = -lNYCat -ObjC

编译运行: -ObjC是指明编译器把跟OC相关代码的都链接到库中。

在使用C++写的工具进行Mach-o修改注入

需要配置llvm环境(我在15款macpro配置成功,在M1 macmini配置失败。):

bash 复制代码
OTHER_LDFLAGS = -Wl,-dead_strip -Wl,-search_paths_first -Wl,-headerpad_max_install_names -lm /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMObject.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMOption.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMSupport.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMMC.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMBitReader.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMCore.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMRemarks.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMBitstreamReader.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMMCParser.a  /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMDebugInfoCodeView.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMDebugInfoMSF.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMTextAPI.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMBinaryFormat.a /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMSupport.a  /Volumes/Tino/llvm/llvm-project/build/Debug/lib/libLLVMDemangle.a

配置完编译成功

核心代码展示(cat大佬写的):

ini 复制代码
uint64_t PageSize;
    switch (InMacho.getArch()) {
    case Triple::ArchType::arm:
    case Triple::ArchType::aarch64:
    case Triple::ArchType::aarch64_32:
      PageSize = 16384;
      break;
    default:
      PageSize = 4096;
    }

    MachOReader Reader(InMacho);
    Expected<std::unique_ptr<Object>> O = Reader.create();
    Object &Obj = **O;
    for (SymbolEntry &Sym : Obj.SymTable) {
        // 1. outs()
        outs() << Sym.Name << "\n";
        // 2. Name
        if (Sym.Name == "_OBJC_CLASS_$_NYCat") {
            Sym.n_desc |= MachO::N_WEAK_DEF;
        } else if (Sym.Name == "_OBJC_METACLASS_$_NYCat") {
            Sym.n_desc |= MachO::N_WEAK_DEF;
        }
    }

    Buffer &Out = FB;
    MachOWriter Writer(**O, InMacho.is64Bit(), In.isLittleEndian(), PageSize, Out);
    if (auto E = Writer.finalize()) {
        E;
    } else {
        Writer.write();
    }

outs c++内置打印工具,打印符号名称。名称等于_OBJC_CLASS__NYCat或者_OBJC_METACLASS__NYCat 。 设置n_desc 为N_WEAK_DEF 弱定义符号,修改后写入mach-o文件中。

arduino 复制代码
//需要用下面命令  转成 .o文件
ar -x xxxx/libNYCat.a 

成功修改mach-o YES!!!

3.分类与类查找

.a是.o的合集,基本上是全局符号,.a库有全局符号,local符号 调试符号,只能删除 调试符号。

如果是App呢? 全局符号 local符号 调试符号 [Foundation]导出符号 间接符号 [符号表],对于外部使用全局符号,其他符号都可以删除。

ini 复制代码
HEADER_SEARCH_PATHS = ${SRCROOT}/lib

MACH_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/${PRODUCT_NAME}
//x --> macho 符号 结构体 16
//m --》 macho

CMD = objdump --macho --objc-meta-data ${MACH_PATH}

TTY=/dev/ttys000
//
OTHER_LDFLAGS =  -ObjC

打印符号地址:

arduino 复制代码
int main(int argc, const char * argv[]) {
    [[LGStaticLibrary new] LGStaticLibraryCall];
    return 0;
}

编译运行:

通过imp地址找到具体方法是实现。 我们通过分析这两个 exec 解析里面的符号。

scss 复制代码
//1.第一步查看汇编代码
objdump --macho -d MachOAndSymbol

//2.第二部
16755+(%rip) 100003f4d  = movq

//3.用lldb看下汇编地址
e -f x -- 0x100003f4d + 16755

//4.看地址
objdump --macho -s MachOAndSymbol

// 指定section 
objdump --macho --section='__stubs' MachOAndSymbol

objdump --macho --section='__stub_helper' MachOAndSymbol 

//打印间接符号,调试符号
objdump --macho --indirect-symbols -t  MachOAndSymbol 

指向了元类地址:

dyld -> 通过符号绑定 ,绑定后不同在绑定。

相关推荐
Jiaberrr11 分钟前
前端实战:使用JS和Canvas实现运算图形验证码(uniapp、微信小程序同样可用)
前端·javascript·vue.js·微信小程序·uni-app
everyStudy35 分钟前
JS中判断字符串中是否包含指定字符
开发语言·前端·javascript
城南云小白36 分钟前
web基础+http协议+httpd详细配置
前端·网络协议·http
前端小趴菜、36 分钟前
Web Worker 简单使用
前端
web_learning_32139 分钟前
信息收集常用指令
前端·搜索引擎
tabzzz1 小时前
Webpack 概念速通:从入门到掌握构建工具的精髓
前端·webpack
200不是二百1 小时前
Vuex详解
前端·javascript·vue.js
滔滔不绝tao1 小时前
自动化测试常用函数
前端·css·html5
码爸2 小时前
flink doris批量sink
java·前端·flink
深情废杨杨2 小时前
前端vue-父传子
前端·javascript·vue.js