-
Notifications
You must be signed in to change notification settings - Fork 2
/
content.json
1 lines (1 loc) · 143 KB
/
content.json
1
{"meta":{"title":"斗扣年华","subtitle":"与扣斗,其乐无穷","description":null,"author":"douCodeLife","url":"wanggang.store","root":"/"},"pages":[],"posts":[{"title":"debug技巧","slug":"debug技巧","date":"2020-09-28T00:43:05.000Z","updated":"2020-09-28T16:26:02.260Z","comments":true,"path":"2020/09/28/debug技巧/","link":"","permalink":"wanggang.store/2020/09/28/debug技巧/","excerpt":"","text":"deubg context断点停在Objective-C代码,LLDB使用Objective-C上下文。 断点停在Swift代码,LLDB使用Swift上下文。 强制表达式使用OC上下文 123// -l 使用的语言// -O -- 等同po(lldb) expression -l objc -O -- [UIApplication sharedApplication] User defined variables当你打印对象时LLDB会自动创建临时变量,你也可以自己创建变量。 比如有时想自建一个对象再去实验一些方法 123(lldb) po id test = [NSObject new](lldb) po testerror: use of undeclared identifier 'test' 这样会有报错 你得使用$标记告诉LLDB你想记住的变量 123(lldb) po id $test = [NSObject new](lldb) po $test<NSObject: 0x60000001d190> xcode 11.3.1 符号断点:MasterContainerViewController.viewDidLoad 即有效 1(lldb) p self LLDB会创建变量$R0 continue后再手动暂停 此时$R0还持有引用 // 看看对引用计数的影响 123456(lldb) expression -l swift -- $R0.title(String?) $R6 = "Quarterback" // 这里R后面的数字不是固定的(lldb) expression -l swift -- $R0.title = "💩💩💩💩💩"// command + ctrl + space 搜索poop// 控制器的标题变成了一堆便便 我们还可以这样,创建一个断点,执行那个被持有变量的方法,命中断点 可以给方法不同输入观察其输出。TDD 12// -i disable this option -> by default, LLDB will ignore any breakpoints when executing commands(lldb) expression -l swift -O -i 0 -- $R0.viewDidLoad() Type formattingGDB formatThe full list of output formats is as follows (taken from https://sourceware.org/gdb/ onlinedocs/gdb/Output-Formats.html): • x: hexadecimal • d: decimal • u: unsigned decimal • o: octal • t: binary • a: address • c: character constant • f: float • s: string LLDB’s extra formattersLLDB’s formatters can be used like this: (lldb) expression -f Y – 1430672467 This gives you the following output: 1(int) $0 = 53 54 46 55 STFU This explains the FourCC code from earlier! LLDB has the following formatters (taken from http://lldb.llvm.org/varformats.html): • B: boolean • b: binary • y: bytes • Y: bytes with ASCII • c: character • C: printable character • F: complex float • s: c-string • i: decimal • E: enumeration • x: hex • f: float • o: octal • O: OSType • U: unicode16 • u: unsigned decimal • p: pointer 1234// -n 告诉LLDB查询符号与函数名(lldb) image lookup -n "-[UIViewController viewDidLoad]"(lldb) image lookup -rn test// Note: 使用-n来进行精确查找。rn regex search","categories":[],"tags":[]},{"title":"SwiftCore","slug":"Swift基础","date":"2020-08-30T07:51:25.000Z","updated":"2020-08-30T08:24:57.330Z","comments":true,"path":"2020/08/30/Swift基础/","link":"","permalink":"wanggang.store/2020/08/30/Swift基础/","excerpt":"","text":"Bool占一个字节,有效位只有一个bit 1234567891011121314151617181920212223242526@frozenpublic struct Bool { @usableFromInline internal var _value: Builtin.Int1 /// Creates an instance initialized to `false`. /// /// Do not call this initializer directly. Instead, use the Boolean literal /// `false` to create a new `Bool` instance. @_transparent public init() { let zero: Int8 = 0 self._value = Builtin.trunc_Int8_Int1(zero._value) } @usableFromInline @_transparent internal init(_ v: Builtin.Int1) { self._value = v } /// Creates an instance equal to the given Boolean value. /// /// - Parameter value: The Boolean value to copy. @inlinable public init(_ value: Bool) { self = value } ...","categories":[],"tags":[{"name":"swift","slug":"swift","permalink":"wanggang.store/tags/swift/"}]},{"title":"属性访问","slug":"属性访问","date":"2020-06-05T02:55:19.000Z","updated":"2020-06-14T07:30:21.130Z","comments":true,"path":"2020/06/05/属性访问/","link":"","permalink":"wanggang.store/2020/06/05/属性访问/","excerpt":"","text":"“对象”就是“基本构造单元”。开发者可以通过对象来存储并传递数据。 在对象之间传递数据并执行任务的过程就叫做“消息传递” 当程序裕兴起来以后,为其提供相关支持的代码叫做“Objective-C运行时环境” 它提供了一些使得对象之间能够传递消息的重要函数,并且包含创建类实例所用的全部逻辑。 属性property 是OC一项特性,用于封装对象中的数据。 尽量描述所有关于属性相关的问题 属性访问的点语法 属性由何而来 编译器自动合成 编译器生成getter setter 实例变量 @synthesize 指定实例变量名 @dynamic 告诉编译器不要自动创建实现属性的存取方法,不会报错,相信运行时一定能找到 1.@property 的本质是什么?ivar、getter、setter 是如何生成并添加到这个类中的 1234567891011@interface Person : NSObject@property NSString *firstName;@property NSString *lastName;@end//与@interface Person : NSObject- (NSString *)firstName;- (void)setFirstName:(NSString *)firstName;- (NSString *)lastName;- (void)setLastName:(NSString *)lastName;@end 编译识别一样,都能通过点语法访问属性,但是后者没有实现实例变量 读写权限readonly readwrite默认是readwrite getter setter方法都会生成 readonly 只生成getter方法 原子性访问 atomic noatomic1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283id objc_getProperty(id self, SEL _cmd, ptrdiff_t offset, BOOL atomic) { if (offset == 0) { return object_getClass(self); } // Retain release world id *slot = (id*) ((char*)self + offset); if (!atomic) return *slot; // Atomic retain release world spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); id value = objc_retain(*slot); slotlock.unlock(); // for performance, we (safely) issue the autorelease OUTSIDE of the spinlock. return objc_autoreleaseReturnValue(value);}static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy) __attribute__((always_inline));static inline void reallySetProperty(id self, SEL _cmd, id newValue, ptrdiff_t offset, bool atomic, bool copy, bool mutableCopy){ if (offset == 0) { object_setClass(self, newValue); return; } id oldValue; id *slot = (id*) ((char*)self + offset); if (copy) { newValue = [newValue copyWithZone:nil]; } else if (mutableCopy) { newValue = [newValue mutableCopyWithZone:nil]; } else { if (*slot == newValue) return; newValue = objc_retain(newValue); } if (!atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); } objc_release(oldValue);}void objc_setProperty(id self, SEL _cmd, ptrdiff_t offset, id newValue, BOOL atomic, signed char shouldCopy) { bool copy = (shouldCopy && shouldCopy != MUTABLE_COPY); bool mutableCopy = (shouldCopy == MUTABLE_COPY); reallySetProperty(self, _cmd, newValue, offset, atomic, copy, mutableCopy);}void objc_setProperty_atomic(id self, SEL _cmd, id newValue, ptrdiff_t offset){ reallySetProperty(self, _cmd, newValue, offset, true, false, false);}void objc_setProperty_nonatomic(id self, SEL _cmd, id newValue, ptrdiff_t offset){ reallySetProperty(self, _cmd, newValue, offset, false, false, false);}void objc_setProperty_atomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset){ reallySetProperty(self, _cmd, newValue, offset, true, true, false);}void objc_setProperty_nonatomic_copy(id self, SEL _cmd, id newValue, ptrdiff_t offset){ reallySetProperty(self, _cmd, newValue, offset, false, true, false);} 可见编译器编译代码时,通过attribute识别在属性访问方法中插入对应方法 id *slot = (id*)((char*)self + offset) slot 二级指针存储属性地址 分析atomic set方法中 12345678910if (!atomic) { oldValue = *slot; *slot = newValue; } else { spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); oldValue = *slot; *slot = newValue; slotlock.unlock(); } 如果是atomic 会在临界区加入自旋锁 oldValue 存储旧值指针 然后赋新值 get方法中 1234spinlock_t& slotlock = PropertyLocks[slot]; slotlock.lock(); id value = objc_retain(*slot); slotlock.unlock(); 那么,atomic能保证属性访问的线程安全吗 可以看到 在setter方法中,获取新值newValue时并未加锁,当多线程写的时候,并不能保证线程安全。 换句话说,它保证的是内存管理的安全。 内存管理语义assign通常针对“纯量类型” 的简单赋值操作strong此特质表明该属性定义了一种“拥有关系”,为这种属性设置新值时,设置方法会先保留新值,释放旧值,然后再将新值设置上去 weakunsafe_unretainedcopy方法中的self msg_send 传进来的类 ARC","categories":[],"tags":[{"name":"iOS","slug":"iOS","permalink":"wanggang.store/tags/iOS/"}]},{"title":"RxSwift","slug":"RxSwift","date":"2020-05-30T12:15:32.000Z","updated":"2020-05-30T12:15:56.250Z","comments":true,"path":"2020/05/30/RxSwift/","link":"","permalink":"wanggang.store/2020/05/30/RxSwift/","excerpt":"","text":"","categories":[],"tags":[{"name":"iOS","slug":"iOS","permalink":"wanggang.store/tags/iOS/"}]},{"title":"你的答案","slug":"你的答案","date":"2020-05-21T13:06:08.000Z","updated":"2020-06-27T07:47:28.760Z","comments":true,"path":"2020/05/21/你的答案/","link":"","permalink":"wanggang.store/2020/05/21/你的答案/","excerpt":"","text":"0.AOP 1.copy、mutableCopy 123456789101112131415161718192021222324252627282930// swift中的NSArray实现的copy与mutableCopyopen override func copy() -> Any { return copy(with: nil) } open func copy(with zone: NSZone? = nil) -> Any { if type(of: self) === NSArray.self { // return self for immutable type return self } else if type(of: self) === NSMutableArray.self { let array = NSArray() array._storage = self._storage return array } return NSArray(array: self.allObjects) } open override func mutableCopy() -> Any { return mutableCopy(with: nil) } open func mutableCopy(with zone: NSZone? = nil) -> Any { if type(of: self) === NSArray.self || type(of: self) === NSMutableArray.self { // always create and return an NSMutableArray let mutableArray = NSMutableArray() mutableArray._storage = self._storage return mutableArray } return NSMutableArray(array: self.allObjects) } 可见如果属性修饰用了copy, 如果属性上声明NSArray, 即便在赋值时使用NSMutableArray, 属性新值依然得到NSArray的实例,而容器的内存也会是新开辟的。 这就是常说的浅拷贝 而copy与mutableCopy的区别仅在于新创建的容器是可变与不可变 array的这个构造方法会进行深拷贝initWithArray:<#(nonnull NSArray *)#> copyItems:<#(BOOL)#> 123456789101112131415161718public convenience init(array: [Any], copyItems: Bool) { let optionalArray : [AnyObject] = copyItems ? array.map { return __SwiftValue.store($0).copy() as! NSObject } : array.map { return __SwiftValue.store($0) } // This would have been nice, but \"initializer delegation cannot be nested in another expression\" // optionalArray.withUnsafeBufferPointer { ptr in // self.init(objects: ptr.baseAddress, count: array.count) // } let cnt = array.count let buffer = UnsafeMutablePointer<AnyObject>.allocate(capacity: cnt) buffer.initialize(from: optionalArray, count: cnt) self.init(objects: buffer, count: cnt) buffer.deinitialize(count: cnt) buffer.deallocate() } 会申请内存空间,存储每个对象定义copy的返回对象。注意的是如果对象需要实现copy方法。cocoa自带的对象一般都有默认实现。 对于NSArray、NSDictionary应使用copy来保证它们的封装性 如果使用比如strong修饰,当NSMutableArray的实例赋给属性时,属性就变成了NSMutableArray, 由于strong修饰,属性指向了可变数组的内存区域,如果可变数组改变了,属性也会跟随改变反之亦然, 再有,此种情况运行时调用addObject:依然有效,因为isa已经不是NSArray了 block为什么不能修改截获的自动变量的值 我们知道,block会把截获的自动变量设置成自身的属性 在block内部修改值,相当于设置了自身的属性,但是却无法改变原本的自动变量的值(全局变量,静态变量除外),这样就造成矛盾了。 也就是在oc代码中,明明在block中修改了自动变量的值,结果却没有改变! 所以有了__block修饰符来说明意图 经__block修饰的变量会被编译套壳 __Block_byref 1234567891011struct __blockContext_block_impl_0 { struct __block_impl impl; struct __blockContext_block_desc_0* Desc; __Block_byref_alan_0 *alan; // by ref __blockContext_block_impl_0(void *fp, struct __blockContext_block_desc_0 *desc, __Block_byref_alan_0 *_alan, int flags=0) : alan(_alan->__forwarding) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }}; 在注释// by ref 可见意图,通过指针引用来间接修改值。 block属性只是自动变量的指针,在block内部通过属性找到自动变量的指针,进而改变值可以改变外部自动变量的值 123456789int alan// ↓struct __Block_byref_alan_0 { void *__isa;__Block_byref_alan_0 *__forwarding; int __flags; int __size; int alan;}; 关键是__forwarding 123456// block修改自动变量的部分static void __blockContext_block_func_0(struct __blockContext_block_impl_0 *__cself) { __Block_byref_alan_0 *alan = __cself->alan; // bound by ref (alan->__forwarding->alan) = 2; } 12// block外部的自动变量的声明__attribute__((__blocks__(byref))) __Block_byref_alan_0 alan = {(void*)0,(__Block_byref_alan_0 *)&alan, 0, sizeof(__Block_byref_alan_0), 1}; 12// 把自动变量赋给block((void (*)())&__blockContext_block_impl_0((void *)__blockContext_block_func_0, &__blockContext_block_desc_0_DATA, (__Block_byref_alan_0 *)&alan, 570425344)); 在block内部想改变外部的自动变量时,获取到的是外部变量壳对象的引用 通过引用找到__forwarding再找到真正的属性变量进行赋值 为什么不直接使用 alan->alan 即通过偏移直接赋值呢? 123456789101112131415//void (*tryBlock)(void) = ((void (*)())&__blockContext_block_impl_0((void *)__blockContext_block_func_0, &__blockContext_block_desc_0_DATA, alan));struct __blockContext_block_impl_0 { struct __block_impl impl; struct __blockContext_block_desc_0* Desc; int alan; __blockContext_block_impl_0(void *fp, struct __blockContext_block_desc_0 *desc, int _alan, int flags=0) : alan(_alan) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; }// void (*tryBlock)(void) = ((void (*)())&__blockContext_block_impl_0((void *)__blockContext_block_func_0, &__blockContext_block_desc_0_DATA, (__Block_byref_alan_0 *)&alan, 570425344)); Block 的copy swift 内存布局 swift如何实现多态","categories":[],"tags":[{"name":"iOS","slug":"iOS","permalink":"wanggang.store/tags/iOS/"}]},{"title":"Grand Central Dispatch","slug":"Grand-Central-Dispatch","date":"2020-05-01T01:14:18.000Z","updated":"2020-05-06T08:58:09.720Z","comments":true,"path":"2020/05/01/Grand-Central-Dispatch/","link":"","permalink":"wanggang.store/2020/05/01/Grand-Central-Dispatch/","excerpt":"","text":"进程进程:运行中的程序 程序本身是没有生命周期的,只是存储在磁盘中的一些指令或是静态数据,是操作系统将这些字节运行起来,让程序发挥作用。 操作系统提供运行中的程序的抽象,就是所谓的进程。 程序是如何转化为进程的?操作系统是如何启动程序的? 首先操作系统将磁盘中的代码和静态数据加载到内存,加载到进程的地址空间(程序以可执行的格式存储在磁盘中),然后初始化堆栈,当这些准备工作完成后,操作系统找到程序的入口main,将CPU的控制权交给这个创建的进程,而后,程序开始运行了。 An abstract thread quality of service (QOS) classification.","categories":[],"tags":[{"name":"iOS","slug":"iOS","permalink":"wanggang.store/tags/iOS/"}]},{"title":"iOS weak","slug":"iOS-weak","date":"2020-04-21T13:40:31.000Z","updated":"2020-04-22T07:25:45.880Z","comments":true,"path":"2020/04/21/iOS-weak/","link":"","permalink":"wanggang.store/2020/04/21/iOS-weak/","excerpt":"","text":"","categories":[],"tags":[{"name":"iOS","slug":"iOS","permalink":"wanggang.store/tags/iOS/"}]},{"title":"安全漏洞","slug":"安全漏洞","date":"2020-03-31T13:31:15.000Z","updated":"2020-08-11T01:21:41.610Z","comments":true,"path":"2020/03/31/安全漏洞/","link":"","permalink":"wanggang.store/2020/03/31/安全漏洞/","excerpt":"","text":"缓冲区溢出错误是造成大多数网络和Internet服务器上安全漏洞的主要原因。 描述一下堆栈原理和缓冲区溢出错误。 如何降低攻击威胁","categories":[],"tags":[{"name":"计算机系统","slug":"计算机系统","permalink":"wanggang.store/tags/计算机系统/"}]},{"title":"程序性能","slug":"程序性能","date":"2020-03-31T13:07:05.000Z","updated":"2020-03-31T13:16:51.090Z","comments":true,"path":"2020/03/31/程序性能/","link":"","permalink":"wanggang.store/2020/03/31/程序性能/","excerpt":"","text":"一个switch语句是否总是比一系列的if-else语句高效得多 一个函数调用的开销有多大 while循环比for循环更有效吗 指针引用比数组索引更有效吗 为什么将循环求和的结果放到一个本地变量中,会比将其放到一个通过引用传递过来的参数中,运行起来快很多呢 为什么我们只是简单地重新排列一下算术表达中的括号就能让函数运行得更快","categories":[],"tags":[{"name":"计算机系统","slug":"计算机系统","permalink":"wanggang.store/tags/计算机系统/"}]},{"title":"Compilers","slug":"Compilers","date":"2020-03-24T08:48:03.000Z","updated":"2020-03-24T08:52:42.570Z","comments":true,"path":"2020/03/24/Compilers/","link":"","permalink":"wanggang.store/2020/03/24/Compilers/","excerpt":"","text":"参考资料wikipedia GCC vs. Clang/LLVM: An In-Depth Comparison of C/C++ Compilers A Performance-Based Comparison of C/C++ Compilers","categories":[],"tags":[{"name":"compilers","slug":"compilers","permalink":"wanggang.store/tags/compilers/"}]},{"title":"Xcode快捷键","slug":"Xcode快捷键","date":"2020-03-21T04:47:30.000Z","updated":"2020-03-21T05:20:05.330Z","comments":true,"path":"2020/03/21/Xcode快捷键/","link":"","permalink":"wanggang.store/2020/03/21/Xcode快捷键/","excerpt":"","text":"cmd + shift + j,焦点在某个文件中, 左侧文件目录定位到对应的文件 Ctrl + i 格式化选中的代码 cmd alt [ 代码块上移 cmd [ 左移代码 ctrl cmd 方向键 interface","categories":[],"tags":[{"name":"Xcode","slug":"Xcode","permalink":"wanggang.store/tags/Xcode/"}]},{"title":"lldb","slug":"lldb","date":"2020-03-20T01:53:17.000Z","updated":"2020-03-20T02:20:56.160Z","comments":true,"path":"2020/03/20/lldb/","link":"","permalink":"wanggang.store/2020/03/20/lldb/","excerpt":"","text":"单指令si ni n","categories":[],"tags":[{"name":"lldb","slug":"lldb","permalink":"wanggang.store/tags/lldb/"}]},{"title":"汇编篇","slug":"汇编篇","date":"2020-03-19T11:07:54.000Z","updated":"2020-05-15T02:55:44.050Z","comments":true,"path":"2020/03/19/汇编篇/","link":"","permalink":"wanggang.store/2020/03/19/汇编篇/","excerpt":"80x86指令系统指令格式[标号:] 操作码[操作数,...] [;注释] 整数指令 浮点数指令 操作系统行指令7类寻址 立即寻址 寄存器寻址 直接寻址 寄存器间接寻址 相对寄存器间接寻址 基址加变址寻址 相对基址加变址寻址","text":"80x86指令系统指令格式[标号:] 操作码[操作数,...] [;注释] 整数指令 浮点数指令 操作系统行指令7类寻址 立即寻址 寄存器寻址 直接寻址 寄存器间接寻址 相对寄存器间接寻址 基址加变址寻址 相对基址加变址寻址 立即寻址把立即数送到寄存器中 123MOV BL, 56H ; 56H -> BLMOV AX, 2056H ; 2056H -> AXMOV ECX, 12345678H ; 12345678 -> ECX 立即数只能作源操作数,不能作目的操作数。 寄存器寻址操作数在CPU的寄存器中,而寄存器的名由指令指出。 12INC CL;MOV AX, BX; (BX) -> AX BX的内容给AX 直接寻址操作数有效地址直接包含在指令中,它位于操作码之后,存放在代码段中。如果指令无前缀指明在哪一段,则默认操作数存放在数据段。 1MOV AX, [2000H]; DS段2000H的内容送AL,2001H的内容送AH。 逻辑地址 = 段基址 + 偏移地址 []里面是偏移地址 段基址默认DS段 指定段加冒号 1MOV AX, ES: [2000H]; ES段2000H的内容送AL,2001H的内容送AH。 寄存器间接寻址操作数的偏移地址存放在寄存器中,而操作数存放在存储器中。 1234MOV AX, [BX]; AX <- (DS*10H+(BX)) MOV AX, [BP]; AX <- (SS*10H+(BP))MOV AX, ES: [BX]; AX <- ()MOV AX, DS: [BP]; 白话:比如存储器中有个数a地址为0x12345678 寄存器BX中存放的就是0x12345678 执行结果是把存放在BX中的0x12345678传送给AX SS:堆栈段的段寄存器 相对寄存器间接寻址寄存器内容与位移量之和形成操作数的有效地址。即 EA = [寄存器]+位移量 12MOV ECX, 1500H[EAX]; DS段(EAX)+1500H中的双字数据送到ECX。MOV ECX, [EAX + 1500H]; 基址加变址寻址基址寄存器内容与变址寄存器内容之和形成操作数的偏移地址。即 EA = [基址寄存器] + [变址寄存器] 1234基址寄存器:任何一个32位通用寄存器变址寄存器:除ESP之外的任一个32位通用寄存器MOV AX, [EBX+ECX]; DS段(EBX)+(ECX)中的字数据送AXMOV AX, [EBX][ECX]; 相对基址加变址寻址EA == 有效地址 == 偏移地址 基址寄存器内容与变址寄存器内容再加偏移量之和形成操作数的有效地址。 EA = [基址寄存器]+[变址寄存器]+偏移量 1234MOV AX, 1234H[BX+DI];MOV AX, [BX+DI+1234H];MOV AX, 1234H[BX][DI];# 几种方式等效,DS段(BX)+(DI)+1234H 中的字数据送AX 与跳转有关的寻址方式无条件转移:JMP dst; dst:转移目标 调用语句: CALL dst; dst:调用目标 数据传送指令交换指令XCHG用于交换两个操作数 1XCHG OP1, OP2 注意可以是两个寄存器或者一个寄存器与一个存储器。但不能是两个存储器 I/O指令 IN 和 OUT用于在I/O端口和AL、AX或EAX累加器之间交换数据。 12IN OP1, OP2 OUT OP1, OP2 装入有效地址指令LEA1LEA OP1, OP2 将有效地址(偏移地址)送通用寄存器。 装入全地址指令LDS,LES,LFS,LGS,LSS12LDS reg, mem... 取mem指示的32位或48位全地址指针(即一个16位段选择符和一个16位或32位偏移地址)装入段寄存器和16位或32位reg中 比如段基址和偏移地址都是16位,段基址送段寄存器,偏移地址动reg中。 例如: 12X DD 12345678HLDS SI , X; DS =1234H, SI = 5678H 对于16位和32位保护方式,选择符送段寄存器,偏移地址送reg中 例如 12345X DD 12345678H; 32位偏移值DW 0010H; 16位选择符LDS ESI, X; ESI=12345678H,DS=0010H 压栈/弹栈指令PUSH/POP PUSH OP1 POP OP1 SS 堆栈段的第一个地址 SP 栈顶 全部通用寄存器压栈和出栈指令PUSHA/POPA: 16位通用寄存器压栈和出栈指令。 入栈顺序为:AX、CX、BX、DX、SP、BP、SI和DI。 PUSHAD/POPAD: 32位通用寄存器压栈和出栈指令 入栈顺序为:EAX、ECX、EBX、EDX、ESP、EBP、ESI和EDI。 其中:SP和ESP为操作前的栈顶指针。 查表转换指令XLAT1XLAT; AL <- ((BX)+(AL)) BX的内容加上AL的内容 得出一个地址,那个地址指向的内容传送给AL 算术运算指令与运算有关的标志 CF: 进位/借位标志。加、减运算最高位产生进位/借位时置1 AF: 辅助进位/借位标志。加、减运算时低半字节产生进位/借位是置1。 OF: 溢出标志。有符号二进制加、减运算结果超出范围时置1。(最高位,次高位只有一个有进位则溢出) ZF: 零标志。结果为零时置1。 SF: 符号标志。结果为负时置1。(最高位为1) PF: 奇偶标志。 结果”1“的个数为偶数时置1。 加法和减法指令ADD/SUB12ADD OP1, OP2; OP1 <- OP1+OP2, 置标志位SUB OP1, OP2; OP1 <- OP1-OP2,置标志位 例如:AL=64H,AH=0A8H,求执行下列指令的结果和标志位的状态。 (1)\u0010ADD AL. AH 123456789 0110 0100 // 100D 通过补码运算 0110 0100 +1010 1000 // -88D - 0101 10001 0000 1100 // 12D (高位1舍弃) 0000 1100 | OF | SF | ZF | AF | PF | CF || ---- | ---- | ---- | ---- | ---- | ---- || 0 | 0 | 0 | 0 | 1 | 1 | 1010 1000 最高位1为负数 求补码(取反加1)取反 0101 0111 -> 加1 0101 1000 -> -88D (2)SUB AL, AH; 123456789 1 0110 0100 // 100D 高位借1 0110 0100 -1010 1000 // -88D + 0101 1000 1011 1100 // -68D (188D) 1011 1100 //-68D 最高位为1负数,补码0100 0100 68D| OF | SF | ZF | AF | PF | CF || ---- | ---- | ---- | ---- | ---- | ---- || 1 | 1 | 0 | 1 | 0 | 1 | 带进位加法/减法指令 ADC/SBB123ADC OP1, OP2; OP1 <- OP1 + OP2 + CFSBB OP1, OP2; OP1 <- OP1 - OP2 - CF 比如实现128位数相加 可以拆分为64 64 利用进位标志 加1/减1指令 INC/DEC12INC OP1; OP1 <- OP1 + 1DEC OP1; OP1 <- OP1 - 1 交换加法指令XADD12XADD OP1, OP2; OP1 <- OP1+OP2 ; OP2 <- OP1 变反指令NEG1NEG OP1; OP1 <- -OP1 置标志位 例子: 12345678NEG AL;若(AL) = 13H, 执行NEG指令后, (AL) = EDH13H => 0001 0011补码 => 1110 1101 => EDH所以AL的内容为EDH 1110 1101 ED 比较指令CMP将OP1减去OP2,但结果不存在OP1中,只使结果影响标志位。 1CMP OP1, OP2; OP1 - OP2, 置标志位 A - B CF ZF SF OF A = B - 1 - - 无符号数(看CF) A < B 1 0 - - A > B 0 0 - - 有符号数 A < B - 0 1 0 A > B - 0 0 0 无符号乘法指令MUL1MUL OP1 被乘数隐含在累加器中(AL,AX,EAX),OP1为乘数。字节运算时乘积返回到AX;字运算乘积返回到DX:AX; 双字运算时乘积返回到EDX:EAX。 影响标志位OF和CF: 若积的高字节、字或双字为0,则OF、CF清0.否则OF、CF置1 比如8位乘8位结果还是8位则CF、OF为0 有符号乘法指令IMUL单操作数1IMUL OP1 被乘数隐含在累加器中(AL,AX,EAX) 字节运算结果返回到AX 字运算 DX:AX 双字运算 EDX:EAX 双操作数1IMUL OP1, OP2; OP1 <- OP1 * OP2 三操作数1IMUL OP1, OP2, OP3; OP1 <- OP2 * OP3 若积的高字节、字或双字不是积的符号位扩展,则OF、CF置1,否则OF、CF请0。 除法指令DIV/IDIV12DIV OP1; 无符号除法IDIV OP1; 有符号除法 被除数隐含在AX中, OP1 (除数) 被除数 余数 余数 字节 AX AL AH 字 DX:AX AX DX 双字 EDX:EAX EAX EDX 注意被除数位数要是除数的二倍16/8 32/16 64/32 那么8位被除数怎么办 符号扩展指令1234567CBW; 将AL中8位带符号数扩展为16位存入AX。CWD; 将AX中16位带符号数扩展为32位存入DX:AXCWDE; 将AX中16位带符号数扩展为32位存入EAX。CDQ; 将EAX中32位带符号数扩展为64位存入EDX:EAX。 Tips:无论地址的位数如何,所指向的一个存储单元大小为1Byte(字节) 十进制(BCD码)调整指令压缩BCD码运算:将压缩BCD数用二进制加、减指令(ADD,SUB,ADC,SBC)运算,运算结果必须用一下调整指令调整为压缩BCD数的结果 12345678DAA; 将AL中的和调整为压缩BCD数DAS; 将AL中的差调整为压缩BCD数非压缩BCD字节数加减乘除(ASCII调整)非压缩BCD码运算:将非压缩BCD数用二进制加减乘除指令运算,配合相应的调整。AAA; (+)将AL中的和调整为非压缩BCD数AAS; (-)将AL中的差调整为非压缩BCD数AAM; (*)将AL中的积调整为非压缩BCD数AAD; (/)调整AX中的被除数,相除的商即为非压缩BCD数 1234567891011121314# 两个ASCII数想减(7-5)MOV AL, '7'; 37HSUB AL, '5'; 37H-35HAAS ; 调整,(AL)=02H# 非组合BCD数6*8=48MOV AL, O6HMOV BL, 08HMUL BL ; AX <- (AL)*(BL)AAM ; 调整(AX)=0408H# 非组合BCD数17/5=3余2MOV AX, 0107HMOV BL, 05HAAD ; 调整被除数,(AX)=0011HDIV BL ; 相除,(AL)=03,(AH)=02 x86通用寄存器x86架构(16位、32位、64位)有8个通用寄存器General-Purpose Registers (GPR),而64位又新增了8个通用寄存器 寄存器(16位) 英文名 中文名 主要功能 AX Accumulator register) 累加寄存器 算术运算 CX Counter register 计数寄存器 计算循环次数 DX Data register 数据寄存器 算术操作和I/O操作 BX Base register 基址寄存器 存储内存地址 SP Stack Pointer register 栈指针寄存器 指向栈顶 BP Stack Base Pointer register 基址指针寄存器 指向栈基址(栈底部) SI Source Index register) 源基址寄存器 存储数据发送源的内存地址 DI Destination Index register 目标基址寄存器 存储数据发送目标的内存地址 32位增加前缀E意为extended 64位增加前缀R意为register EAX ECX EDX EBX ESP EBP ESI EDI RAX RCX RDX RBX RSP RBP RSI RDI 段寄存器6个段寄存器 SS (Stack Segment) Pointer to the stack. 堆栈段 CS (Code Segment) Pointer to the code. 代码段 DS (Data Segment) Pointer to the data. 数据段 ES (Extra Segment) Pointer to extra data (‘E’ stands for ‘Extra’). 附加段 FS (F Segment) Pointer to more extra data (‘F’ comes after ‘E’). F段 GS (G Segment) Pointer to still more extra data (‘G’ comes after ‘F’). G段 64位新增8个通用寄存器R8-R15 R0-R7 => RAX-RDI R8D-R15D 每个64位寄存器的低32位(D代表双字,32位) R8W-R15W 低16位(W 字,16位) R8B-R15B 低8位(B 字节,8位) AX的高8位AH低8位AX 所以我们访问存储器有5种方式 RAX,EAX,AX,AH,AL IP 指令指针(寄存器),存储代码段的偏移量,指向当前将要执行的指令 program counter (PC) 通常在Intel x86和Itanium微处理器中叫做IP(Instruction pointer),有时又叫做address register(IAR) 处理器从存储获取指令来执行,存储器的地址由pc而来,通常为顺序获取,每取一次,pc自增加。 有些控制指令可以改变pc的值而实现跳跃获取 函数调用过程寄存的角色 AT&T vs Intelx86 汇编语言两大主要派系: Intel stntax,AT&T syntax Intel语法主要应用在DOS和Windows AT&T语法主要应用在Unix 主要不同 AT&T Intel 符号 立即数要加前缀”$“,寄存器要加前缀”%“ 汇编器自行决定符号的类型 参数的顺序 源在前,目标操作数在后mov $5, %eax 目标在前,源在后mov eax, 5 参数带有尺寸 助记符带有后缀标明操作的内存大小q:qword、l:long,dword、w:word、b:byteaddl $4, %esp 从寄存器的名字可推断出操作类型的大小比如rax~q,eax~l,ax~w,al~badd esp, 4 有效地址 DISP(BASE,INDEX,SCALE).mov mem_location(%ebx,%ecx,4), %eax [`]方括号里书序表达式,此外如果无法从操作数中推断出大小则需要添加关键字如qword,dword,word,byte<br />mov eax , [ebx + ecx*4 + mem_location]<br />mov eax. dword prt [ebp+8]` 12mov eax. dword prt [ebp+8]; dword double word pointer# 这句指令的意思:ebp寄存器的值加8后得到的值为内存地址,从改地址开始取4个字节的数据 名词理解二进制负数运算二进制数中表示负数值时,一般会把最高位作为符号来使用,因此我们把这个最高位称为符号位。符号位是 0 时表示正数 ,符号位 是 1 时表示负数。那么-1 用 8 位二进制数来表示的话是什么样的呢?可能很多人会认为“1 的二进制数是 00000001,因此-1 就是 10000001”,但这个答案是错的,正确答案是 11111111。 计算机在做减法运算时,实际上内部是在做加法运算。用加法运算来实现减法运算,是不是很新奇呢?为此,在表示负数时就需要使用“二进制的补数”。补数就是用正数来表示负数,很不可思议吧。 为了获得补数,我们需要将二进制数的各数位的数值全部取反,然后再将结果加 1。例如,用 8 位二进制数表示-1 时,只需求得 1,也就是 00000001 的补数即可。具体来说,就是将各数位的 0 取反成 1,1 取反成 0,然后再将取反的结果加 1,最后就转化成了 11111111 “当然,结果不为 0 的运算同样可以通过使用补数来得到正确的结果。不过,有一点需要注意,当运算结果为负数时,计算结果的值也是以补数的形式来表示的。比如 3-5 这个运算,用 8 位二进制数表示 3 时为 00000011,而 5 = 00000101 的补数为“取反+1”,也就是 11111011。因此 3-5 其实就是 00000011+11111011 的运算。 00000011 + 11111011 的运算结果为 11111110,最高位变成了 1。这就表示结果是一个负数,这点大家应该都能理解。那么 11111110 表示的负数是多少大家知道吗?这时我们可以利用负负得正这个性质。假若 11111110 是负△△,那么 11111110 的补数就是正△△。通过求解补数的补数,就可知该值的绝对值。11111110 的补数,取反加 1 后为 00000010。这个是 2 的十进制数。因此,11111110 表示的就是-2。 仔细思考一下补数的机制,大家就会明白像-32768~32767 这样负数比正数多一个的原因了。最高位是 0 的正数,有 0~32767 共 32768 个,这其中也包含 0。最高位是 1 的负数,有-1~-32768 共 32768 个,这其中不包含 0。也就是说,0 包含在正数范围内,所以负数就要比正数多 1 个。虽然 0 不是正数,但考虑到符号位,就将其划分到了正数中。 符号拓展位符号扩充(又名符号扩展)是计算机算术中,在保留数字的符号(正负性)及数值的情况下,增加二进制数字位数的操作。此操作根据使用的特定有符号数处理方式,通过在数字的最高有效位端添加位数的方式完成。 举个例子,若计算机使用六位二进制数表示数字“00 1010”(十进制的正10),且此数字需要将字长符号扩充至十六位,则扩充后的值为“0000 0000 0000 1010”。此时,数值与符号均保留了下来。 若计算机使用十位数及二补数表示数字“11 1111 0001”(十进制的负15),且此值需要扩充至十六位,则扩充后的值为“1111 1111 1111 0001”。 此时,负号及原数字数值通过将左侧填充为1的方式保留了下来。 在英特尔的x86指令集中,符号扩充有两种方式:使用指令cbw、cwd、cwde及cdq:分别将字节转化为字、字转化为双字、字转化为扩充双字、双字转化为四倍长字(x86环境中,一个字节为8位、一个字16位、双字与扩充双字均为32位、四倍长字64位);使用符号扩展移动指令,可通过movsx(“带符号移动”)指令族完成。 零扩展是与符号扩展类似的概念。在移动操作或转换操作中,零扩展指的是将目标的高位数设置为零,而不是将高位数设置成原数字的最高有效位。零扩展通常用于将无符号数字移动至较大的字段中,同时保留其数值;而符号扩展通常用于有符号的数字。 在x86及x64指令集中,movzx指令(“使用零扩展移动”)将执行零扩展移动。举个例子,movzx ebx, al会复制al中的一个字节至ebx的低位字节,随后使用0填充ebx的剩余字节。 在x64平台上,大多数写入通用寄存器的低32位的指令将使用0填充目标寄存器的上半部分。举个例子,指令mov eax, 1234 将清除rax寄存器的上32位。 移位运算移位运算指的是将二进制数值的各数位进行左右移位 <<左移 >>右移 逻辑右移和算术右移的区别右移有移位后在最高位补0和最高位补1两种情况 逻辑右移:移位后需要在最高位补 0。 当二进制数的值表示图形模式而非数值时,类似于霓虹灯往右滚动的效果。 算术右移:移位后要在最高位填充移位前符号位的值(0 或 1) 当二进制数作为带符号的数值进行运算时,如果数值是用补数表示的负数值,那么右移后在空出来的最高位补 1,如果是正数,只需在最高位补 0 即可。 PS 只有在右移时才必须区分逻辑位移和算术位移。左移时,无论是图形模式(逻辑左移)还是相乘运算(算术左移),都只需在空出来的低位补 0 即可 BCD码压缩BCD码:4位二进制表示一个十进制位 需要注意的是每个十进制数都用一组四位二进制数来表示。不足4位者(十进制数0到7)加添0字开头,以凑足4位。还有六种数码:1010,1011,1100,1101,1110,1111,是不用的。 非压缩BCD码:一个字节(8位二进制位)表示一个十进制位 非压缩型BCD码一个字节可存放一个一位十进制数,其中高4位的内容不做规定(也有部分书籍要求为0,二者均可),低4位二进制表示该位十进制数。如5的非压缩型BCD码是0000 0101,必须存放在一个字节中,56的非压缩型BCD码是00000101 00000110,必须存放在一个字中; 参考资料《微机原理与接口技术》 戴胜华 https://en.wikibooks.org/wiki/X86_Assembly/X86_Architecture https://en.wikipedia.org/wiki/X86_memory_segmentation https://wiki.cdot.senecacollege.ca/wiki/X86_64_Register_and_Instruction_Quick_Start AT&T vs Intel","categories":[],"tags":[{"name":"assembly","slug":"assembly","permalink":"wanggang.store/tags/assembly/"}]},{"title":"iOS中的对象","slug":"iOS中的对象","date":"2020-03-17T07:23:24.000Z","updated":"2020-06-03T12:41:20.410Z","comments":true,"path":"2020/03/17/iOS中的对象/","link":"","permalink":"wanggang.store/2020/03/17/iOS中的对象/","excerpt":"站在巨人的肩膀,乘后人之凉","text":"站在巨人的肩膀,乘后人之凉 分析OC代码,通用手段是用C++ rewrite,通过分析C++代码来了解OC compile time 先来份完整片段 如下OC代码 Alan.m 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748#import <UIKit/UIKit.h>#import "AppDelegate.h"@interface Human: NSObject@end@implementation Human- (void)breath { NSLog(@"fresh air");}+ (void)eat { NSLog(@"raw food");}@end@interface AlanWang: Human@property NSString *hack;@end@implementation AlanWang- (void)cook { NSLog(@"then eat");}+ (void)swim { NSLog(@"so much water");}@endint main(int argc, char * argv[]) { NSString * appDelegateClassName; AlanWang *man = [AlanWang alloc]; man = [man init]; @autoreleasepool { appDelegateClassName = NSStringFromClass([AppDelegate class]); } return UIApplicationMain(argc, argv, nil, appDelegateClassName);} rewrite xcrun -sdk iphoneos clang -rewrite-objc -F UIKit -fobjc-arc -arch arm64 Alan.m 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319#ifndef _REWRITER_typedef_Human#define _REWRITER_typedef_Humantypedef struct objc_object Human;typedef struct {} _objc_exc_Human;#endifstruct Human_IMPL { struct NSObject_IMPL NSObject_IVARS;};/* @end */// @implementation Humanstatic void _I_Human_breath(Human * self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_1f_vycdcqqn5r32n45pwpqrj1gr0000gn_T_Alan_02f76f_mi_0);}static void _C_Human_eat(Class self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_1f_vycdcqqn5r32n45pwpqrj1gr0000gn_T_Alan_02f76f_mi_1);}// @end#ifndef _REWRITER_typedef_AlanWang#define _REWRITER_typedef_AlanWangtypedef struct objc_object AlanWang;typedef struct {} _objc_exc_AlanWang;#endifextern \"C\" unsigned long OBJC_IVAR_$_AlanWang$_hack;struct AlanWang_IMPL { struct Human_IMPL Human_IVARS; NSString *__strong _hack;};// @property NSString *hack;/* @end */// @implementation AlanWangstatic void _I_AlanWang_cook(AlanWang * self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_1f_vycdcqqn5r32n45pwpqrj1gr0000gn_T_Alan_02f76f_mi_2);}static void _C_AlanWang_swim(Class self, SEL _cmd) { NSLog((NSString *)&__NSConstantStringImpl__var_folders_1f_vycdcqqn5r32n45pwpqrj1gr0000gn_T_Alan_02f76f_mi_3);}static NSString * _I_AlanWang_hack(AlanWang * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_AlanWang$_hack)); }static void _I_AlanWang_setHack_(AlanWang * self, SEL _cmd, NSString *hack) { (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_AlanWang$_hack)) = hack; }// @endint main(int argc, char * argv[]) { NSString * appDelegateClassName; AlanWang *man = ((AlanWang *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass(\"AlanWang\"), sel_registerName(\"alloc\")); man = ((AlanWang *(*)(id, SEL))(void *)objc_msgSend)((id)man, sel_registerName(\"init\")); /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; appDelegateClassName = NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass(\"AppDelegate\"), sel_registerName(\"class\"))); } return UIApplicationMain(argc, argv, __null, appDelegateClassName);}struct _prop_t { const char *name; const char *attributes;};struct _protocol_t;struct _objc_method { struct objc_selector * _cmd; const char *method_type; void *_imp;};struct _protocol_t { void * isa; // NULL const char *protocol_name; const struct _protocol_list_t * protocol_list; // super protocols const struct method_list_t *instance_methods; const struct method_list_t *class_methods; const struct method_list_t *optionalInstanceMethods; const struct method_list_t *optionalClassMethods; const struct _prop_list_t * properties; const unsigned int size; // sizeof(struct _protocol_t) const unsigned int flags; // = 0 const char ** extendedMethodTypes;};struct _ivar_t { unsigned long int *offset; // pointer to ivar offset location const char *name; const char *type; unsigned int alignment; unsigned int size;};struct _class_ro_t { unsigned int flags; unsigned int instanceStart; unsigned int instanceSize; const unsigned char *ivarLayout; const char *name; const struct _method_list_t *baseMethods; const struct _objc_protocol_list *baseProtocols; const struct _ivar_list_t *ivars; const unsigned char *weakIvarLayout; const struct _prop_list_t *properties;};struct _class_t { struct _class_t *isa; struct _class_t *superclass; void *cache; void *vtable; struct _class_ro_t *ro;};struct _category_t { const char *name; struct _class_t *cls; const struct _method_list_t *instance_methods; const struct _method_list_t *class_methods; const struct _protocol_list_t *protocols; const struct _prop_list_t *properties;};extern \"C\" __declspec(dllimport) struct objc_cache _objc_empty_cache;#pragma warning(disable:4273)static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[1];} _OBJC_$_INSTANCE_METHODS_Human __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)\"breath\", \"v16@0:8\", (void *)_I_Human_breath}}};static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[1];} _OBJC_$_CLASS_METHODS_Human __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)\"eat\", \"v16@0:8\", (void *)_C_Human_eat}}};static struct _class_ro_t _OBJC_METACLASS_RO_$_Human __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { 1, sizeof(struct _class_t), sizeof(struct _class_t), 0, \"Human\", (const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_Human, 0, 0, 0, 0, };static struct _class_ro_t _OBJC_CLASS_RO_$_Human __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { 0, sizeof(struct Human_IMPL), sizeof(struct Human_IMPL), 0, \"Human\", (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_Human, 0, 0, 0, 0, };extern \"C\" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;extern \"C\" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_Human __attribute__ ((used, section (\"__DATA,__objc_data\"))) = { 0, // &OBJC_METACLASS_$_NSObject, 0, // &OBJC_METACLASS_$_NSObject, 0, // (void *)&_objc_empty_cache, 0, // unused, was (void *)&_objc_empty_vtable, &_OBJC_METACLASS_RO_$_Human,};extern \"C\" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;extern \"C\" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Human __attribute__ ((used, section (\"__DATA,__objc_data\"))) = { 0, // &OBJC_METACLASS_$_Human, 0, // &OBJC_CLASS_$_NSObject, 0, // (void *)&_objc_empty_cache, 0, // unused, was (void *)&_objc_empty_vtable, &_OBJC_CLASS_RO_$_Human,};static void OBJC_CLASS_SETUP_$_Human(void ) { OBJC_METACLASS_$_Human.isa = &OBJC_METACLASS_$_NSObject; OBJC_METACLASS_$_Human.superclass = &OBJC_METACLASS_$_NSObject; OBJC_METACLASS_$_Human.cache = &_objc_empty_cache; OBJC_CLASS_$_Human.isa = &OBJC_METACLASS_$_Human; OBJC_CLASS_$_Human.superclass = &OBJC_CLASS_$_NSObject; OBJC_CLASS_$_Human.cache = &_objc_empty_cache;}extern \"C\" unsigned long int OBJC_IVAR_$_AlanWang$_hack __attribute__ ((used, section (\"__DATA,__objc_ivar\"))) = __OFFSETOFIVAR__(struct AlanWang, _hack);static struct /*_ivar_list_t*/ { unsigned int entsize; // sizeof(struct _prop_t) unsigned int count; struct _ivar_t ivar_list[1];} _OBJC_$_INSTANCE_VARIABLES_AlanWang __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { sizeof(_ivar_t), 1, {{(unsigned long int *)&OBJC_IVAR_$_AlanWang$_hack, \"_hack\", \"@\\\"NSString\\\"\", 3, 8}}};static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[3];} _OBJC_$_INSTANCE_METHODS_AlanWang __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { sizeof(_objc_method), 3, {{(struct objc_selector *)\"cook\", \"v16@0:8\", (void *)_I_AlanWang_cook}, {(struct objc_selector *)\"hack\", \"@16@0:8\", (void *)_I_AlanWang_hack}, {(struct objc_selector *)\"setHack:\", \"v24@0:8@16\", (void *)_I_AlanWang_setHack_}}};static struct /*_method_list_t*/ { unsigned int entsize; // sizeof(struct _objc_method) unsigned int method_count; struct _objc_method method_list[1];} _OBJC_$_CLASS_METHODS_AlanWang __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { sizeof(_objc_method), 1, {{(struct objc_selector *)\"swim\", \"v16@0:8\", (void *)_C_AlanWang_swim}}};static struct /*_prop_list_t*/ { unsigned int entsize; // sizeof(struct _prop_t) unsigned int count_of_properties; struct _prop_t prop_list[1];} _OBJC_$_PROP_LIST_AlanWang __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { sizeof(_prop_t), 1, {{\"hack\",\"T@\\\"NSString\\\",&,V_hack\"}}};static struct _class_ro_t _OBJC_METACLASS_RO_$_AlanWang __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { 1, sizeof(struct _class_t), sizeof(struct _class_t), 0, \"AlanWang\", (const struct _method_list_t *)&_OBJC_$_CLASS_METHODS_AlanWang, 0, 0, 0, 0, };static struct _class_ro_t _OBJC_CLASS_RO_$_AlanWang __attribute__ ((used, section (\"__DATA,__objc_const\"))) = { 0, __OFFSETOFIVAR__(struct AlanWang, _hack), sizeof(struct AlanWang_IMPL), 0, \"AlanWang\", (const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_AlanWang, 0, (const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_AlanWang, 0, (const struct _prop_list_t *)&_OBJC_$_PROP_LIST_AlanWang,};extern \"C\" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_Human;extern \"C\" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;extern \"C\" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_AlanWang __attribute__ ((used, section (\"__DATA,__objc_data\"))) = { 0, // &OBJC_METACLASS_$_NSObject, 0, // &OBJC_METACLASS_$_Human, 0, // (void *)&_objc_empty_cache, 0, // unused, was (void *)&_objc_empty_vtable, &_OBJC_METACLASS_RO_$_AlanWang,};extern \"C\" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Human;extern \"C\" __declspec(dllexport) struct _class_t OBJC_CLASS_$_AlanWang __attribute__ ((used, section (\"__DATA,__objc_data\"))) = { 0, // &OBJC_METACLASS_$_AlanWang, 0, // &OBJC_CLASS_$_Human, 0, // (void *)&_objc_empty_cache, 0, // unused, was (void *)&_objc_empty_vtable, &_OBJC_CLASS_RO_$_AlanWang,};static void OBJC_CLASS_SETUP_$_AlanWang(void ) { OBJC_METACLASS_$_AlanWang.isa = &OBJC_METACLASS_$_NSObject; OBJC_METACLASS_$_AlanWang.superclass = &OBJC_METACLASS_$_Human; OBJC_METACLASS_$_AlanWang.cache = &_objc_empty_cache; OBJC_CLASS_$_AlanWang.isa = &OBJC_METACLASS_$_AlanWang; OBJC_CLASS_$_AlanWang.superclass = &OBJC_CLASS_$_Human; OBJC_CLASS_$_AlanWang.cache = &_objc_empty_cache;}#pragma section(\".objc_inithooks$B\", long, read, write)__declspec(allocate(\".objc_inithooks$B\")) static void *OBJC_CLASS_SETUP[] = { (void *)&OBJC_CLASS_SETUP_$_Human, (void *)&OBJC_CLASS_SETUP_$_AlanWang,};static struct _class_t *L_OBJC_LABEL_CLASS_$ [2] __attribute__((used, section (\"__DATA, __objc_classlist,regular,no_dead_strip\")))= { &OBJC_CLASS_$_Human, &OBJC_CLASS_$_AlanWang,};static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 }; 可以搜索关键字AlanWang来定位代码,当然往往都在最下面 不难发现c++重写后OC类被加了前缀 OBJC_CLASS_REF_$_ OBJC_CLASS_$_ OBJC_METACLASS_$_ 查阅Swift源码可见的确是这些前缀。可见在rewrite过程中总会被加一些标识前缀,所以阅读时自行转化前缀 123456789101112131415161718192021222324252627// An Objective-C class reference reference. The symbol is private, so // the mangling is unimportant; it should just be readable in LLVM IR.case Kind::ObjCClassRef: { llvm::SmallString<64> tempBuffer; StringRef name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(tempBuffer); std::string Result(\"OBJC_CLASS_REF_$_\"); Result.append(name.data(), name.size()); return Result;} // An Objective-C class reference; not a swift mangling.case Kind::ObjCClass: { llvm::SmallString<64> TempBuffer; StringRef Name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(TempBuffer); std::string Result(\"OBJC_CLASS_$_\"); Result.append(Name.data(), Name.size()); return Result;} // An Objective-C metaclass reference; not a swift mangling.case Kind::ObjCMetaclass: { llvm::SmallString<64> TempBuffer; StringRef Name = cast<ClassDecl>(getDecl())->getObjCRuntimeName(TempBuffer); std::string Result(\"OBJC_METACLASS_$_\"); Result.append(Name.data(), Name.size()); return Result;} 先来看两个类的初始化方法 extern “C” 的意思是把它当做C语言编译 比如C++支持重载,而C不支持 1extern \"C\" __declspec(dllexport) struct _class_t OBJC_CLASS_$_Human; 1234567extern \"C\" __declspec(dllexport) struct _class_t OBJC_CLASS_$_AlanWang __attribute__ ((used, section (\"__DATA,__objc_data\"))) = { 0, // &OBJC_METACLASS_$_AlanWang, 0, // &OBJC_CLASS_$_Human, 0, // (void *)&_objc_empty_cache, 0, // unused, was (void *)&_objc_empty_vtable, &_OBJC_CLASS_RO_$_AlanWang,}; AlanWang 这个类被存在"__DATA, __objc_classlist,regular,no_dead_strip"section中 _class_t1234567struct _class_t { struct _class_t *isa; struct _class_t *superclass; void *cache; void *vtable; struct _class_ro_t *ro;}; _class_t 的 isa 和 superclass 指向 _class_t SETUP AlanWang12345678static void OBJC_CLASS_SETUP_$_AlanWang(void ) { OBJC_METACLASS_$_AlanWang.isa = &OBJC_METACLASS_$_NSObject; OBJC_METACLASS_$_AlanWang.superclass = &OBJC_METACLASS_$_Human; OBJC_METACLASS_$_AlanWang.cache = &_objc_empty_cache; OBJC_CLASS_$_AlanWang.isa = &OBJC_METACLASS_$_AlanWang; OBJC_CLASS_$_AlanWang.superclass = &OBJC_CLASS_$_Human; OBJC_CLASS_$_AlanWang.cache = &_objc_empty_cache;} SETUP Human12345678static void OBJC_CLASS_SETUP_$_Human(void ) { OBJC_METACLASS_$_Human.isa = &OBJC_METACLASS_$_NSObject; OBJC_METACLASS_$_Human.superclass = &OBJC_METACLASS_$_NSObject; OBJC_METACLASS_$_Human.cache = &_objc_empty_cache; OBJC_CLASS_$_Human.isa = &OBJC_METACLASS_$_Human; OBJC_CLASS_$_Human.superclass = &OBJC_CLASS_$_NSObject; OBJC_CLASS_$_Human.cache = &_objc_empty_cache;} 一图胜千言。 _class_ro_t123456789101112struct _class_ro_t { unsigned int flags; unsigned int instanceStart; unsigned int instanceSize; const unsigned char *ivarLayout; const char *name; const struct _method_list_t *baseMethods; const struct _objc_protocol_list *baseProtocols; const struct _ivar_list_t *ivars; const unsigned char *weakIvarLayout; const struct _prop_list_t *properties;}; ro readonly 12345struct _objc_method { struct objc_selector * _cmd; const char *method_type; void *_imp;}; 通观全文,不难发现并未找到alloc方法的声明与实现 main函数中的实现 123456int main(int argc, char * argv[]) { NSString * appDelegateClassName; AlanWang *man = ((AlanWang *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass(\"AlanWang\"), sel_registerName(\"alloc\")); man = ((AlanWang *(*)(id, SEL))(void *)objc_msgSend)((id)man, sel_registerName(\"init\")); ...} 到运行时dyld通知回调时执行map_images时才会注册alloc方法 Type Encodings 运行时体系结构(Runtime Architecture)具有关键的一些方面 提供了用于启动和执行程序的工具 指定了代码和数据怎样驻留在磁盘上—-也就是说,它制定了二进制格式。它还指定了编译器和相关工具必须怎样生成代码和数据 指定了怎样将代码和数据加载进内存中 指定了怎样解析指向外部库的引用 Mac OS X & iOS 运行时环境: Mach-O 对象的诞生objc_getClass 12345678910111213/************************************************************************ objc_getClass. Return the id of the named class. If the class does* not exist, call _objc_classLoader and then objc_classHandler, either of * which may create a new class.* Warning: doesn't work if aClassName is the name of a posed-for class's isa!**********************************************************************/Class objc_getClass(const char *aClassName){ if (!aClassName) return Nil; // NO unconnected, YES class handler return look_up_class(aClassName, NO, YES);} 提供类名到类映射的哈希表 123// Maps class name to Class, for in-use classes only. NXStrValueMapPrototype.OBJC_EXPORT NXMapTable * _Nullable gdb_objc_realized_classes OBJC_AVAILABLE(10.6, 3.1, 9.0, 1.0, 2.0); 1234567891011121314static Class getClass_impl(const char *name){ runtimeLock.assertLocked(); // allocated in _read_images assert(gdb_objc_realized_classes); // Try runtime-allocated table Class result = (Class)NXMapGet(gdb_objc_realized_classes, name); if (result) return result; // Try table from dyld shared cache return getPreoptimizedClass(name);} 1234567891011121314/************************************************************************ _read_images* Perform initial processing of the headers in the linked * list beginning with headerList. ** Called by: map_images_nolock** Locking: runtimeLock acquired by map_images**********************************************************************/void objc_getClass_read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses){ ... readClass ... _objc_init–>map_images –> map_images_nolock –> _read_images objc_getClass:通过key= className 在哈希表(gdb_objc_realized_classes)找到 Class sel_registerName:通过key= selectorName 在哈希表(namedSelectors )找到SEL objc_msgSend: 参数为Class 与 SEL objc_msgSend伪代码如下 1234567891011121314151617/******************************************************************** * * id objc_msgSend(id self, SEL _cmd, ...); * IMP objc_msgLookup(id self, SEL _cmd, ...); * * objc_msgLookup ABI: * IMP returned in x17 * x16 reserved for our use but not used * ********************************************************************/ id objc_msgSend(id self, SEL _cmd, ...) { Class class = object_getClass(self); IMP imp = class_getMethodImplementation(class, _cmd); return imp ? imp(self, _cmd, ...) : 0;} 在cache中找 在类的方法列表中找 消息转发 终于找到alloc方法了 运行时类的结构objc源代码下载 目前最新的是objc4-779.1 objc.h 123456789101112131415161718192021222324252627/* * objc.h * Copyright 1988-1996, NeXT Software, Inc. */#if !OBJC_TYPES_DEFINED/// An opaque type that represents an Objective-C class.typedef struct objc_class *Class;/// Represents an instance of a class.struct objc_object { Class _Nonnull isa OBJC_ISA_AVAILABILITY;};/// A pointer to an instance of a class.typedef struct objc_object *id;#endif/// An opaque type that represents a method selector.typedef struct objc_selector *SEL;/// A pointer to the function of a method implementation. #if !OBJC_OLD_DISPATCH_PROTOTYPEStypedef void (*IMP)(void /* id, SEL, ... */ ); #elsetypedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); #endif Class 意为一个不透明的类型(指针) 代表一个Objective-C的类 id 意为类的实例的指针 SEL 意为一个不透明的类型 代表一个方法选择器 IMP 意为方法实现的函数指针 123456789101112131415/* * objc-api.h *//* OBJC_OLD_DISPATCH_PROTOTYPES == 0 enforces the rule that the dispatch * functions must be cast to an appropriate function pointer type. */#if !defined(OBJC_OLD_DISPATCH_PROTOTYPES)# if __swift__ // Existing Swift code expects IMP to be Comparable. // Variadic IMP is comparable via OpaquePointer; non-variadic IMP isn't.# define OBJC_OLD_DISPATCH_PROTOTYPES 1# else# define OBJC_OLD_DISPATCH_PROTOTYPES 0# endif#endif 如果有swift的代码。则可变的IMP是不透明指针,不可变则不是。 OBJC_TYPES_DEFINED` 宏定义是因为在另一处 objc-private.h中也有声明 避免重复定义 1234567891011121314151617181920212223242526272829303132/* * objc-private.h * Copyright 1988-1996, NeXT Software, Inc. */ #define OBJC_TYPES_DEFINED 1 struct objc_class;struct objc_object;typedef struct objc_class *Class;typedef struct objc_object *id;namespace { struct SideTable;};#include \"isa.h\"union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } Class cls; uintptr_t bits;#if defined(ISA_BITFIELD) struct { ISA_BITFIELD; // defined in isa.h };#endif};... Meta 元类宏objc-config.h中的宏__LP64__字长为64位的操作系统 在 XL C/C++ V10.1 中添加了四个新宏: _ILP32 __ILP32__ 仅当对一个目标进行编译时,才可定义为 1,其中该目标的 long int,int 和指针使用的都是 32 位。否则, 不能对它定义。 _LP64 __LP64__ 仅当对一个目标进行编译时,才可定义为 1,其中该目标的 long int 和指针使用的都是 64 位而 int 使用的是 32 位。否则, 不能对它定义。 https://www.ibm.com/support/knowledgecenter/zh/SSXVZZ_13.1.0/com.ibm.xlcpp131.linux.doc/getstart/predef_macro_v10v12.html 与编译器运行的平台有关,利用clang可以查看对应的宏定义 1234➜ / clang -dM -E -x c /dev/null#define OBJC_NEW_PROPERTIES 1#define _LP64 1... __arm__32-bit ARM 12345678910#if __PTRAUTH_INTRINSICS__// Always use ptrauth when it's supported.#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_PTRAUTH#elif defined(__arm__)// 32-bit ARM uses no encoding.#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_NONE#else// Everything else uses ISA ^ IMP.#define CACHE_IMP_ENCODING CACHE_IMP_ENCODING_ISA_XOR#endif SUPPORT_PACKED_ISAisa 视为被数据包裹的可以掩码化的指针。 12345678// Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa // field as a maskable pointer with other data around it.#if (!__LP64__ || TARGET_OS_WIN32 || \\ (TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC))# define SUPPORT_PACKED_ISA 0#else# define SUPPORT_PACKED_ISA 1#endif iOS中为1 SUPPORT_TAGGED_POINTERS1234567// Define SUPPORT_TAGGED_POINTERS=1 to enable tagged pointer objects// Be sure to edit tagged pointer SPI in objc-internal.h as well.#if !(__OBJC2__ && __LP64__)# define SUPPORT_TAGGED_POINTERS 0#else# define SUPPORT_TAGGED_POINTERS 1#endif iOS中为1 SUPPORT_INDEXED_ISA123456789// Define SUPPORT_INDEXED_ISA=1 on platforms that store the class in the isa // field as an index into a class table.// Note, keep this in sync with any .s files which also define it.// Be sure to edit objc-abi.h as well.#if __ARM_ARCH_7K__ >= 2 || (__arm64__ && !__LP64__)# define SUPPORT_INDEXED_ISA 1#else# define SUPPORT_INDEXED_ISA 0#endif iOS中为0 (__arm64__ && !__LP64__) 这个条件为了兼容32位系统? SUPPORT_PACKED_ISA12345678// Define SUPPORT_PACKED_ISA=1 on platforms that store the class in the isa // field as a maskable pointer with other data around it.#if (!__LP64__ || TARGET_OS_WIN32 || \\ (TARGET_OS_SIMULATOR && !TARGET_OS_IOSMAC))# define SUPPORT_PACKED_ISA 0#else# define SUPPORT_PACKED_ISA 1#endif iOS中为1 SUPPORT_NONPOINTER_ISA可能在isa中存有额外的数据,支持所有平台 1234567// Define SUPPORT_NONPOINTER_ISA=1 on any platform that may store something// in the isa field that is not a raw pointer.#if !SUPPORT_INDEXED_ISA && !SUPPORT_PACKED_ISA# define SUPPORT_NONPOINTER_ISA 0#else# define SUPPORT_NONPOINTER_ISA 1#endif SUPPORT_FIXUP12345678// Define SUPPORT_FIXUP=1 to repair calls sites for fixup dispatch.// Fixup messaging itself is no longer supported.// Be sure to edit objc-abi.h as well (objc_msgSend*_fixup)#if !(defined(__x86_64__) && (TARGET_OS_OSX || TARGET_OS_SIMULATOR))# define SUPPORT_FIXUP 0#else# define SUPPORT_FIXUP 1#endif SUPPORT_ZEROCOST_EXCEPTIONS1234567// Define SUPPORT_ZEROCOST_EXCEPTIONS to use "zero-cost" exceptions for OBJC2.// Be sure to edit objc-exception.h as well (objc_add/removeExceptionHandler)#if !__OBJC2__ || (defined(__arm__) && __USING_SJLJ_EXCEPTIONS__)# define SUPPORT_ZEROCOST_EXCEPTIONS 0#else# define SUPPORT_ZEROCOST_EXCEPTIONS 1#endif ISA_BITFIELD, RC_ONE, RC_HALF前文提到iOS中SUPPORT_PACKED_ISA为1,所以iOS64位机中isa的相关字段定义如下 12345678910111213141516171819202122232425262728#if SUPPORT_PACKED_ISA // extra_rc must be the MSB-most field (so it matches carry/overflow flags) // nonpointer must be the LSB (fixme or get rid of it) // shiftcls must occupy the same bits that a real class pointer would // bits + RC_ONE is equivalent to extra_rc + 1 // RC_HALF is the high bit of extra_rc (i.e. half of its range) // future expansion: // uintptr_t fast_rr : 1; // no r/r overrides // uintptr_t lock : 2; // lock for atomic property, @synch // uintptr_t extraBytes : 1; // allocated with extra bytes# if __arm64__# define ISA_MASK 0x0000000ffffffff8ULL# define ISA_MAGIC_MASK 0x000003f000000001ULL# define ISA_MAGIC_VALUE 0x000001a000000001ULL# define ISA_BITFIELD \\ uintptr_t nonpointer : 1; \\ uintptr_t has_assoc : 1; \\ uintptr_t has_cxx_dtor : 1; \\ uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \\ uintptr_t magic : 6; \\ uintptr_t weakly_referenced : 1; \\ uintptr_t deallocating : 1; \\ uintptr_t has_sidetable_rc : 1; \\ uintptr_t extra_rc : 19# define RC_ONE (1ULL<<45)# define RC_HALF (1ULL<<18) MSB: most significant bit 最高有效位(最左位) LSB: least significant bit 最低有效位 ISA_MAGIC_VALUE0x000001a000000001ULL TaggedPointer1234567#if (TARGET_OS_OSX || TARGET_OS_IOSMAC) && __x86_64__ // 64-bit Mac - tag bit is LSB# define OBJC_MSB_TAGGED_POINTERS 0#else // Everything else - tag bit is MSB# define OBJC_MSB_TAGGED_POINTERS 1#endif 只有64位 Mac 上是LSB 也就是看最低位 iOS中看MSB 也就是最高位(第63位) 1234567891011121314151617181920212223#if OBJC_MSB_TAGGED_POINTERS# define _OBJC_TAG_MASK (1UL<<63)# define _OBJC_TAG_INDEX_SHIFT 60# define _OBJC_TAG_SLOT_SHIFT 60# define _OBJC_TAG_PAYLOAD_LSHIFT 4# define _OBJC_TAG_PAYLOAD_RSHIFT 4# define _OBJC_TAG_EXT_MASK (0xfUL<<60)# define _OBJC_TAG_EXT_INDEX_SHIFT 52# define _OBJC_TAG_EXT_SLOT_SHIFT 52# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 12# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12#else# define _OBJC_TAG_MASK 1UL# define _OBJC_TAG_INDEX_SHIFT 1# define _OBJC_TAG_SLOT_SHIFT 0# define _OBJC_TAG_PAYLOAD_LSHIFT 0# define _OBJC_TAG_PAYLOAD_RSHIFT 4# define _OBJC_TAG_EXT_MASK 0xfUL# define _OBJC_TAG_EXT_INDEX_SHIFT 4# define _OBJC_TAG_EXT_SLOT_SHIFT 4# define _OBJC_TAG_EXT_PAYLOAD_LSHIFT 0# define _OBJC_TAG_EXT_PAYLOAD_RSHIFT 12#endif 流程libdispatch_init -> _os_object_init -> _objc_init —[_dyld_objc_notify_register(&map_images, load_images, unmap_image)] —> dyld 通知 -> (map_images,load_images,unmap_image) map_images->arr_init-> AutoreleasePoolPage::init(); -> 注册析构到对应线程 SideTableInit();-> realizeClassWithoutSwift addRootClass(cls); 123456789101112# 没有supercls的根类(Class) $0 = NSObject(Class) $1 = __NSAtom(Class) $2 = NSProxy-<main>-(Class) $3 = Object(Class) $4 = __NSMessageBuilder(Class) $5 = _NSZombie_(Class) $6 = __NSGenericDeallocHandler(Class) $7 = NSLeafProxy(Class) $8 = JSExport(Class) $9 = _CNZombie_ 从dyld进入到libSystem先看ImageLoaderMachO::doModInitFunctions 12Initializer* inits = (Initializer*)(sect->addr + fSlide);const size_t count = sect->size / sizeof(uintptr_t); 通过这两句得到libSystem.B.dylib__mod_init_funcssection inits数组,元素类型为Initializer* 函数指针类型 12// dyld/src/ImageLoader.htypedef void (*Initializer)(int argc, const char* argv[], const char* envp[], const char* apple[], const ProgramVars* vars); otool -l /usr/lib/libSystem.dylib查看libSystem依赖的库 libSystem.dylib会加载libSystem.B.dylib 12345678910➜ ~ otool -L /usr/lib/libSystem.dylib/usr/lib/libSystem.dylib: /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1) /usr/lib/system/libcache.dylib (compatibility version 1.0.0, current version 81.0.0) /usr/lib/system/libcommonCrypto.dylib (compatibility version 1.0.0, current version 60118.250.2) /usr/lib/system/libcompiler_rt.dylib (compatibility version 1.0.0, current version 63.4.0) /usr/lib/system/libcopyfile.dylib (compatibility version 1.0.0, current version 1.0.0) /usr/lib/system/libcorecrypto.dylib (compatibility version 1.0.0, current version 602.255.2) /usr/lib/system/libdispatch.dylib (compatibility version 1.0.0, current version 1008.255.1) ... 查看load commands 12345678910111213141516➜ ~ otool -l /usr/lib/libSystem.B.dylib/usr/lib/libSystem.B.dylib:...Section sectname __mod_init_func segname __DATA addr 0x0000000000002218 size 0x0000000000000008 offset 8728 align 2^3 (8) reloff 0 nreloc 0 flags 0x00000009 reserved1 0 reserved2 0 ... otool -l /usr/lib/libSystem.dylib` 123456789101112Section sectname __mod_init_func segname __DATA addr 0x0000000000002218 size 0x0000000000000008 offset 8728 align 2^3 (8) reloff 0 nreloc 0 flags 0x00000009 reserved1 0 reserved2 0 调用数组里的函数 123Initializer func = inits[j];func(context.argc, context.argv, context.envp, context.apple, &context.programVars); 汇编片段 12345 0x1090833a0 <+510>: movq -0x68(%rbp), %r8 0x1090833a4 <+514>: callq *%r13-> 0x1090833a7 <+517>: testq %r14, %r14 0x1090833aa <+520>: jne 0x1090833c9 ; <+551> 0x1090833ac <+522>: leaq 0x42e45(%rip), %rax ; dyld::gLibSystemHelpers 此时控制台 123(lldb) register read r13r13 = 0x00007fff4ff16773 libSystem.B.dylib`libSystem_initializer 如果设置了DYLD_PRINT_INITIALIZERS变量,还会打印 1dyld: calling initializer function 0x7fff4ff16773 in /usr/lib/libSystem.B.dylib 对应代码 12if ( context.verboseInit ) dyld::log(\"dyld: calling initializer function %p in %s\\n\", func, this->getPath()); 小结:ImageLoaderMachO::doModInitFunctions方法中找到__mod_init_funcs section,通过地址加fSlide计算得到inits数组,然后调用libSystem.B.dylib`libSystem_initializer ImageLoaderMachO::doModInitFunctions相关代码如下 SECTION_TYPE 12345678/* * The flags field of a section structure is separated into two parts a section * type and section attributes. The section types are mutually exclusive (it * can only have one type) but the section attributes are not (it may have more * than one attribute). */#define SECTION_TYPE 0x000000ff /* 256 section types */#define SECTION_ATTRIBUTES 0xffffff00 /* 24 section attributes */ S_MOD_INIT_FUNC_POINTERS 123// darwin-xnu/EXTERNAL_HEADERS/mach-o/loader.h#define S_MOD_INIT_FUNC_POINTERS 0x9 /* section with only function pointers for initialization*/ 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889void ImageLoaderMachO::doModInitFunctions(const LinkContext& context){ if ( fHasInitializers ) { const uint32_t cmd_count = ((macho_header*)fMachOData)->ncmds; const struct load_command* const cmds = (struct load_command*)&fMachOData[sizeof(macho_header)]; const struct load_command* cmd = cmds; for (uint32_t i = 0; i < cmd_count; ++i) { if ( cmd->cmd == LC_SEGMENT_COMMAND ) { const struct macho_segment_command* seg = (struct macho_segment_command*)cmd; const struct macho_section* const sectionsStart = (struct macho_section*)((char*)seg + sizeof(struct macho_segment_command)); const struct macho_section* const sectionsEnd = &sectionsStart[seg->nsects]; for (const struct macho_section* sect=sectionsStart; sect < sectionsEnd; ++sect) { // #define SECTION_TYPE 0x000000ff // 取sect->flags后两位,意为section type const uint8_t type = sect->flags & SECTION_TYPE; // #define S_MOD_INIT_FUNC_POINTERS 0x9 if ( type == S_MOD_INIT_FUNC_POINTERS ) { Initializer* inits = (Initializer*)(sect->addr + fSlide); const size_t count = sect->size / sizeof(uintptr_t); // <rdar://problem/23929217> Ensure __mod_init_func section is within segment if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) dyld::throwf(\"__mod_init_funcs section has malformed address range for %s\\n\", this->getPath()); for (size_t j=0; j < count; ++j) { Initializer func = inits[j]; // <rdar://problem/8543820&9228031> verify initializers are in image if ( ! this->containsAddress(stripPointer((void*)func)) ) { dyld::throwf(\"initializer function %p not in mapped image for %s\\n\", func, this->getPath()); } if ( ! dyld::gProcessInfo->libSystemInitialized ) { // <rdar://problem/17973316> libSystem initializer must run first const char* installPath = getInstallPath(); if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) ) dyld::throwf(\"initializer in image (%s) that does not link with libSystem.dylib\\n\", this->getPath()); } if ( context.verboseInit ) dyld::log(\"dyld: calling initializer function %p in %s\\n\", func, this->getPath()); bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL); { dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0); func(context.argc, context.argv, context.envp, context.apple, &context.programVars); } bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL); if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) { // now safe to use malloc() and other calls in libSystem.dylib dyld::gProcessInfo->libSystemInitialized = true; } } } else if ( type == S_INIT_FUNC_OFFSETS ) { const uint32_t* inits = (uint32_t*)(sect->addr + fSlide); const size_t count = sect->size / sizeof(uint32_t); // Ensure section is within segment if ( (sect->addr < seg->vmaddr) || (sect->addr+sect->size > seg->vmaddr+seg->vmsize) || (sect->addr+sect->size < sect->addr) ) dyld::throwf(\"__init_offsets section has malformed address range for %s\\n\", this->getPath()); if ( seg->initprot & VM_PROT_WRITE ) dyld::throwf(\"__init_offsets section is not in read-only segment %s\\n\", this->getPath()); for (size_t j=0; j < count; ++j) { uint32_t funcOffset = inits[j]; // verify initializers are in TEXT segment if ( funcOffset > seg->filesize ) { dyld::throwf(\"initializer function offset 0x%08X not in mapped image for %s\\n\", funcOffset, this->getPath()); } if ( ! dyld::gProcessInfo->libSystemInitialized ) { // <rdar://problem/17973316> libSystem initializer must run first const char* installPath = getInstallPath(); if ( (installPath == NULL) || (strcmp(installPath, libSystemPath(context)) != 0) ) dyld::throwf(\"initializer in image (%s) that does not link with libSystem.dylib\\n\", this->getPath()); } Initializer func = (Initializer)((uint8_t*)this->machHeader() + funcOffset); if ( context.verboseInit ) dyld::log(\"dyld: calling initializer function %p in %s\\n\", func, this->getPath()); bool haveLibSystemHelpersBefore = (dyld::gLibSystemHelpers != NULL); { dyld3::ScopedTimer(DBG_DYLD_TIMING_STATIC_INITIALIZER, (uint64_t)fMachOData, (uint64_t)func, 0); func(context.argc, context.argv, context.envp, context.apple, &context.programVars); } bool haveLibSystemHelpersAfter = (dyld::gLibSystemHelpers != NULL); if ( !haveLibSystemHelpersBefore && haveLibSystemHelpersAfter ) { // now safe to use malloc() and other calls in libSystem.dylib dyld::gProcessInfo->libSystemInitialized = true; } } } } } cmd = (const struct load_command*)(((char*)cmd)+cmd->cmdsize); } }} ImageLoaderMachO::doModInitFunctions->libSystem.B.dylib`libSystem_initializer 从libSystem到libSystem1libSystem.B.dylib`libSystem_initializer->libdispatch.dylib`libdispatch_init 1234567891011voidlibdispatch_init(void){ ... _dispatch_hw_config_init(); _dispatch_time_init(); _dispatch_vtable_init(); _os_object_init(); _voucher_init(); _dispatch_introspection_init();} libdispatch_init中, dispatch进行一系列的初始化后,调用_os_object_init。 1234567891011121314151617181920void_os_object_init(void){ _objc_init(); Block_callbacks_RR callbacks = { sizeof(Block_callbacks_RR), (void (*)(const void *))&objc_retain, (void (*)(const void *))&objc_release, (void (*)(const void *))&_os_objc_destructInstance }; _Block_use_RR2(&callbacks);#if DISPATCH_COCOA_COMPAT const char *v = getenv(\"OBJC_DEBUG_MISSING_POOLS\"); if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v); v = getenv(\"DISPATCH_DEBUG_MISSING_POOLS\"); if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v); v = getenv(\"LIBDISPATCH_DEBUG_MISSING_POOLS\"); if (v) _os_object_debug_missing_pools = _dispatch_parse_bool(v);#endif} _os_object_init->_objc_init 从libdispatch进入到objc123456789101112131415161718192021/************************************************************************ _objc_init* Bootstrap initialization. Registers our image notifier with dyld.* Called by libSystem BEFORE library initialization time**********************************************************************/void _objc_init(void){ static bool initialized = false; if (initialized) return; initialized = true; // fixme defer initialization until an objc-using image is found? environ_init(); tls_init(); static_init(); lock_init(); exception_init(); _dyld_objc_notify_register(&map_images, load_images, unmap_image);} 进行一系列初始化 注册三个方法到dyld registerObjCNotifiers->notifyBatchPartial objc 接收到通知 1234567891011121314/************************************************************************ map_images* Process the given images which are being mapped in by dyld.* Calls ABI-agnostic code after taking ABI-specific locks.** Locking: write-locks runtimeLock**********************************************************************/voidmap_images(unsigned count, const char * const paths[], const struct mach_header * const mhdrs[]){ mutex_locker_t lock(runtimeLock); return map_images_nolock(count, paths, mhdrs);} 123456789101112131415161718192021void map_images_nolock(unsigned mhCount, const char * const mhPaths[], const struct mach_header * const mhdrs[]){...// Perform one-time runtime initialization that must be deferred until // the executable itself is found. This needs to be done before // further initialization. // (The executable may not be present in this infoList if the // executable does not contain Objective-C code but Objective-C // is dynamically loaded later. if (firstTime) { sel_init(selrefCount); arr_init(); ... } if (hCount > 0) { _read_images(hList, hCount, totalClasses, unoptimizedTotalClasses); } ... 1234567891011121314151617181920212223242526272829303132333435363738/************************************************************************ sel_init* Initialize selector tables and register selectors used internally.**********************************************************************/void sel_init(size_t selrefCount){ ... // Register selectors used by libobjc#define s(x) SEL_##x = sel_registerNameNoLock(#x, NO)#define t(x,y) SEL_##y = sel_registerNameNoLock(#x, NO) mutex_locker_t lock(selLock); s(load); s(initialize); t(resolveInstanceMethod:, resolveInstanceMethod); t(resolveClassMethod:, resolveClassMethod); t(.cxx_construct, cxx_construct); t(.cxx_destruct, cxx_destruct); s(retain); s(release); s(autorelease); s(retainCount); s(alloc); t(allocWithZone:, allocWithZone); s(dealloc); s(copy); s(new); t(forwardInvocation:, forwardInvocation); t(_tryRetain, tryRetain); t(_isDeallocating, isDeallocating); s(retainWeakReference); s(allowsWeakReference);#undef s#undef t} 1234567891011121314151617181920212223242526272829SEL sel_registerNameNoLock(const char *name, bool copy) { return __sel_registerName(name, 0, copy); // NO lock, maybe copy}static NXMapTable *namedSelectors;static SEL __sel_registerName(const char *name, bool shouldLock, bool copy) { SEL result = 0; ... if (namedSelectors) { result = (SEL)NXMapGet(namedSelectors, name); } if (result) return result; // No match. Insert. if (!namedSelectors) { // 新建哈希表 namedSelectors = NXCreateMapTable(NXStrValueMapPrototype, (unsigned)SelrefCount); } if (!result) { // 查表没有命中,则插入 result = sel_alloc(name, copy); // fixme choose a better container (hash not map for starters) NXMapInsert(namedSelectors, sel_getName(result), result); } return result;} sel_init中的方法注册到全局静态哈希表namedSelectors,数据结构定义如下 12345678typedef struct { const NXHashTablePrototype * _Nonnull prototype OBJC_HASH_AVAILABILITY; unsigned count OBJC_HASH_AVAILABILITY; unsigned nbBuckets OBJC_HASH_AVAILABILITY; void * _Nullable buckets OBJC_HASH_AVAILABILITY; const void * _Nullable info OBJC_HASH_AVAILABILITY; } NXHashTable OBJC_HASH_AVAILABILITY; /* private data structure; may change */ 建表过程会调用到bootstrap 1234567891011static void bootstrap (void) { free(malloc(8)); prototypes = ALLOCTABLE (DEFAULT_ZONE); prototypes->prototype = &protoPrototype; prototypes->count = 1; prototypes->nbBuckets = 1; /* has to be 1 so that the right bucket is 0 */ prototypes->buckets = ALLOCBUCKETS(DEFAULT_ZONE, 1); prototypes->info = NULL; ((HashBucket *) prototypes->buckets)[0].count = 1; ((HashBucket *) prototypes->buckets)[0].elements.one = &protoPrototype; }; arr_init12345void arr_init(void) { AutoreleasePoolPage::init(); SideTableInit();} 初始化AutoreleasePoolPage 初始化SideTable _read_images123456789101112131415161718192021222324252627282930313233343536373839/************************************************************************ _read_images* Perform initial processing of the headers in the linked * list beginning with headerList. ** Called by: map_images_nolock** Locking: runtimeLock acquired by map_images**********************************************************************/void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses){... // Realize non-lazy classes (for +load methods and static instances) for (EACH_HEADER) { classref_t *classlist = _getObjc2NonlazyClassList(hi, &count); for (i = 0; i < count; i++) { Class cls = remapClass(classlist[i]); if (!cls) continue; // hack for class __ARCLite__, which didn't get this above ... addClassTableEntry(cls); if (cls->isSwiftStable()) { if (cls->swiftMetadataInitializer()) { _objc_fatal(\"Swift class %s with a metadata initializer \" \"is not allowed to be non-lazy\", cls->nameForLogging()); } // fixme also disallow relocatable classes // We can't disallow all Swift classes because of // classes like Swift.__EmptyArrayStorage } realizeClassWithoutSwift(cls); } } ... } releaize先看主体流程 1234567891011121314151617181920212223242526272829303132333435363738394041424344/************************************************************************ realizeClassWithoutSwift* Performs first-time initialization on class cls, * including allocating its read-write data.* Does not perform any Swift-side initialization.* Returns the real class structure for the class. * Locking: runtimeLock must be write-locked by the caller**********************************************************************/static Class realizeClassWithoutSwift(Class cls){...0 //MARK: - 自注解 NSObject递归结束条件 if (!cls) return nil; //MARK: - 自注解 子类,元类结束条件 if (cls->isRealized()) return cls;...1 // Realize superclass and metaclass, if they aren't already. // This needs to be done after RW_REALIZED is set above, for root classes. // This needs to be done after class index is chosen, for root metaclasses. // This assumes that none of those classes have Swift contents, // or that Swift's initializers have already been called. // fixme that assumption will be wrong if we add support // for ObjC subclasses of Swift classes. //MARK: - 自注解 递归确保根节点realize supercls = realizeClassWithoutSwift(remapClass(cls->superclass)); metacls = realizeClassWithoutSwift(remapClass(cls->ISA()));...2// Update superclass and metaclass in case of remapping cls->superclass = supercls; cls->initClassIsa(metacls); ...3 // Connect this class to its superclass's subclass lists if (supercls) { addSubclass(supercls, cls); } else { //MARK: - 自注解 添加根类 addRootClass(cls); } // Attach categories methodizeClass(cls); return cls;} 123456789101112// addRootClass(cls); cls包含的类(Class) $0 = NSObject(Class) $1 = __NSAtom(Class) $2 = NSProxy<main>(Class) $3 = Object(Class) $4 = __NSMessageBuilder(Class) $5 = _NSZombie_(Class) $6 = __NSGenericDeallocHandler(Class) $7 = NSLeafProxy(Class) $8 = JSExport(Class) $9 = _CNZombie_ 可以看到,main函数前,有三个cls,NSObject, __NSAtom, NSProxy 12345678static void addRootClass(Class cls){ runtimeLock.assertLocked(); assert(cls->isRealized()); cls->data()->nextSiblingClass = _firstRealizedClass; _firstRealizedClass = cls;} addRootClass 根类数据结构的调整,可以发现为单链表,每一次把新结点通过头插法插入到表头(_firstRealizedClass) 123456789101112/************************************************************************ addSubclass* Adds subcls as a subclass of supercls.* Locking: runtimeLock must be held by the caller.**********************************************************************/static void addSubclass(Class supercls, Class subcls){ if (supercls && subcls) { ... subcls->data()->nextSiblingClass = supercls->data()->firstSubclass; supercls->data()->firstSubclass = subcls;... addSubclass的方法与root类似,链表的表头为supercls中的firstSubclss. 整体结构如图 省略了尾(空)结点 追究细节,先看1处省略的代码 123456789101112131415161718ro = (const class_ro_t *)cls->data(); if (ro->flags & RO_FUTURE) { // This was a future class. rw data is already allocated. rw = cls->data(); ro = cls->data()->ro; cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); } else { // Normal class. Allocate writeable class data. rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); rw->ro = ro; //MARK: - 自注解 每次递归都会把路径上的cls设置一些数据 RW_REALIZED 代表isRealized,所以元类不会死循环 rw->flags = RW_REALIZED|RW_REALIZING; cls->setData(rw); } isMeta = ro->flags & RO_META; rw->version = isMeta ? 7 : 0; // old runtime went up to 6 ro的声明const class_ro_t *ro; ro = (const class_ro_t *)cls->data();当看到data()的实现不免疑惑 返回rw*类型,为什么要强转ro*类型呢? 为什么能转呢? 123class_rw_t *data() { return bits.data();} 看下class_ro_t与class_rw_t的定义 123456789101112131415161718struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize;#ifdef __LP64__ uint32_t reserved;#endif const uint8_t * ivarLayout; const char * name; method_list_t * baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; const uint8_t * weakIvarLayout; property_list_t *baseProperties; ... 1234567891011121314151617181920struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint32_t version; const class_ro_t *ro; method_array_t methods; property_array_t properties; protocol_array_t protocols; Class firstSubclass; Class nextSiblingClass; char *demangledName;#if SUPPORT_INDEXED_ISA uint32_t index;#endif... ro 9*8字节,rw 8*8字节 这得从编译时说起 编译时还没有rw,看下ro的结构 1234567891011121314151617181920struct _class_ro_t { unsigned int flags; unsigned int instanceStart; unsigned int instanceSize; const unsigned char *ivarLayout; const char *name; const struct _method_list_t *baseMethods; const struct _objc_protocol_list *baseProtocols; const struct _ivar_list_t *ivars; const unsigned char *weakIvarLayout; const struct _prop_list_t *properties;};struct _class_t { struct _class_t *isa; struct _class_t *superclass; void *cache; void *vtable; struct _class_ro_t *ro;}; 对比运行时objc_class的数据结构 12345678910111213141516171819202122struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; class_data_bits_t bits; }typedef uint32_t mask_t;struct cache_t { struct bucket_t *_buckets; mask_t _mask; mask_t _occupied;}// ---------展开后----------struct objc_class { isa_t isa; Class superclass; struct bucket_t *_buckets; uint32_t _mask; uint32_t _occupied; class_data_bits_t bits;} 对比数据结构_class_t与objc_class 发现 objc_class的bits对应于_class_t的ro 就此清晰了,编译时确定的ro结构,在运行时初始化类时通过objc_class的data方法获取到有效的地址接下来会赋给新建的rw。 methodizeClass12345678/************************************************************************ methodizeClass* Fixes up cls's method list, protocol list, and property list.* Attaches any outstanding categories.* Locking: runtimeLock must be held by the caller**********************************************************************/static void methodizeClass(Class cls){ method_list_t 123456789101112// Two bits of entsize are used for fixup markers.struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> { bool isFixedUp() const; void setFixedUp(); uint32_t indexOfMethod(const method_t *meth) const { uint32_t i = (uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize()); assert(i < count); return i; }}; 1234567891011121314/************************************************************************ entsize_list_tt<Element, List, FlagMask>* Generic implementation of an array of non-fragile structs.** Element is the struct type (e.g. method_t)* List is the specialization of entsize_list_tt (e.g. method_list_t)* FlagMask is used to stash extra bits in the entsize field* (e.g. method list fixup markers)**********************************************************************/template <typename Element, typename List, uint32_t FlagMask>struct entsize_list_tt { uint32_t entsizeAndFlags; uint32_t count; Element first; 名词malloc c++的atuo 根据表达式推断出数据类型","categories":[],"tags":[{"name":"iOS","slug":"iOS","permalink":"wanggang.store/tags/iOS/"}]},{"title":"逆向","slug":"逆向","date":"2020-03-13T16:21:03.000Z","updated":"2020-03-26T10:29:07.780Z","comments":true,"path":"2020/03/14/逆向/","link":"","permalink":"wanggang.store/2020/03/14/逆向/","excerpt":"123# -g 产生调试信息# -c 预编译,编译,汇编 生成obj文件$ gcc -g -c file","text":"123# -g 产生调试信息# -c 预编译,编译,汇编 生成obj文件$ gcc -g -c file objdump1$ objdump -D -M intel file.bin | grep main.: -A20 Mac OS X 上 objdump二进制文件实际上可能是指向llvm的 objdump 的链接,具有不同的命令行选项和行为。比如在我的Mac上’/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/objdump’ 所以例子的 -M intel 无效 12345678910111213141516-d --disassemble # 从objfile中反汇编那些特定指令机器码的section。 -D --disassemble-all # 与 -d 类似,但反汇编所有section. -M intel # 汇编代码以Intel语法显示 --x86-asm-syntax=intel # OSX上汇编代码以Intel语法显示 | grep main.: -A20 # 显示main函数20行 国际惯例,先来感受 hello world 123456789// hello.c#include <stdio.h>int main(){ printf(\"Hello World\\n\"); return 0;} gcc -g -c hello.c生成hello.o 12345678910111213141516171819202122➜ hello objdump --source --x86-asm-syntax=intel hello.ohello.o: file format Mach-O 64-bit x86-64Disassembly of section __TEXT,__text:_main:; { 0: 55 push rbp 1: 48 89 e5 mov rbp, rsp 4: 48 83 ec 10 sub rsp, 16 8: c7 45 fc 00 00 00 00 mov dword ptr [rbp - 4], 0; printf(\"Hello World\\n\"); f: 48 8d 3d 14 00 00 00 lea rdi, [rip + 20] 16: b0 00 mov al, 0 18: e8 00 00 00 00 call 0 <_main+0x1d> 1d: 31 c9 xor ecx, ecx 1f: 89 45 f8 mov dword ptr [rbp - 8], eax; return 0; 22: 89 c8 mov eax, ecx 24: 48 83 c4 10 add rsp, 16 28: 5d pop rbp 29: c3 ret 下面开始 通过一个例子函数调用 add.c 12345678910111213// add.c//返回两个参数值之和的函数int addNum(int a, int b){ return a + b;}//调用addNum函数的函数void myFunc(){ int c; c = addNum(123, 456);} gcc -g -c add.c 得到add.o objdump --source --x86-asm-syntax=intel add.o 得到反汇编Intel 格式 1234567891011121314151617181920212223242526272829303132add.o: file format Mach-O 64-bit x86-64Disassembly of section __TEXT,__text:_addNum:; { 0: 55 push rbp 1: 48 89 e5 mov rbp, rsp 4: 89 7d fc mov dword ptr [rbp - 4], edi 7: 89 75 f8 mov dword ptr [rbp - 8], esi; return a + b; a: 8b 75 fc mov esi, dword ptr [rbp - 4] d: 03 75 f8 add esi, dword ptr [rbp - 8] 10: 89 f0 mov eax, esi 12: 5d pop rbp 13: c3 ret 14: 66 2e 0f 1f 84 00 00 00 00 00 nop word ptr cs:[rax + rax] 1e: 66 90 nop_myFunc:; { 20: 55 push rbp 21: 48 89 e5 mov rbp, rsp 24: 48 83 ec 10 sub rsp, 16; c = addNum(123, 456); 28: bf 7b 00 00 00 mov edi, 123 2d: be c8 01 00 00 mov esi, 456 32: e8 00 00 00 00 call 0 <_myFunc+0x17> 37: 89 45 fc mov dword ptr [rbp - 4], eax; } 3a: 48 83 c4 10 add rsp, 16 3e: 5d pop rbp 3f: c3 ret objdump --source add.o 得到AT&T格式 1234567891011121314151617181920212223242526272829303132add.o: file format Mach-O 64-bit x86-64Disassembly of section __TEXT,__text:_addNum:; { 0: 55 pushq %rbp 1: 48 89 e5 movq %rsp, %rbp 4: 89 7d fc movl %edi, -4(%rbp) 7: 89 75 f8 movl %esi, -8(%rbp); return a + b; a: 8b 75 fc movl -4(%rbp), %esi d: 03 75 f8 addl -8(%rbp), %esi 10: 89 f0 movl %esi, %eax 12: 5d popq %rbp 13: c3 retq 14: 66 2e 0f 1f 84 00 00 00 00 00 nopw %cs:(%rax,%rax) 1e: 66 90 nop_myFunc:; { 20: 55 pushq %rbp 21: 48 89 e5 movq %rsp, %rbp 24: 48 83 ec 10 subq $16, %rsp; c = addNum(123, 456); 28: bf 7b 00 00 00 movl $123, %edi 2d: be c8 01 00 00 movl $456, %esi 32: e8 00 00 00 00 callq 0 <_myFunc+0x17> 37: 89 45 fc movl %eax, -4(%rbp); } 3a: 48 83 c4 10 addq $16, %rsp 3e: 5d popq %rbp 3f: c3 retq Mac电脑,就以AT&T为例了 热身准备 操作码 操作数 功能 mov A, B 把B的值赋给A and A, B 把A的值与B的值相加,结果赋给A push A 把A的值存储在栈中 pop A 从栈中读取值,并将其赋给A call A 调用函数 ret 无 将返回到函数的调用源 字节序 大端法:最高有效位在最前面 小端法:最低有效位在最前面 内存地址是一致的,不同的是从高还是低位开始‘装入’ 下面举例,一个int(32位)类型变量,位于地址0x100处,十六进制值为0x01234567.地址范围0x100~0x·03的字节顺序依赖于机器的类型: 0x100 0x101 0x102 0x103 大端法 01 23 45 67 小端法 67 45 23 01 通常 Intel机使用小端模式 ARM 可以按照大端法或小端法,一旦选定了也就固定下来了。我们熟知的移动操作系统Android和iOS运行小端模式 程序运行时,会在内存上申请栈空间。存储顺序为LIFO。对栈进行读写的内存地址是由esp进行管理的。push和pop指令运行后,esp的值会自动进行更新 Hopperida64参考资料x86 assembly language objdump-wiki linux-objdump Linux命令大全 https://wiki.cdot.senecacollege.ca/wiki/X86_64_Register_and_Instruction_Quick_Start","categories":[],"tags":[{"name":"二进制, 'reverse engineering'","slug":"二进制-reverse-engineering","permalink":"wanggang.store/tags/二进制-reverse-engineering/"}]},{"title":"小程序","slug":"小程序","date":"2020-03-13T16:19:49.000Z","updated":"2020-03-23T08:45:27.900Z","comments":true,"path":"2020/03/14/小程序/","link":"","permalink":"wanggang.store/2020/03/14/小程序/","excerpt":"","text":"微信小程序IDE 一些快速创建代码段快捷方式 小程序IDE中右键创建文件会自动创建模板文件,如果是page会自动添到路由page里 但是亲测,变动文件会很麻烦甚至难以理解的问题。 所以建议,创建新增用小程序IDE比较快捷,一些模范相对准确,毕竟官方维护。 而如果我们阅读代码或是开发代码 可以使用VSCode,如果要调试则把小程序IDE保持在前台, 也就是小程序IDE与VSCode同时打开同一项目,注意的是有改动要及时保存,避免冲突。","categories":[],"tags":[{"name":"小程序","slug":"小程序","permalink":"wanggang.store/tags/小程序/"}]},{"title":"链接","slug":"链接","date":"2020-03-13T16:19:26.000Z","updated":"2020-03-31T13:30:50.990Z","comments":true,"path":"2020/03/14/链接/","link":"","permalink":"wanggang.store/2020/03/14/链接/","excerpt":"","text":"链接器报告说它无法解析一个引用,这是什么意思 静态变量和全局变量的区别是什么 如果你在不同的C文件中定义了名字相同的两个全局变量会发生什么 静态库和动态库的区别是什么 我们在命令上排列库的顺序有什么影响 为什么有些链接错误直到运行时才会出现","categories":[],"tags":[{"name":"计算机系统","slug":"计算机系统","permalink":"wanggang.store/tags/计算机系统/"}]},{"title":"HTML+CSS+JS","slug":"HTML-CSS-JS","date":"2020-03-13T16:05:26.000Z","updated":"2020-03-21T08:45:44.230Z","comments":true,"path":"2020/03/14/HTML-CSS-JS/","link":"","permalink":"wanggang.store/2020/03/14/HTML-CSS-JS/","excerpt":"","text":"","categories":[],"tags":[{"name":"HTML","slug":"HTML","permalink":"wanggang.store/tags/HTML/"}]},{"title":"flutter","slug":"flutter","date":"2020-03-13T16:03:41.000Z","updated":"2020-06-08T05:20:47.630Z","comments":true,"path":"2020/03/14/flutter/","link":"","permalink":"wanggang.store/2020/03/14/flutter/","excerpt":"","text":"英文 flutter 官网 https://flutter.cn/ 内容丰富 flutter 中文网 参考资料flutter 官网 flutter 中文网","categories":[],"tags":[{"name":"hybrid, flutter","slug":"hybrid-flutter","permalink":"wanggang.store/tags/hybrid-flutter/"}]},{"title":"Swift-Struct","slug":"Swift-Struct","date":"2020-03-13T14:27:50.000Z","updated":"2020-03-13T15:21:12.620Z","comments":true,"path":"2020/03/13/Swift-Struct/","link":"","permalink":"wanggang.store/2020/03/13/Swift-Struct/","excerpt":"","text":"vectorunion参考资料Working with Vectors Using Imported C Structs and Unions in Swift","categories":[],"tags":[{"name":"swift","slug":"swift","permalink":"wanggang.store/tags/swift/"}]},{"title":" 网络篇","slug":"网络篇","date":"2020-03-09T07:38:51.000Z","updated":"2020-05-08T02:58:42.950Z","comments":true,"path":"2020/03/09/网络篇/","link":"","permalink":"wanggang.store/2020/03/09/网络篇/","excerpt":"Q: 两台服务器A和B的网络配置, A B IP address 192.168.26.129 192.168.26.3 Subnet mask 255.255.255.0 255.255.255.224 Default gateway 192.168.26.2 192.168.26.2 子网掩码本都为255.255.255.0, B的子网掩码不小心配成了255.255.255.224。它们还能正常通信吗? 如果看到这个问题能自信回答。下文无须再看。","text":"Q: 两台服务器A和B的网络配置, A B IP address 192.168.26.129 192.168.26.3 Subnet mask 255.255.255.0 255.255.255.224 Default gateway 192.168.26.2 192.168.26.2 子网掩码本都为255.255.255.0, B的子网掩码不小心配成了255.255.255.224。它们还能正常通信吗? 如果看到这个问题能自信回答。下文无须再看。 名词理解IPip地址 不要被地址前的ip吓到,ip地址也是地址,位置的标识。 先来感受一下 IPv4地址 192.168.18.4 字面上分析:由3个点分为4组,可以是16进制也可以是10进制,32位 IPv6地址 2020:8210:242a:f270:1ca2:3f7:6ff:18af 字面上分析:由7个冒号分为8块也叫字段,16进制表示,128位 在IPv6中地址长度是128位,是IPv4的四倍。 1.在一个块中前导的零不必书写 2.全零的块可以省略,用符号::代替,为了避免歧义,一个IPv6地址中::只能用一次。 在某些情况下(例如表示一个包含地址的URL时),IPv6地址中的冒号分隔符可能与其他分隔符混淆,例如IP地址和端口号之间使用的冒号。这时候用[和]包围IPv6地址。例如,URL http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]:443/ 是指IPv6主机2001:0db8:85a3:08d3:1319:8a2e:0370:7344中的端口号443使用HTTP 、TCP和IPv6协议 增加的这些灵活性随之而来的就是会造成不必要的混淆性,同一个IP地址有多种的表示方式。 为了弥补这种情况,还要兼容特性,制定了规则,保证IP地址表示的唯一性 前导的零必须压缩。 符号::只能用于影响最大的地方(压缩最多的零),如果多少块中包含等长度的零,顺序靠前的块被替换为::。 a到f的十六进制数字应该用小写表示。 互联网上的每个接口必须有一个唯一的 I n t e r n e t 地址,也称作 I P 地址 IP地址的历史演变最初定义 Internet地址结构 = 网络号 + 主机号 网络号: 用于识别接口使用的IP地址在哪个网络中可被发现; 主机号:用于识别由网络部分给出的网络中的特定主机; 当时,大多数主机只有一个网络接口,因此术语接口地址和主机地址有时交替使用。 分类寻址 column 避免歧义,论证我的观点 IP地址分为A、B、C、D、E五类这是没有争议的。 但是我对E的前缀码有所怀疑 下表来自Wikipedia https://zh.wikipedia.org/wiki/IP%E5%9C%B0%E5%9D%80 A类IPv4地址 B类IPv4地址 C类IPv4地址 D类IPv4地址 E类IPv4地址 网络标志位 0 10 110 1110 11110 IP地址范围 1.0.0.0~127.255.255.255 128.0.0.0~191.255.255.255 192.0.0.0~223.255.255.255 224.0.0.0~239.255.255.255 240.0.0.0~247.255.255.255 可用IP地址范围 1.0.0.1~127.255.255.254 128.0.0.1~191.255.255.254 192.0.0.1~223.255.255.254 是否可以分配给主机使用 是 是 是 否 否 网络数量(个) 126 (27-2) 16384 (214) 2097152 (221) — — 每个网络中可容纳主机数(个) 16777214 (224-2) 65534 (216-2) 254 (28-2) — — 适用范围 大量主机的大型网络 中等规模主机数的网络 小型局域网 留给Internet体系结构委员会(IAB)使用【组播地址】 保留,仅作为搜索、Internet的实验和开发用 下图信息来自《TCP/IP详解 卷1:协议》译者: 范建华等 以上两处信息表达一致。但是我得疑问是如果E的前缀码是11110 那么剩下的那个区间的地址算什么 11111000 00000000 00000000 00000000 ~ 11111111 11111111 11111111 11111111 248.0.0.0 ~255.255.255.255 又查阅了《TCP/IP详解 卷1:协议》第二版,吴英等译 吴功宜 审校 E类的 地址范围 240.0.0.0 ~ 255.255.255.255 高位序 1111 我还是倾向于后者,所以之后的理论基于此,尽管这无关紧要。 现实中的不同网络可能有不同数量的主机,每台主机都需要一个唯一IP地址。 地址空间的划分涉及五大类,每类都基于网络中可容纳的主机数量,确定在一个32位的IPv地址中分配给网络号和主机号的位数。 由上图的信息可以算得下表数据 类 地址范围 高序位 用途 百分比 网络数 主机数 A 0.0.0.0~127.255.255.255 0 单播/特殊 1/2 128 (2^7) 16777216 (2^24) B 128.0.0.0~191.255.255.255 10 单播/特殊 1/4 16384 (2^14) 65536 (2^16) C 192.0.0.0~223.255.255.255 110 单播/特殊 1/8 2097152 (2^21) 256 (2^8) D 224.0.0.0~239.255.255.255 1110 组播 1/16 N/A N/A E 240.0.0.0~255.255.255.255 1111 保留 1/16 N/A N/A 百分比的计算可以 画棵哈夫曼树, 比如 0为左子树1为右子树 各类的网络数呈几何倍的递增,而网络数却呈几何倍递减,这样的分类有些脱离现实过于数字化了。 A类和B类网络号通常浪费太多主机号,而C类网络号则不能为很多站点提供足够的主机号。 比如C类网络号主机过少,而B类网络号主机又过多,如果想选择一个千为单位的网络号则十分尴尬了。 子网寻址背负历史包袱,负重前行 为了在不改变Internet核心路由基础设施的情况下解决这个问题,人们很自然想到一种方式,在一个站点接入Internet后为其分配一个网络号,然后又站点管理员进一步划分本地的子网数。 实现这个想法需要改变IP地址的网路部分和主机部分的限制,但这样做只是针对一个站点自身而言;Internet其余部分将只能’看到‘传统的A类、B类和C类部分。支持此功能的方法称为子网寻址。 解释一下,一个站点可能申请到了B类网络号,也就是IP地址的前16位确定了,余下16位自由位, 如果按照传统思维(分类寻址)那么剩下的16位就是主机号, 而按照子网寻址的逻辑,这16位对于Internet其他部分看来依然是主机号,对于站点自身来看,可通过子网掩码自由再划分成网络号与主机号。如下图一种可能的格式 这个例子中为一个B类地址被划分子网。它使用8位作为子网ID,提供256个子网和每个子网中254台主机(当前每个子网的第一个和最后一个地址无效,即从整个分配范围中去除第一个和最后一个地址)。这种划分可由网络管理员改变 前16位有权威机构分配。该站点的每个地址前16位固定不变,后16位由管理员分配。 注意只有划分子网的网络中的主机和路由器知道子网结构,在进行子网寻址前,Internet其他部分仍将它作为站点相关地址来看待。 下图展示如何工作 某个站点被分配一个典型的B类网络号128.32 。 网络管理员决定用于站点范围内的子网掩码为255.255.255.0,提供256个子网,每个子网可容纳256-2 = 254台主机。同一子网中每台主机的IPv4地址拥有相同子网号。左侧的局域网段中主机的IPv4地址开始于128.32.1,右侧的所有主机开始于128.32.2 。 Internet中其他站点要访问这个站点目的地址以128.32开始的所有流量直接由Internet路由系统交给边界路由器,如137.64.23.30要访问128.32.1.1 则先访问到站点边界路由器,边界路由器区分128.32网络中的不同子网。比如图中,它则要区分目的地址为128.32.1.x和目的地址为128.32.2.x的流量。这些地址分别表示子网号1和2,它们都采用128.32的B类网络号。那么路由器如何在地址中找到子网ID,这就需要子网掩码了。 子网掩码子网掩码是由一台主机或路由器使用的分配位,以确定如何从一台主机对应IP地址中获得网络和子网信息。IP子网掩码与对应的IP地址长度相同(IPv4为32位,IPv6为128位)。它们通常在一台主机或路由器中以IP地址相同的方式配置,既可以是静态的(通常是路由器),也可以是动态的(例如动态主机配置协议DHCP)。 子网掩码表示网络号与主机号的边界,全是1的那边表示网络号全是0的表示主机号 如IPv4地址为128.32.1.14/255.255.255.0 地址(128.32.1.14) 10000000 00100000 00000001 00001110 掩码(255.255.255.0) 11111111 11111111 11111111 00000000 按位与结果 (128.32.1.0) 10000000 00100000 00000001 00000000 (网络号和地址)按位异或结果 00000000 00000000 00000000 00001110 网络号为 128.32.1.0 子网主机ID为 14 可变长度子网掩码VLSM同一站点的不同部分,可将不同长度的子网掩码应用于相同网络号 回顾前文的问题 Q: 两台服务器A和B的网络配置, A B IP address 192.168.26.129 192.168.26.3 Subnet mask 255.255.255.0 255.255.255.224 Default gateway 192.168.26.2 192.168.26.2 子网掩码本都为255.255.255.0, B的子网掩码不小心配成了255.255.255.224。它们还能正常通信吗? A的地址 192.169.26.129/24 如果没有配置错则A与B在同一个子网内192.168.26.0 而现在B地址为 192.168.26.3/27 已经了解历史VLSM的原理 ,我们可以简单的绘制网络拓扑 A与B掩码不同处于不同层次的子网,但是依然处于192.168.26.x的结点下。通信是一定可以的,只是方式有所转变。 当讲解完Wireshark后,后再抓包分析这个流程 CIDR和聚合20世纪90年代初,采用子网寻址缓解增长带来的痛苦后,Internet开始面临更严重的规模问题。有三个问题尤为重要。 到1994年,一半以上的B类地址已被分配。预计B类地址空间大约在1995年将被用尽。 32位的IPv4地址被认为不足以应付Internet在21世纪的初期规模。 全球性路由表的条目数(每个网络数对应一条),1995年大约为65000个条目,目前仍在增长。伴随着越来越多的ABC类路由条目出现,路由性能受到影响。 前缀前文讲到的子网寻址方式base于分类寻址之上的。虽然对站点内的情况有所改善,但是对于整体Internet依然面临着严峻的问题(如为新站点选择网络号) 为了缓解IPv4的地址的压力,无类别域间路由CIDR应运而生。 CIDR无类别域间路由顾名思义,CIDR意在消除类别,即消除IP地址中网络和主机号的预定义分隔,将更细粒度的IP地址分配范围成为可能。 使用CIDR,未经过预定义的任何地址范围可作为一个类的一部分,但需要一个类似于子网掩码的掩码,有时也称为CIDR掩码。使用CIDR掩码不再局限于一个站点,而对全球路由系统都是可见的。因此,除了网络号之外,核心Internet路由器必须能解释和处理掩码。这个数字组合称为网络前缀,它用于IPv4和IPv6地址管理。 暂且理解为 CIDR 为 子网掩码的Internet版。 聚合通过取消分类结构的IP地址,能分配各种尺寸的IP地址块。但是,这样做没有解决问题3. 它并未有助于减少路由条目数。 路由条目:路由器发送流量的指向 从本质上来说,路由器检查每个到达的数据报中的目的IP地址,找到一条匹配的路由表条目,并从该条目中提取数据报的”下一跳“。 分层路由20世纪70年代末,由Kleinrock和Kamoun发表的分层路由研究 如果将网络拓扑排列为一棵树,并且以对网络拓扑敏感的方式来分配地址,可获得一个非常小的路由表,同时保持到所有目的地最短路径,下图举个例子 左侧a)图中的树根是标记为19.12.4.8的路由器。为了知道每个可能的目的地的下一跳,它需要知道一棵树中在其下面的所有路由的条目,所以其存有的路由条目共9条。 右侧b)图中的树根是标记为19.0.0.1,并要求其路由表中只有3个条目(二叉树)左子树中所有路由器(节点)以前缀19.1开始,右侧的所有路由器以19.2开始。因此。路由器19.0.0.1的表中只需将19.1开始的目的地显示下一跳为19.1.0.1而将以19.2开始的目的地下一跳显示为19.2.0.1 。 任何其他的目的地址都被路由到标有”网络其他部分“的云中。结果共有3个条目(父结点,子结点) 路由聚合通过将相邻的多个IP前缀合并成一个短前缀称为一个聚合或汇聚 前两个(190.154.27.0/26与190.154.27.64/26)数值相邻,因此可被聚合。前缀190.154.27.192/26不能被聚合,由于它们并非数值相邻。 这样原来的三个前缀和两个新增的前缀可聚合成一个前缀。 特殊用途地址IPv4和IPv6地址空间中都包括几个地址范围,它们被用于特殊用途,因此不能用于单播地址分配。 这些特殊地址不详谈了。 还有一些单播地址被用于构建专用网络,来自这些范围的地址可用于一个站点或组织内部的主机和路由器之间的通信,但不能用于Internet中。因此这些地址有时也被称为不可路由的地址。 专用、不可路由的地址空间管理完全由本地决定。IPv4专用地址在家庭网络、中等规模、大型企业内部网络中很常见。它们经常与网络地址转换(NAT)结合使用,在IP数据报进入Internet时修改其中的IP地址。 这些构建内网的IP常称为私有IP IPv4前缀 10/8 172.16/12和192.168/16, IPv6前缀 fc00::/7 Private IPv4 addresses The Internet Engineering Task Force (IETF) has directed the Internet Assigned Numbers Authority (IANA) to reserve the following IPv4 address ranges for private networks RFC1918 name IP address range Number of addresses Largest CIDR block (subnet mask) Host ID size Mask bits Classful description[Note 1] 24-bit block 10.0.0.0 – 10.255.255.255 16777216 10.0.0.0/8 (255.0.0.0) 24 bits 8 bits single class A network 20-bit block 172.16.0.0 – 172.31.255.255 1048576 172.16.0.0/12 (255.240.0.0) 20 bits 12 bits 16 contiguous class B networks 16-bit block 192.168.0.0 – 192.168.255.255 65536 192.168.0.0/16 (255.255.0.0) 16 bits 16 bits 256 contiguous class C networks 简化后如下图 大致感受一下中小企业的网络拓扑 IP地址结构 大多数IPv4地址块最终被细分为一个地址用于识别连接Internet或者某些专用的内联网的计算机网络接口,这些地址称为单播地址。除了单播地址,其他类型的地址包括广播、组播和任播地址,它们可能涉及多个接口,还有一些特殊用途的地址。而我们主要围绕讨论的是单播地址 广播地址 ,如果发送这个地址,所有10.11.12 网络里面的设备都可以收到 网络号, 子网掩码和IP按位与就可得到网络号 IP地址的主机号 全0:表示整个子网 全1:表示向子网上所有设备发送包,即“广播” IP怎么来的如何配置IP地址? 往往我们上网不会为了IP而担忧,打开wifi,IP就自动设置好了。 其实就是使用默认的DHCP ##### 第一个协议 DHCP(Dynamic Host Configuration Protocol) 动态主机配置协议, 一个自动配置的协议有了这个协议,网络管理员就轻松多了。他只需要配置一段共享的 IP 地址。每一台新接入的机器都通过 DHCP 协议,来这个共享的 IP 地址里申请,然后自动配置好就可以了。等人走了,或者用完了,还回去,这样其他的机器也能用 DHCP 的方式就相当于租房。你不用装修,都是帮你配置好的。你暂时用一下,用完退租就可以了 #####解析 DHCP 的工作方式 1.DHCP Discover 新来的机器使用 IP 地址 0.0.0.0 发送了一个广播包,目的 IP 地址为 255.255.255.255。广播包封装了 UDP,UDP 封装了 BOOTP。其实 DHCP 是 BOOTP 的增强版 Boot request 我的 MAC 地址是这个,我还没有 IP,谁能给租给我个 IP 地址! DHCP Offer ip DHCP Request ip ACK Socket库提供查询IP地址的功能 询问 DNS 服务器 获取IP ,端口号 Socket 存有 描述符,IP,端口号 通过“描述符”来识别socket 还没有MAC地址 ARP 通过IP找MAC 同网段广播 套接字的实体就是通信控制信息 协议栈是根据套接字中记录的控制信息来工作的。 连接是什么意思 连接实际上是通信双方交换控制信息 收据收发 数据小 首先,协议栈并不关心应用程序传来的数据是什么内容。应用程序在 调用 write 时会指定发送数据的长度,在协议栈看来,要发送的数据就是 一定长度的二进制字节序列而已。 其次,协议栈并不是一收到数据就马上发送出去,而是会将数据存放在内部的发送缓冲区中,并等待应用程序的下一段数据。这样做是有道理的。应用程序交给协议栈发送的数据长度是由应用程序本身来决定的,不同的应用程序在实现上有所不同,有些程序会一次性传递所有的数据,有些程序则会逐字节或者逐行传递数据。总之,一次将多少数据交给协议栈是由应用程序自行决定的,协议栈并不能控制这一行为。在这样的情况下,如果一收到数据就马上发送出去,就可能会发送大量的小包,导致网络效率下降,因此需要在数据积累到一定量时再发送出去。至于要积累多少数据才能发送,不同种类和版本的操作系统会有所不同,不能一概而论,但都是根据下面几个要素来判断的。 第一个判断要素是每个网络包能容纳的数据长度,协议栈会根据一个 叫作MTU的参数来进行判断。MTU 表示一个网络包的最大长度,在以太网中一般是 1500 字节。MTU 是包含头部的总长度,因此需要从MTU 减去头部的长度,然后得到的长度就是一个网络包中所能容纳的最大数据长度,这一长度叫作 MSS。当从应用程序收到的数据长度超过或者接近 MSS 时再发送出去,就可以避免发送大量小包的问题了。 另一个判断要素是时间。当应用程序发送数据的频率不高的时候,如果每次都等到长度接近 MSS 时再发送,可能会因为等待时间太长而造成发送延迟,这种情况下,即便缓冲区中的数据长度没有达到 MSS,也应该果 断发送出去。为此,协议栈的内部有一个计时器,当经过一定时间之后,就会把网络包发送出去 A。 判断要素就是这两个,但它们其实是互相矛盾的。如果长度优先,那么网络的效率会提高,但可能会因为等待填满缓冲区而产生延迟;相反地,如果时间优先,那么延迟时间会变少,但又会降低网络的效率。因此,在进行发送操作时需要综合考虑这两个要素以达到平衡。不过,TCP 协议规 格中并没有告诉我们怎样才能平衡,因此实际如何判断是由协议栈的开发者来决定的,也正是由于这个原因,不同种类和版本的操作系统在相关操 作上也就存在差异。 正如前面所说,如果仅靠协议栈来判断发送的时机可能会带来一些问 题,因此协议栈也给应用程序保留了控制发送时机的余地。应用程序在发送数据时可以指定一些选项,比如如果指定“不等待填满缓冲区直接发送”,则协议栈就会按照要求直接发送数据。像浏览器这种会话型的应用程 序在向服务器发送数据时,等待填满缓冲区导致延迟会产生很大影响,因 此一般会使用直接发送的选项。 MTU:Maximum Transmission Unit:一个网络包的最大长度,以太网中一般为1500字节。 MSS:Maximum Segment Size:除去头部之后,一个网络包所能容纳的TCP数据的最大长度。 最大分段大小。 TCP 和 IP 的头部加起来一般是 40 字节,因此 MTU 减去这个长度就是 MSS。例如,在以太网中, MTU 为 1500,因此 MSS 就是 1460。TCP/IP 可以使用一些可选参数 (protocol option),如加密等,这时头部的长度会增加,那么 MSS 就会随着头部长度增加而相应缩短。 数据大 滑动窗口 所谓滑动窗口,就是在发送一 个包之后,不等待 ACK 号返回,而是直接发送后续的一系列包。 TCP整体流程 UDP 作为iOS出身,深受那一代人影响。所以第一个抓包工具使用的是Charles 俗称青花瓷,有免费版本,不过会定时的弹框。受限于中间人抓包法,注定无法捕获全部的包。 抓包神器 Wireshark参考资料 https://en.wikipedia.org/wiki/Private_network","categories":[],"tags":[{"name":"Internet, http","slug":"Internet-http","permalink":"wanggang.store/tags/Internet-http/"}]},{"title":"Swift Packages","slug":"Swift-Packages","date":"2020-03-08T14:29:13.000Z","updated":"2020-05-30T12:14:48.320Z","comments":true,"path":"2020/03/08/Swift-Packages/","link":"","permalink":"wanggang.store/2020/03/08/Swift-Packages/","excerpt":"苹果官方的包管理工具","text":"苹果官方的包管理工具 https://swift.org/package-manager/","categories":[],"tags":[{"name":"swift","slug":"swift","permalink":"wanggang.store/tags/swift/"}]},{"title":"cocoapods","slug":"cocoapods","date":"2020-03-08T14:09:05.000Z","updated":"2020-03-11T16:12:22.740Z","comments":true,"path":"2020/03/08/cocoapods/","link":"","permalink":"wanggang.store/2020/03/08/cocoapods/","excerpt":"What is CocoaPodsCocoaPods manages library dependencies for your Xcode projects. The dependencies for your projects are specified in a single text file called a Podfile. CocoaPods will resolve dependencies between libraries, fetch the resulting source code, then link it together in an Xcode workspace to build your project. Ultimately the goal is to improve discoverability of, and engagement in, third party open-source libraries by creating a more centralised ecosystem.","text":"What is CocoaPodsCocoaPods manages library dependencies for your Xcode projects. The dependencies for your projects are specified in a single text file called a Podfile. CocoaPods will resolve dependencies between libraries, fetch the resulting source code, then link it together in an Xcode workspace to build your project. Ultimately the goal is to improve discoverability of, and engagement in, third party open-source libraries by creating a more centralised ecosystem. iOS 开发的依赖管理工具 cocoapods 的作用 Project GoalsCocoaPods aims to improve the engagement with, and discoverability of, third party open-source Cocoa libraries. These project goals influence and drive the design of CocoaPods: Create and share libraries, and use them in your own projects, without creating extra work for library authors. Integrate non-CocoaPods libraries and hack on your own fork of any CocoaPods library with a simple transparent Podspec standard. Allow library authors to structure their libraries however they like. Save time for library authors by automating a lot of Xcode work not related to their libraries’ functionality. Support any source management system. (Currently supported are git, svn, mercurial, bazaar, and various types of archives downloaded over HTTP.) Promote a culture of distributed collaboration on pods, but also provide features only possible with a centralised solution to foster a community. Build tools on top of the core Cocoa development system, including those typically deployed to other operating systems, such as web-services. Provide opinionated and automated integration, but make it completely optional. You may manually integrate your CocoaPods dependencies into your Xcode project as you see fit, with or without a workspace. Solve everyday problems for Cocoa and Xcode developers. 解决的痛点 自动集成,便于管理,解决了手动集成的弊端,如代码更新难以维护 依赖相互依赖递归依赖 各个库依赖的版本不同问题 查看细节 –verbose1234pod install --verbose# 不更新本地库 比如本地缓存有AFN2.0就不会自动更新到3.0# [-v | --verbose]pod install --verbose --no-repo-update Podfile.lock (记录install的文件,不只是pod install)这是 CocoaPods 创建的最重要的文件之一。它记录了需要被安装的 pod 的每个已安装的版本。如果你想知道已安装的 pod 是哪个版本,可以查看这个文件。推荐将 Podfile.lock 文件加入到版本控制中,这有助于整个团队的一致性。 第一次pod install会创建.lock文件,之后pod install(如果Podfile没改变)则不会改动.lock文件 而执行pod update 则会强行按照Podfile文件进行拉取依赖文件,并更新.lock文件。这样看来如果执行update指令还是有风险的 Manifest.lock (为了校验Pods里的文件与Podfile.lock记录是否一致)这是每次运行 pod install 命令时创建的 Podfile.lock 文件的副本。如果你遇见过这样的错误 沙盒文件与 Podfile.lock 文件不同步 (The sandbox is not in sync with the Podfile.lock),这是因为 Manifest.lock 文件和 Podfile.lock 文件不一致所引起。由于 Pods 所在的目录并不总在版本控制之下,这样可以保证开发者运行 app 之前都能更新他们的 pods,否则 app 可能会 crash,或者在一些不太明显的地方编译失败。 日常工作中是否应把pod相关文件加到版本控制中Pods文件夹不要加 Podfile 要加 Podfile.lock 要加到版本控制中 假设我们在 Podfile 中写上: pod 'AFNetWorking',那么默认是安装 AFNetworking 的最新代码。这就导致用户 A 可能装的是 3.0 版本,而用户 B 再安装就变成了 4.0 版本。即使我们在 Podfile 中指定了库的具体版本,那也不能保证不出问题。因为一个第三方库还有可能依赖其他的第三方库,而且不保证它的依赖关系是具体到版本号的。 因此 Podfile.lock 存在的意义是将某一次 pod install 时使用的各个库的版本,以及这个库依赖的其他第三方库的版本记录下来,以供别人使用。 这样一来,pod install 的流程其实是: 判断 Podfile.lock 是否存在,如果不存在,按照 Podfile 中指定的版本安装 如果 Podfile.lock 存在,检查 Podfile 中每一个 Pod 在 Podfile.lock 中是否存在 如果存在, 则忽略 Podfile 中的配置,使用 Podfile.lock 中的配置(实际上就是什么都不做) 如果不存在,则使用 Podfile 中的配置,并写入 Podfile.lock 中 而另一个常用命令 pod update 并不是一个日常更新命令。它的原理是忽略 Podfile.lock 文件,完全使用 Podfile 中的配置,并且更新 Podfile.lock。一旦决定使用 pod update,就必须所有团队成员一起更新。因此在使用 update 前请务必了解其背后发生的事情和对团队造成的影响,并且确保有必要这么做。 参考cocoapods objccn.io 上的期刊《深入理解CocoaPods》 张星宇的博客","categories":[],"tags":[{"name":"CocoaPods","slug":"CocoaPods","permalink":"wanggang.store/tags/CocoaPods/"}]},{"title":"terminal经验 ","slug":"terminal经验","date":"2020-03-07T05:59:05.000Z","updated":"2020-03-25T13:17:00.440Z","comments":true,"path":"2020/03/07/terminal经验/","link":"","permalink":"wanggang.store/2020/03/07/terminal经验/","excerpt":"操作文件根目录常见的两种 1234# Mac(硬盘)的根/# 用户的根~ 比如你当前处于 ~目录下。执行pwd,得到/Users/用户名 变更目录 (change directory)123456789# 跳转到上层路径 比如当前路径 ~/a/b -> ~/acd .. # 跳转到上两层cd ...# (用户)当前用户的根目录 比如我的电脑用户名alan, /Users/alancd ~cd --# 跳转到上一次所在的路径cd -","text":"操作文件根目录常见的两种 1234# Mac(硬盘)的根/# 用户的根~ 比如你当前处于 ~目录下。执行pwd,得到/Users/用户名 变更目录 (change directory)123456789# 跳转到上层路径 比如当前路径 ~/a/b -> ~/acd .. # 跳转到上两层cd ...# (用户)当前用户的根目录 比如我的电脑用户名alan, /Users/alancd ~cd --# 跳转到上一次所在的路径cd - 创建文件1234# 创建文件夹mkdir# 创建文件touch 查看文件123456789101112# 查看文件内容cat # 查看当前路径pwd# 当前文件列表ls# ls /bin# 显示隐藏文件ls -a # 详细信息,主要可以看文件权限ls -l 复制文件12# 将a复制到bcp a b 删除文件123456# 删除空文件夹rmdir # 删除文件 -r递归删除rm -r <filePath># 删除文件 -f强行删除rm -rf <filePath> 移动文件12# 移动文件 a -> bmv a b 打开文件12# 国际通用helpopen --help 12# 打开文件(默认程序打开)open <fileName> 12# 使用特定app打开特定文件open -a <appUrl> <fileName> 文件内查找123grep# 比如可以配合lsls | grep <filter> 当前正在运行的进程 123ps# 查看正在运行进程的可执行文件路径ps -e","categories":[],"tags":[{"name":"terminal","slug":"terminal","permalink":"wanggang.store/tags/terminal/"}]},{"title":"git经验","slug":"git经验","date":"2020-03-07T05:57:26.000Z","updated":"2020-03-14T14:59:46.790Z","comments":true,"path":"2020/03/07/git经验/","link":"","permalink":"wanggang.store/2020/03/07/git经验/","excerpt":"","text":"撤销git reset通过把分支记录回退几个提交记录来实现撤销改动。你可以将这想象成“改写历史”。向上移动分支,原来指向的提交记录就跟重来没有提交过一样。 git revert两者的区别reset意义 :回到指定节点(哈希值或者相对引用) 表象:清理结点,这个节点之后记录的统统不要了,真正做到回到过去。 revert意义:撤销指定结点的操作 表象:提交一新个记录,该次提交的作用为上一步的逆操作,工作树上表现为新增一个结点 总结得到的结果是一样的,但是在work tree 上表现不同, reset为不留痕迹 revert保留完整的操作记录 查看log以图表形式查看分支git log --graph 只显示第一行git log --pretty=short 只显示指定文件的日志git log <filename> 显示文件的改动git log -p git log -p <filename> 查看更改前后的差别git diff 分支git checkout -b <branchname> 提交 添加与提交一起完成 git commit -a -m","categories":[],"tags":[{"name":"git","slug":"git","permalink":"wanggang.store/tags/git/"}]},{"title":"Django Restful","slug":"Django-Restful","date":"2020-03-04T03:35:33.000Z","updated":"2020-04-09T15:20:46.180Z","comments":true,"path":"2020/03/04/Django-Restful/","link":"","permalink":"wanggang.store/2020/03/04/Django-Restful/","excerpt":"","text":"python 搭建rest API有两大主流 Django 与 Flask Django 较重 组件齐全 Flask 较轻,灵活 RESTRepresentational state transfer 表现层状态转换 Roy Thomas Fielding博士提出的一种网络服务架构的风格。也叫restful。 表现层状态转换是基于超文本传输协议之上的一族约束和属性,是一种设计网络服务的软件构建风格。 客户端发出请求以统一资源标识符访问和操作网络资源 当前在三种主流的Web服务实现方案中,因为REST模式与复杂的SOAP和XML-RPC相比更加简洁,越来越多的Web服务开始采用REST风格设计和实现。 Django rest 框架 django-rest-framework 官方文档 https://www.django-rest-framework.org/","categories":[],"tags":[]},{"title":"SQL","slug":"SQL","date":"2020-03-03T10:35:43.000Z","updated":"2020-03-03T14:25:37.660Z","comments":true,"path":"2020/03/03/SQL/","link":"","permalink":"wanggang.store/2020/03/03/SQL/","excerpt":"","text":"MYSQL安装 mysql下载:https://dev.mysql.com/downloads/ navicat下载:https://www.navicat.com.cn/ 出现个问题 .bash_profile 添加路径 但是在terminal上无效,还需要手动 设置PATH PATH=${PATH}:/usr/local/mysql/bin mysql -u root -p -u 代表user 这里用户名为root -p password 使用MYSQL的图形化程序navicat","categories":[],"tags":[{"name":"SQL","slug":"SQL","permalink":"wanggang.store/tags/SQL/"}]},{"title":"Django入门","slug":"Django入门","date":"2020-02-28T02:39:28.000Z","updated":"2020-03-11T16:12:39.800Z","comments":true,"path":"2020/02/28/Django入门/","link":"","permalink":"wanggang.store/2020/02/28/Django入门/","excerpt":"","text":"Django 初级篇虚拟环境 virtualenv1virtualenv venv -p python3 为project提供独立的python环境。pycharm默认开启的 开启 1source venv/bin/activate 开启后 命令行最左端有(venv)字样 代表虚拟环境开启 1(venv) ➜ PlanA 注意开启后 所有操作都在venv环境下进行的 使用 python 而不是python3,pip而不是pip3 关闭 1deactivate 安装 Djangoinstall django ```12345678就此环境搭建完毕##### 开始构建项目(Project)我的第一个项目`PlanA````django-admin startproject PlanA 12345678910PlanA/ <-- higher level folder├── PlanA <-- django project folder ( `startproject` 命令创建的)│ ├── PlanA│ │ ├── __init__.py│ │ ├── asgi.py│ │ ├── settings.py│ │ ├── urls.py│ │ └── wsgi.py│ └── manage.py└── venv <-- virtual environment folder 这些目录和文件的用处是:(django 文档给出) The outer mysite/ root directory is a container for your project. Its name doesn’t matter to Django; you can rename it to anything you like. manage.py: 一个让你用各种方式管理 Django 项目的命令行工具。你可以阅读 django-admin and manage.py 获取所有 manage.py 的细节。 里面一层的 mysite/ 目录包含你的项目,它是一个纯 Python 包。它的名字就是当你引用它内部任何东西时需要用到的 Python 包名。 (比如 mysite.urls). mysite/__init__.py:一个空文件,告诉 Python 这个目录应该被认为是一个 Python 包。如果你是 Python 初学者,阅读官方文档中的 更多关于包的知识。 mysite/settings.py:Django 项目的配置文件。如果你想知道这个文件是如何工作的,请查看 Django 配置 了解细节。 mysite/urls.py:Django 项目的 URL 声明,就像你网站的“目录”。阅读 URL调度器 文档来获取更多关于 URL 的内容。 mysite/asgi.py: An entry-point for ASGI-compatible web servers to serve your project. See How to deploy with ASGI for more details. mysite/wsgi.py:作为你的项目的运行在 WSGI 兼容的Web服务器上的入口。阅读 如何使用 WSGI 进行部署 了解更多细节。 一个project可以包含很多个app 同一个app可以存在与多个project中 创建appstartapp boards ```123456789101112注意这次我们用的是`startapp`.├── __init__.py├── admin.py├── apps.py├── migrations│ └── __init__.py├── models.py├── tests.py└── views.py PlanA/ <– higher level folder├── boards <– our new django app!│ ├── init.py│ ├── admin.py│ ├── apps.py│ ├── migrations│ │ └── init.py│ ├── models.py│ ├── tests.py│ └── views.py├── PlanA <– django project folder ( startproject 命令创建的)│ ├── PlanA│ │ ├── init.py│ │ ├── asgi.py│ │ ├── settings.py│ │ ├── urls.py│ │ └── wsgi.py│ └── manage.py└── venv <– virtual environment folder 123456##### 本地运行服务 8000端口``` python manage.py runserver 更换端口默认情况下,runserver 命令会将服务器设置为监听本机内部 IP 的 8000 端口。 如果你想更换服务器的监听端口,请使用命令行参数。举个例子,下面的命令会使服务器监听 8080 端口: 1$ python manage.py runserver 8080 如果你想要修改服务器监听的IP,在端口之前输入新的。比如,为了监听所有服务器的公开IP(这你运行 Vagrant 或想要向网络上的其它电脑展示你的成果时很有用),使用: 1$ python manage.py runserver 0:8000 0 是 0.0.0.0 的简写。完整的关于开发服务器的文档可以在 :djamdin:runserver 参考文档中找到。 新版django 的路由 path与re_path1234urlpatterns = [ path('polls/', include('polls.urls')), path('admin/', admin.site.urls),] 以前是url path() 参数: route¶route 是一个匹配 URL 的准则(类似正则表达式)。当 Django 响应一个请求时,它会从 urlpatterns 的第一项开始,按顺序依次匹配列表中的项,直到找到匹配的项。 这些准则不会匹配 GET 和 POST 参数或域名。例如,URLconf 在处理请求 https://www.example.com/myapp/ 时,它会尝试匹配 myapp/ 。处理请求 https://www.example.com/myapp/?page=3 时,也只会尝试匹配 myapp/。 path() 参数: view¶当 Django 找到了一个匹配的准则,就会调用这个特定的视图函数,并传入一个 HttpRequest 对象作为第一个参数,被“捕获”的参数以关键字参数的形式传入。稍后,我们会给出一个例子。 path() 参数: kwargs¶任意个关键字参数可以作为一个字典传递给目标视图函数。本教程中不会使用这一特性。 path() 参数: name¶为你的 URL 取名能使你在 Django 的任意地方唯一地引用它,尤其是在模板中。这个有用的特性允许你只改一个文件就能全局地修改某个 URL 模式。 当你了解了基本的请求和响应流程后,请阅读 教程的第 2 部分 开始使用数据库. 数据库迁移manage.py makemigrations```12```python manage.py migrate 资料https://tutorial.djangogirls.org/zh/ 一个gitbook,难度较低的教程,如果有基础不建议看 https://github.com/TwoWater/Python 国人写的教程,能够快速了解陌生的信息 https://docs.djangoproject.com/ 官方文档,更新较为即时,语言可选中文。 https://simpleisbetterthancomplex.com/series/beginners-guide/1.11/ 新人友善的博客,带你一步一步完成一个论坛类的project 17年写的有些东西需要更新。","categories":[],"tags":[{"name":"python, django","slug":"python-django","permalink":"wanggang.store/tags/python-django/"}]},{"title":"mac socket","slug":"mac-socket","date":"2019-12-27T08:14:01.000Z","updated":"2020-03-11T16:13:11.530Z","comments":true,"path":"2019/12/27/mac-socket/","link":"","permalink":"wanggang.store/2019/12/27/mac-socket/","excerpt":"### 套接字接口的起源 套接字接口是加州大学伯克利分校的研究员在20世纪80年代早期提出来的。因为这个原因,它也常被叫做伯克利套接字。伯克利的研究者使得套接字接口适用于任何底层的协议。第一个实现的就是针对TCP/IP协议的,他们把它包括在Unix 4.2BSD的内核里,并且分发给许多学校和实验室。这在因特网的历史上是一个重大事件。几乎一夜之间,成千上万的人们接触到了TCP/IP和它的源代码。它引起了巨大的轰动,并激发了新的网络和网络互联研究的浪潮 节选自 《深入理解计算机系统》第三版 伯克利 Berkeley Core Foundation通过CFNetwork提供了封装套接字的CFSocket和CFStream,此外还进一步提供了一些协议的封装,例如CFFTP、CFHTTP等 BSD套接字是XNU中所有网络组件的核心","text":"### 套接字接口的起源 套接字接口是加州大学伯克利分校的研究员在20世纪80年代早期提出来的。因为这个原因,它也常被叫做伯克利套接字。伯克利的研究者使得套接字接口适用于任何底层的协议。第一个实现的就是针对TCP/IP协议的,他们把它包括在Unix 4.2BSD的内核里,并且分发给许多学校和实验室。这在因特网的历史上是一个重大事件。几乎一夜之间,成千上万的人们接触到了TCP/IP和它的源代码。它引起了巨大的轰动,并激发了新的网络和网络互联研究的浪潮 节选自 《深入理解计算机系统》第三版 伯克利 Berkeley Core Foundation通过CFNetwork提供了封装套接字的CFSocket和CFStream,此外还进一步提供了一些协议的封装,例如CFFTP、CFHTTP等 BSD套接字是XNU中所有网络组件的核心 socket 函数客户端和服务器使用socket函数来创建一个套接字描述符 在<sys/socket.h>中定义int socket(int, int, int); 在bsd>kern>syscalls.master 中97 AUE_SOCKET ALL { int socket(int domain, int type, int protocol); } 若成功则为非负描述符,若出错则为-1 12345678910/** domain 套接字域(地址/协议族)通常指的是 AF(Address Family)或者PF(Protocol Family)表示第2层或第3层地址对应的套接字地址模式。常见的IP协议,即PF_INET或AF_INET传统上使用的是AF_*常量,PF_*常量只不过是通过AF_*常量#define得到的* type * protocol 所在文件bsd > netinet > in.h Xcode里面也能看见注释*/int socket(int domain, int type, int protocol) 1234567891011// socket.h 114~123/* * Types */#define SOCK_STREAM 1 /* stream socket */#define SOCK_DGRAM 2 /* datagram socket */#define SOCK_RAW 3 /* raw-protocol interface */#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)#define SOCK_RDM 4 /* reliably-delivered message */#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */#define SOCK_SEQPACKET 5 /* sequenced packet stream */ 123456789101112131415// socket.h 528~586/* * Address families. */#define AF_UNSPEC 0 /* unspecified */#define AF_UNIX 1 /* local to host (pipes) */#if !defined(_POSIX_C_SOURCE) || defined(_DARWIN_C_SOURCE)#define AF_LOCAL AF_UNIX /* backward compatibility */#endif /* (!_POSIX_C_SOURCE || _DARWIN_C_SOURCE) */#define AF_INET 2 /* internetwork: UDP, TCP, etc. */...#define AF_INET6 30 /* IPv6 */... 1234567891011// socket.h 632~680/* * Protocol families, same as address families for now. */#define PF_UNSPEC AF_UNSPEC#define PF_LOCAL AF_LOCAL#define PF_UNIX PF_LOCAL /* backward compatibility */#define PF_INET AF_INET...#define PF_INET6 AF_INET6...","categories":[],"tags":[{"name":"socket","slug":"socket","permalink":"wanggang.store/tags/socket/"}]},{"title":"Mac app socket server","slug":"Mac-app-socket-server","date":"2019-12-26T08:24:57.000Z","updated":"2020-03-21T08:42:57.160Z","comments":true,"path":"2019/12/26/Mac-app-socket-server/","link":"","permalink":"wanggang.store/2019/12/26/Mac-app-socket-server/","excerpt":"在macOS app 上创建socket server 发现创建失败 抛出异常代码如下 1try checkNotMinus1{ Darwin.bind(ipv4Socket, addrPtr, UInt32(MemoryLayout<sockaddr_in>.size)) 123456789/// 封装检测是否为-1/// errno 为Darwin的错误码enum SocketError: Error { case socketCreateFailed(Int32) }@discardableResultfunc checkNotMinus1(_ f: () -> Int32) throws -> Int32 { let r = f() guard r != -1 else { throw SocketError.socketCreateFailed(errno) } return r} catch中的error 为ssocketCreateFailed(1) 也就是errno为1","text":"在macOS app 上创建socket server 发现创建失败 抛出异常代码如下 1try checkNotMinus1{ Darwin.bind(ipv4Socket, addrPtr, UInt32(MemoryLayout<sockaddr_in>.size)) 123456789/// 封装检测是否为-1/// errno 为Darwin的错误码enum SocketError: Error { case socketCreateFailed(Int32) }@discardableResultfunc checkNotMinus1(_ f: () -> Int32) throws -> Int32 { let r = f() guard r != -1 else { throw SocketError.socketCreateFailed(errno) } return r} catch中的error 为ssocketCreateFailed(1) 也就是errno为1 ⌘左键 进入代码定义(Jump to Definition) 发现只有定义没有注释,能看到他是定义在Darwin中 打开Darwin-xnu 找到errno.h (<sys/errno.h>) 发现错误码为1 对应 Operation not permitted 我是使用Xcode的版本 Version 11.2.1 (11B500) 新版Xcode会把app 的能力单独一页列出来 当我勾选Network 的 Server选项后 bind成功了","categories":[],"tags":[{"name":"Mac OS","slug":"Mac-OS","permalink":"wanggang.store/tags/Mac-OS/"}]},{"title":"SF Symbols","slug":"SF-Symbols","date":"2019-12-26T05:06:06.000Z","updated":"2019-12-26T05:56:36.420Z","comments":true,"path":"2019/12/26/SF-Symbols/","link":"","permalink":"wanggang.store/2019/12/26/SF-Symbols/","excerpt":"SF Symbols Mac上的一款app 有超1500个符号可供使用,也可以自定义符号 可以导出文件,用sketch编辑","text":"SF Symbols Mac上的一款app 有超1500个符号可供使用,也可以自定义符号 可以导出文件,用sketch编辑 UIKit 提供了两种API,一种是使用系统符号,另外一种是自定义 init(systemName:) init(systemName:compatibleWith:) init(systemName:withConfiguration:)methods of UIImage. init(named:) init(named:in:compatibleWith:) init(named:in:with:) 1let image = UIImage(systemName: \"square.and.arrow.up\") image 会加载对应的符号 也可以自定义符号 Assets.xcassets / + / New Symbol Image Set / 拖拽编辑好的SVG文件到xcode中 效果如图 1let image = UIImage(systemName: \"arrowupfill\") #####link refer Creating Custom Symbol Images for Your App","categories":[],"tags":[{"name":"iOS","slug":"iOS","permalink":"wanggang.store/tags/iOS/"}]},{"title":"iOS URLSession Request","slug":"iOS-URLSession-Request","date":"2019-11-27T15:14:13.000Z","updated":"2019-11-27T16:22:22.730Z","comments":true,"path":"2019/11/27/iOS-URLSession-Request/","link":"","permalink":"wanggang.store/2019/11/27/iOS-URLSession-Request/","excerpt":"概览","text":"概览 参考资料 Foundation","categories":[],"tags":[]},{"title":"Hello World","slug":"hello-world","date":"2019-05-28T14:39:18.000Z","updated":"2019-05-28T14:39:18.000Z","comments":true,"path":"2019/05/28/hello-world/","link":"","permalink":"wanggang.store/2019/05/28/hello-world/","excerpt":"","text":"Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub. Quick StartCreate a new post1$ hexo new \"My New Post\" More info: Writing Run server1$ hexo server More info: Server Generate static files1$ hexo generate More info: Generating Deploy to remote sites1$ hexo deploy More info: Deployment","categories":[],"tags":[]}]}