Swift Name Mangling - Swift_0x01

From Brad.Cox to Chris.Lattner.

尝试利用一些业余时间研究下 Swift,写一些由 OC 到 Swift 的变化。

背景

当我使用 ClassDump 对一个项目操作的时候,输出了一些看不懂的东西。(后来知道了这是一个 OC Swift 混编的工程)

#import 
@interface _TtC11MandrakeKit11LoadingView : UIViewController {
}
- (id)initWithCoder:(id)arg1;
- (id)initWithNibName:(id)arg1 bundle:(id)arg2;
- (void)viewDidLoad;
@end

通过 Reveal 分析 UI,我大概知道这个类是 MandrakeKit.LoadingView,通过查看 MachO 发现 __TEXT 段中有 __swift5_typeref,其中的数据跟这些很类似,所以这些就是Swift输出的类名。

Swift 这样进行转换的原因是为了防止不同的库不会出现命名冲突

OC & C

OC 的符号表中没有这些复杂的符号重整,OC 使用 Selector + Type Encoding,并且OC 无重载。

C 语言有轻微的 Name Mangling,即会将 void main(){return 0;} 符号化为:_main,增加一个下划线。

OC 举一个栗子:Point类下的 + (id) initWithX: (int) number andY: (int) number;方法

+ (id) initWithX: (int) number andY: (int) number;
- (id) value;

_c_Point_initWithX_andY_
_i_Point_value

C++

对于如下方法,C++的NameMangling会翻译成下面这样:

    int foo(int a) { return a * 2; }
    int foo(double a) { return a * 2.0; }
    int main() { return foo(1) + foo(1.0); }

    0000000100000f30 T __Z3food
    0000000100000f10 T __Z3fooi
    0000000100000000 T __mh_execute_header
    0000000100000f60 T _main

C++编译器遵循一套严格的 mangles 规则,参考这个链接Itanium C++ ABI documentation

int foo(double a) 为例,重整后的符号为 __Z3fooi

大致意思如下:

_ _Z 3 foo d

-   : 代表C风格的符号
_Z  : 这个前缀标记这个符号是一个mangled(重整)的全局C++名字
3   : 字符长度,foo 这个名字,有3个字符
foo : 
d   : 代表`double`,如果是 `int` 的话就是`i`

Swift

Swift 的重整规则基于 C++,也有些不同,包含更多的信息和概念。

栗子:

xcrun swiftc -emit-library -o test -
public class myClass{
    public func calculate(x: Int) -> Int {
        return 0;
    }
}

nm -g test
0000000000002bb0 T _$s4test7myClassC9calculate1xS2i_tF
_$s4test7myClassC9calculate1xS2i_tF

_  // 通用起始
 $s // '$s' global  // Swift 稳定mangling版本
  4test // 字符长度,模块名
  7myClass // 方法归属的类名
  C // 从属关系,myClass 是 test 模块中的 Class
    9calculate // 字符 calculate
     1x // 参数类型
     S2i // 堆栈中放入两个 Swift.Int
     _
       tF // 从属关系,calculate 是 test.myClass 的 Function ???

这里解释一下S2i,这个是从后往前取的,参数往里放是栈结构,先进后出,第一个进栈的是参数 `x:Int`  第二个是返回的参数 Int,所以S2i两个`Swift.Int`。
如果入参是 `x:String`,反参是 Int,那就变成是 `SSSi` 了。

/* 此段过期,是以前的老版本
_TFC4test7MyClass9calculatefS0_FT1xSi_Si

_T // Swift通用起始标记
  F // Non-curried function
    C // Function of a class. (method)
      4test // 字符长度,模块名
        7MyClass // 方法归属的类名
          9calculate // 函数名
            f // 非柯里化函数(Uncurried Function)
              S0 // 指定 类实例 为类型堆栈的第一个参数
                _FT // 参数开始
                  1x // 第一个参数参数名
                    Si // Swift 内置类型 Swift.Int
                _Si // 返回类型,同上 Swift.Int
*/

// 结果:
test.MyClass.calculate(test.MyClass) -> (x: Swift.Int) -> Swift.Int

栗子2:

 didi ~  xcrun swiftc -emit-library -o test -
public func  (lhs: Int, rhs: Int) -> Int {
    return 0;
}

 didi ~  nm -g test
0000000000002d60 T _$s4test004GrIh3lhs3rhsS2i_SitF

_  // 通用起始
  $s // '$s' global  // Swift 稳定mangling版本
    4test // 符号 4是长度
      004GrIh // 00 特殊字符 GrIh 就是 emoji U+1F49B
        3lhs // 参数 3是字符串长度
        3rhs // 参数 3是字符串长度
         S2i // 解释参数的类型,两个Swift.Int
         _ // end??? 待填坑
          Si // Swift.Int
           tF // 从属关系, 是 test 的 Function ???

//结果:
test.(Swift.Int, Swift.Int) -> Swift.Int

以上

对大多数人来说,不是很多。从算法的意义上讲,读取变形的名称是相当简单的,但是对于人眼来说则是不必要的困难。
这就是为什么存在拆解工具的原因。

补充

关于curried & uncurried function:
上面案例中的方法d是一个uncurried function,因为参数是一个一个传递进去的,并不是当做一个多元元组传递进去的。(可能理解有误。。)
这个概念是跟函数式编程相关的,暂未深入了解,挖个坑,稍后填坑。
ref:stackOverFlow:How is this exactly a curried function?


参考链接:

  • 最权威:官方 GitHub Doc
  • https://www.mikeash.com/pyblog/friday-qa-2014-08-15-swift-name-mangling.html
  • wikipedia:Name mangling
  • https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling
  • 官方SwiftDoc关于mangling的章节

其他:
OS X ABI Mach-O File Format Reference

你可能感兴趣的:(Swift Name Mangling - Swift_0x01)