iOS強(qiáng)化 : 符號(hào) Symbol

前言 :
  • 之前接觸過(guò)Bugly幕屹,在Bugly文檔中心 這里接觸了一些iOS符號(hào)表的內(nèi)容浅碾,符號(hào)(Symbol)是日常開(kāi)發(fā)中經(jīng)常接觸的一個(gè)概念粤咪,雖然日常開(kāi)發(fā)中直接應(yīng)用的場(chǎng)景比較少才漆,但符號(hào)編譯期和運(yùn)行時(shí)都扮演了重要的角色。今天就來(lái)好好學(xué)習(xí)下符號(hào) Symbol,并為后面學(xué)習(xí)動(dòng)態(tài)庫(kù)靜態(tài)庫(kù)學(xué)習(xí)做準(zhǔn)備。

  • 參考:帥駝駝

目錄 :
    1. 符號(hào) Symbol的定義
    1. 符號(hào)表的種類
    1. 符號(hào)的實(shí)際探究
    1. 符號(hào)的擴(kuò)展
  • 5 .符號(hào)的剝離Strip命令

一双揪、 符號(hào) Symbol的定義

《深入理解計(jì)算機(jī)系統(tǒng)》一書中有一段Linux編譯系統(tǒng)采用的方法:

在編譯時(shí),編譯器向匯編器輸出每個(gè)全局符號(hào)包帚,或者是強(qiáng)(strong)或者是弱(weak)渔期,而匯編器把這個(gè)信息隱含地編碼在可重定位目標(biāo)文件的符號(hào)表里。函數(shù)和已初始化的全局變量是強(qiáng)符號(hào)渴邦,未初始化的全局變量是弱符號(hào)疯趟。

  • 符號(hào) Symbol

符號(hào)是一個(gè)數(shù)據(jù)結(jié)構(gòu),包含了名稱(String)和類型等元數(shù)據(jù)谋梭,符號(hào)對(duì)應(yīng)一個(gè)函數(shù)或者數(shù)據(jù)的地址信峻。

符號(hào)直接影響api的體積

  • 符號(hào)表Symbol Table

什么是符號(hào)表?

符號(hào)表是內(nèi)存地址與函數(shù)名章蚣、文件名站欺、行號(hào)的映射表姨夹。符號(hào)表元素如下所示:
<起始地址> <結(jié)束地址> <函數(shù)> [<文件名:行號(hào)>]

  • Symbol Table:符號(hào)表存儲(chǔ)了當(dāng)前文件的符號(hào)信息纤垂,靜態(tài)鏈接器(ld)和動(dòng)態(tài)鏈接器(dyld)在鏈接的過(guò)程中都會(huì)讀取符號(hào)表,另外調(diào)試器也會(huì)用符號(hào)表來(lái)把符號(hào)映射到源文件, 就是?來(lái)保存符號(hào)磷账。

  • String Table:就是?來(lái)保存符號(hào)的名稱峭沦。

  • Indirect Symbol Table間接符號(hào)表。保存使?的外部符號(hào)逃糟。更準(zhǔn)確?點(diǎn)就是使?的外部動(dòng)態(tài)庫(kù)的符號(hào)吼鱼。是Symbol Table的?集。

符號(hào)表中存儲(chǔ)符號(hào)的數(shù)據(jù)結(jié)構(gòu)如下:

struct nlist_64 {  
   union {       
      uint32_t  n_strx; /* index into the string table */   
    } n_un;  
   uint8_t n_type;        /* type flag, see below */
   uint8_t n_sect;        /* p number or NO_SECT */    
   uint16_t n_desc;       /* see <mach-o/stab.h> */   
   uint64_t n_value;      /* value of this symbol (or stab offset) */
};

符號(hào)表 在Mach-O中的位置

通過(guò)兩個(gè)Load Commands绰咽,描述Symbol Table的大小和位置菇肃,以及其他元數(shù)據(jù)

  • LC_SYMTAB :用來(lái)描述該文件的符號(hào)表。不論是靜態(tài)鏈接器還是動(dòng)態(tài)鏈接器在鏈接此文件時(shí)取募,都要使用該Load Command琐谤。調(diào)試器也可以使用該Load Command找到調(diào)試信息

  • LC_DYSYMTAB :描述動(dòng)態(tài)鏈接器使用其他的Symbol Table信息,

定義LC_SYMTAB加載命令具體屬性。在/usr/include/mach-o/loader.h中定義:

struct symtab_command {
    // 共有屬性玩敏。指明當(dāng)前描述的加載命令斗忌,當(dāng)前被設(shè)置為L(zhǎng)C_SYMTAB
    uint32_t cmd ;
    // 共有屬性质礼。指明加載命令的大小,當(dāng)前被設(shè)置為sizeof(symtab_command)
    uint32_t cmdsize;
    // 表示從文件開(kāi)始到symbol table所在位置的偏移量织阳。symbol table用[nlist]來(lái)表示
    uint32_t symoff;
    // 符號(hào)表內(nèi)符號(hào)的數(shù)量
    uint32_t nsyms;
    // 表示從文件開(kāi)始到string table所在位置的偏移量眶蕉。
    uint32_t stroff;
    // 表示string table大小(以byteカ單位)
    uint32_t strsize;
};

二、符號(hào)表的種類

2.1 分類

按照模塊區(qū)分:
  • 全局符號(hào)(Global Symbol) : 整個(gè)項(xiàng)目可見(jiàn)
  • 本地符號(hào)(Local Symbol) : 當(dāng)前類可見(jiàn)
按照位置劃分:
  • 外部符號(hào) : 符號(hào)不在當(dāng)前文件唧躲,需要ld或者dyld在鏈接的時(shí)候解決
  • 非外部符號(hào) : 即當(dāng)前文件內(nèi)的符號(hào)
按照功能分:
Type 說(shuō)明
f File
F Function
O Data
d Debug
ABS Absolute
COM Common
UND 未定義
按照符號(hào)種類劃分:
Type 說(shuō)明
U undefined(未定義)
A absolute(絕對(duì)符號(hào))
T text section symbol__TEXT.__text
D data section symbol__DATA.__data
B bss section symbol__DATA.__bss
C common symbol(只能出現(xiàn)在MH_OBJECT類型的Mach-O?件中)
- debugger symbol table
S 除了上?所述的造挽,存放在其他section的內(nèi)容,例如未初始化的全局變量存放在(__DATA,__common)中
I indirect symbol(符號(hào)信息相同弄痹,代表同?符號(hào))
u 動(dòng)態(tài)共享庫(kù)中的?寫u表示?個(gè)未定義引?對(duì)同?庫(kù)中另?個(gè)模塊中私有外部符號(hào)

注:標(biāo)記Type刽宪,?寫代表本地符號(hào)(local symbol

nm命令里的小寫字母對(duì)應(yīng)著本地符號(hào),大寫字母表示全局符號(hào)界酒;U表示undefined圣拄,即未定義的外部符號(hào)


三、符號(hào)的實(shí)際探究

3.1 可見(jiàn)性

有個(gè)很常見(jiàn)的case毁欣,就是你有1000個(gè)函數(shù)庇谆,但只有10個(gè)函數(shù)是公開(kāi)的,希望最后生成的動(dòng)態(tài)庫(kù)里不包含其他990個(gè)函數(shù)的符號(hào)凭疮,這時(shí)候就可以用clang的attribute來(lái)實(shí)現(xiàn):

//符號(hào)可被外部鏈接__attribute__((visibility("default")))//符號(hào)不會(huì)被放到Dynamic Symbol Table里饭耳,意味著不可以再被其他編譯單元鏈接__attribute__((visibility("hidden")))

clang來(lái)提供了一個(gè)全局的開(kāi)關(guān),用來(lái)設(shè)置符號(hào)的默認(rèn)可見(jiàn)性:

如果動(dòng)態(tài)庫(kù)的Target把這個(gè)開(kāi)關(guān)打開(kāi)执解,會(huì)發(fā)現(xiàn)動(dòng)態(tài)庫(kù)仍然能編譯通過(guò)寞肖,但是App會(huì)報(bào)一堆鏈接錯(cuò)誤,因?yàn)榉?hào)變成了hidden衰腌。

但這是一種常見(jiàn)的編譯方式:讓符號(hào)默認(rèn)是Hidden的新蟆,即-fvisibility=hidden,然后手動(dòng)為每個(gè)接口加上__attribute__((visibility("default")))右蕊。

//頭文件#define LH_EXPORT __attribute__((visibility("default")))LH_EXPORT void method_1(void);//實(shí)現(xiàn)文件LH_EXPORT void method_1(){    NSLog(@"1");}

全局符號(hào)本地符號(hào)琼稻,它們本質(zhì)上的區(qū)別就是可見(jiàn)性

一個(gè)符號(hào)的可見(jiàn)性有兩種:

  • default:默認(rèn)值,定義的符號(hào)類型是全局即為全局饶囚,是本地即為本地
  • hidden:將全局符號(hào)隱藏帕翻,變?yōu)楸镜胤?hào)

故此隱藏全局符號(hào)的方式有兩種:

  • 使用static關(guān)鍵字修飾
  • 使用attribute((visibility("hidden")))

舉例 :

案例1:

打開(kāi)項(xiàng)目,里面包含兩個(gè)Project

來(lái)到LGOneFramework萝风,打開(kāi)LGOneObject.m文件嘀掸,實(shí)現(xiàn)global_object函數(shù)

LGOneObject.h文件中,并沒(méi)有暴露global_object函數(shù)

來(lái)到LGApp(另一個(gè)Project)规惰,打開(kāi)ViewController.m文件睬塌,定義global_object函數(shù),但并不實(shí)現(xiàn)。在viewDidLoad方法中調(diào)用global_object函數(shù)

運(yùn)行項(xiàng)目衫仑,LGOneFramework中實(shí)現(xiàn)的global_object函數(shù)被正常調(diào)用

案例2:

如果將LGOneFramework中的global_object函數(shù)梨与,使用static關(guān)鍵字修飾

LGApp中,global_object函數(shù)的調(diào)用代碼不變

此時(shí)global_object函數(shù)變成本地符號(hào)文狱,僅對(duì)當(dāng)前文件可見(jiàn)粥鞋。所以在LGApp中調(diào)用該函數(shù),編譯報(bào)錯(cuò)瞄崇,并提示未定義符號(hào)

案例3:

LGOneFramework中呻粹,實(shí)現(xiàn)global_object函數(shù),不使用static關(guān)鍵字修飾

LGAppViewController.m中苏研,也實(shí)現(xiàn)global_object函數(shù)

運(yùn)行項(xiàng)目等浊,調(diào)用的是LGApp中的global_object函數(shù)

案例3中,為什么不會(huì)發(fā)生沖突摹蘑?
LGAppLGOneFramework兩個(gè)Project中筹燕,都定義了global_object函數(shù),對(duì)于global_object函數(shù)來(lái)說(shuō)衅鹿,它們其實(shí)存儲(chǔ)在兩個(gè)Mach-O中撒踪。由于編譯器有?級(jí)命名空間的概念,所以兩個(gè)global_object函數(shù)的符號(hào)其實(shí)是不一樣的

two_levelnamespace & flat_namespace
?級(jí)命名空間與?級(jí)命名空間大渤。鏈接器默認(rèn)采??級(jí)命名空間制妄,也就是除了會(huì)記錄符號(hào)名稱,還會(huì)記錄符號(hào)屬于哪個(gè)Mach-O的泵三,?如會(huì)記錄下來(lái)_NSLog來(lái)?Foundation

3.2 作用域

導(dǎo)入符號(hào)導(dǎo)出符號(hào)

  • 動(dòng)態(tài)庫(kù)因?yàn)椴恢劳饷媸侨绾问褂玫母蹋宰詈玫姆绞绞撬蓄^文件暴露出的符號(hào)全部導(dǎo)出來(lái)。從包大小的角度考慮烫幕,肯定是用到哪些符號(hào)俺抽,保留哪些符號(hào)對(duì)應(yīng)的代碼,ld提供了這樣一個(gè)方案纬霞,通過(guò)exported_symbol來(lái)只保留特定的符號(hào)

export symbol:導(dǎo)出符號(hào)意味著凌埂,告訴別的模塊,我有?個(gè)這樣的符號(hào)诗芜,你可以將其導(dǎo)?(Import)。

NSLog為例:

NSLog(@"%d", static_init_value);

NSLog存儲(chǔ)在Foundation庫(kù)中

  • 對(duì)于Foundation庫(kù)來(lái)說(shuō)埃疫,NSLog屬于供外部使用的導(dǎo)出符號(hào)
  • 對(duì)于當(dāng)前程序來(lái)說(shuō)伏恐,NSLog屬于從Foundation庫(kù)中導(dǎo)入的符號(hào)

導(dǎo)出符號(hào)就是全局符號(hào)

項(xiàng)目中定義的全局變量生成為全局符號(hào),默認(rèn)就會(huì)被導(dǎo)出栓霜,這些符號(hào)可以被外界查看并使用

使用objdump --macho --exports-trie ${MACH_PATH}命令查看導(dǎo)出符號(hào)

Exports trie:
0x100000000  __mh_execute_header
0x100003F50  _main
0x100008010  _global_init_value
0x100008020  _global_uninit_value

  • 只有上述四個(gè)導(dǎo)出符號(hào)翠桦,對(duì)應(yīng)符號(hào)表中的四個(gè)全局符號(hào)

動(dòng)態(tài)庫(kù)在運(yùn)行時(shí)才會(huì)加載,在編譯鏈接階段只提供符號(hào)即可。Mach-O中使用的動(dòng)態(tài)庫(kù)符號(hào)保存在間接符號(hào)表里

使用objdump --macho --indirect-symbols ${MACH_PATH}命令查看間接符號(hào)表

Indirect symbols for (__TEXT,__stubs) 1 entries
address            index name
0x0000000100003f90     8 _NSLog
Indirect symbols for (__DATA_CONST,__got) 1 entries
address            index name
0x0000000100004000    10 dyld_stub_binder
Indirect symbols for (__DATA,__la_symbol_ptr) 1 entries
address            index name
0x0000000100008000     8 _NSLog

  • 符號(hào)在Mach-O中占有一定體積销凑,剝離符號(hào)時(shí)丛晌,間接符號(hào)表是不能被刪除的
  • Mach-O所使用的動(dòng)態(tài)庫(kù)的全局符號(hào)都不能被刪除
  • 動(dòng)態(tài)庫(kù)剝離符號(hào),只能剝離非全局符號(hào)的所有符號(hào)

查看OC中的符號(hào)

打開(kāi)LGOneObject.m文件斗幼,寫入以下代碼:

#import "LGOneObject.h"

@interface LGOneObject : NSObject

- (void)testOneObject;

@end

@implementation LGOneObject

- (void)testOneObject {
    NSLog(@"testOneObject");
}

@end

使用objdump --macho --exports-trie ${MACH_PATH}命令查看導(dǎo)出符號(hào)

Exports trie:
0x100000000  __mh_execute_header
0x100003F20  _main
0x1000080B8  _OBJC_METACLASS_$_LGOneObject
0x1000080E0  _OBJC_CLASS_$_LGOneObject
0x100008110  _global_init_value
0x100008120  _global_uninit_value

OC默認(rèn)都是全局符號(hào)澎蛛,同時(shí)也是導(dǎo)出符號(hào)。它們可以被外界查看并使用蜕窿,會(huì)增加Mach-O的體積

開(kāi)發(fā)OC動(dòng)態(tài)庫(kù)時(shí)谋逻,想要減小Mach-O的體積,就要將外部無(wú)需使用的符號(hào)剝離桐经。此時(shí)可以借助鏈接器毁兆,將不想暴露的符號(hào)聲明為不導(dǎo)出符號(hào)

打開(kāi)xcconfig文件,添加OTHER_LDFLAGS配置項(xiàng)

OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_CLASS_$_LGOneObject
OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbol -Xlinker _OBJC_METACLASS_$_LGOneObject
  • _OBJC_CLASS_$_LGOneObject聲明為不導(dǎo)出符號(hào)
  • _OBJC_METACLASS_$_LGOneObject聲明為不導(dǎo)出符號(hào)

編譯項(xiàng)目阴挣,此時(shí)OC的兩個(gè)導(dǎo)出符號(hào)已經(jīng)被隱藏

Exports trie:
0x100000000  __mh_execute_header
0x100003F20  _main
0x100008110  _global_init_value
0x100008120  _global_uninit_value
  • 隱藏OC不想暴露的符號(hào)气堕,需要借助鏈接器,將符號(hào)聲明為不導(dǎo)出符號(hào)
  • 由于OC是運(yùn)行時(shí)語(yǔ)言畔咧,不能直接使用visibility("hidden")
  • 不導(dǎo)出符號(hào)送巡,將全局符號(hào)變?yōu)楸镜胤?hào),這些符號(hào)可以被剝離盒卸,從而減小Mach-O的體積
  • 隱藏不需要暴露的符號(hào)骗爆,從而避免被外界查看并使用,解決安全隱患

鏈接器提供的另一種方式:指定一個(gè)文件蔽介,將文件內(nèi)的符號(hào)全部聲明為不導(dǎo)出符號(hào)

創(chuàng)建symbol.txt文件摘投,放到工程目錄中,里面定義不想暴露的符號(hào)

_OBJC_CLASS_$_LGOneObject
_OBJC_METACLASS_$_LGOneObject
_global_init_value
_global_uninit_value

打開(kāi)xcconfig文件虹蓄,添加OTHER_LDFLAGS配置項(xiàng)

OTHER_LDFLAGS=$(inherited) -Xlinker -unexported_symbols_list >$(PROJECT_DIR)/symbol.txt

編譯項(xiàng)目犀呼,此時(shí)symbol.txt文件中定義的四個(gè)符號(hào)已經(jīng)被隱藏

Exports trie:
0x100000000  __mh_execute_header
0x100003F20  _main

3.3 Weak Symbol

弱定義符號(hào)

Weak Defintion Symbol:表示此符號(hào)為弱定義符號(hào)。如果靜態(tài)鏈接器或動(dòng)態(tài)鏈接器為此符號(hào)找到另?個(gè)(?弱)定義薇组,則弱定義將被忽略外臂。只能將合并部分中的符號(hào)標(biāo)記為弱定義

打開(kāi)WeakSymbol.m文件,寫入以下代碼:

#import "WeakSymbol.h"
#import <Foundation/Foundation.h>

void weak_function(void) {
   NSLog(@"weak_function");
}
  • 此時(shí)weak_function是一個(gè)全局符號(hào)律胀,同樣也是導(dǎo)出符號(hào)

打開(kāi)WeakSymbol.h文件宋光,寫入以下代碼:

void weak_function(void)  __attribute__((weak));
  • 使用__attribute__((weak))weak_function聲明為弱定義符號(hào)

使用objdump --macho --exports-trie ${MACH_PATH}命令查看導(dǎo)出符號(hào)

Exports trie:
0x100000000  __mh_execute_header
0x100003EF0  _weak_function [weak_def]
0x100003F30  _main
0x100008010  _global_init_value
0x100008020  _global_uninit_value
  • weak_function還是導(dǎo)出符號(hào),這也證明它依然是全局符號(hào)炭菌,其后增加了[weak_def]的標(biāo)記

弱定義符號(hào)的作用

WeakSymbol.mmain.m中罪佳,都實(shí)現(xiàn)一個(gè)weak_function函數(shù)

void weak_function(void) {
   NSLog(@"weak_function");
}

同一個(gè)Project中,出現(xiàn)兩個(gè)相同的全局符號(hào)黑低,此時(shí)編譯報(bào)錯(cuò)赘艳,提示出現(xiàn)重復(fù)符號(hào)

將其中一個(gè)weak_function函數(shù)聲明為弱定義符號(hào),此時(shí)編譯成功

void weak_function(void)  __attribute__((weak));
  • 弱定義符號(hào)的作用:可以解決同名符號(hào)的沖突;鏈接器按照符號(hào)上下順序蕾管,找到一處符號(hào)的實(shí)現(xiàn)后枷踏,其他地方的同名符號(hào)將被忽略

如果同時(shí)使用weakvisibility("hidden"),符號(hào)會(huì)變成一個(gè)弱定義的本地符號(hào)

打開(kāi)WeakSymbol.m文件掰曾,寫入以下代碼:

void weak_hidden_function(void) {
   NSLog(@"weak_hidden_function");
}

打開(kāi)WeakSymbol.h文件旭蠕,將weak_hidden_function函數(shù)同時(shí)使用weakvisibility("hidden")修飾

void weak_hidden_function(void) __attribute__((weak, visibility("hidden")));

使用objdump --macho --syms ${MACH_PATH}命令查看符號(hào)表

SYMBOL TABLE:
0000000100003f10 lw    F __TEXT,__text _weak_hidden_function
0000000100008008 l     O __DATA,__data __dyld_private
0000000100008014 l     O __DATA,__data _static_init_value
0000000100008018 l     O __DATA,__bss _static_uninit_value
0000000100008028 l     O __DATA,__common _default_x
0000000100000000 g     F __TEXT,__text __mh_execute_header
0000000100008010 g     O __DATA,__data _global_init_value
0000000100008020 g     O __DATA,__common _global_uninit_value
0000000100003f30 g     F __TEXT,__text _main
0000000100003ef0 gw    F __TEXT,__text _weak_function
0000000000000000         *UND* _NSLog
0000000000000000         *UND* ___CFConstantStringClassReference
0000000000000000         *UND* dyld_stub_binder

  • 此時(shí)_weak_hidden_function被標(biāo)記為lw,變?yōu)槿醵x本地符號(hào)
弱引用符號(hào)

Weak Reference Symbol:表示此未定義符號(hào)是弱引?婴梧。如果動(dòng)態(tài)鏈接器找不到該符號(hào)的定義下梢,則將其設(shè)置為0。鏈接器會(huì)將此符號(hào)設(shè)置弱鏈接標(biāo)志

打開(kāi)WeakImportSymbol.h文件塞蹭,寫入以下代碼:

void weak_import_function(void) __attribute__((weak_import));

  • 使用__attribute__((weak_import))weak_import_function聲明為若引用符號(hào)
  • 此時(shí)項(xiàng)目中沒(méi)有weak_import_function函數(shù)的實(shí)現(xiàn)

打開(kāi)main.m文件孽江,寫入以下代碼:

#import <Foundation/Foundation.h>
#import "WeakImportSymbol.h"

int main(int argc, char *argv[]) {
   if (weak_import_function) {
       weak_import_function();
   }
   return 0;
}

由于weak_import_function函數(shù)沒(méi)有實(shí)現(xiàn),但在main.m中被使用番电,此時(shí)編譯報(bào)錯(cuò)岗屏,提示未定義符號(hào)

  • 當(dāng)導(dǎo)入.h頭文件并使用符號(hào)時(shí),類似于API的使用漱办,只要找到符號(hào)的聲明即可这刷。即使函數(shù)沒(méi)有被實(shí)現(xiàn),也可以生成目標(biāo)文件娩井。但鏈接生成可執(zhí)行文件時(shí)暇屋,需要知道符號(hào)的具體位置,如果函數(shù)沒(méi)有被實(shí)現(xiàn)洞辣,會(huì)出現(xiàn)錯(cuò)誤提示:未定義符號(hào)

解決弱引用符號(hào)的使用問(wèn)題咐刨,可以通過(guò)鏈接器,將符號(hào)聲明為動(dòng)態(tài)鏈接

使用man ld命令查看鏈接器參數(shù):

  • -U:指明該符號(hào)未定義扬霜,需要運(yùn)行時(shí)動(dòng)態(tài)查找

打開(kāi)xcconfig文件定鸟,添加OTHER_LDFLAGS配置項(xiàng)

OTHER_LDFLAGS=$(inherited) -Xlinker -U -Xlinker _weak_import_function
  • 此時(shí)項(xiàng)目可以正常編譯成功
  • 通過(guò)-U參數(shù),告訴鏈接器此符號(hào)是動(dòng)態(tài)鏈接的著瓶,所以在鏈接階段联予,即使它是未定義符號(hào),忽略材原,不用管它沸久。因?yàn)樵谶\(yùn)行時(shí),動(dòng)態(tài)鏈接器會(huì)自動(dòng)找到它

運(yùn)行項(xiàng)目华糖,雖然weak_import_function函數(shù)沒(méi)有被實(shí)現(xiàn)麦向,但運(yùn)行并不會(huì)報(bào)錯(cuò)

  • 因?yàn)?code>main函數(shù)中調(diào)用weak_import_function函數(shù)之前有if (weak_import_function)的判斷
  • 當(dāng)動(dòng)態(tài)鏈接器找不到該符號(hào)的定義,則將其設(shè)置為0客叉。所以weak_import_function函數(shù)并不會(huì)被調(diào)用

弱引用符號(hào)的作用

  • 將一個(gè)符號(hào)聲明為弱引用符號(hào),可以避免編譯鏈接時(shí)報(bào)錯(cuò)。在調(diào)用之前增加條件判斷兼搏,運(yùn)行時(shí)也不會(huì)報(bào)錯(cuò)
  • 使用動(dòng)態(tài)庫(kù)的時(shí)候卵慰,可以將整個(gè)動(dòng)態(tài)庫(kù)聲明為弱引用,此時(shí)動(dòng)態(tài)庫(kù)即使沒(méi)有被導(dǎo)入佛呻,也不會(huì)出現(xiàn)未找到動(dòng)態(tài)庫(kù)的錯(cuò)誤
Common Symbol

在定義時(shí)裳朋,未初始化的全局符號(hào)

例如:main.m文件中,未初始化的global_uninit_value全局變量吓著,它就屬于Common Symbol

int global_uninit_value;

打開(kāi)main.m文件鲤嫡,定義兩個(gè)同名的全局變量,一個(gè)初始化绑莺,另一個(gè)不進(jìn)行初始化暖眼,這種操作并不會(huì)報(bào)錯(cuò)

int global_init_value = 10;
int global_init_value;
  • Common Symbol的作用
  • 在編譯和鏈接的過(guò)程中,如果找到定義的符號(hào)纺裁,會(huì)自動(dòng)將未定義符號(hào)刪掉
  • 在鏈接過(guò)程中诫肠,鏈接器默認(rèn)會(huì)把未定義符號(hào)變成強(qiáng)制定義的符號(hào)

鏈接器設(shè)置:

  • -d:強(qiáng)制定義Common Symbol
  • -commons:指定對(duì)待Common Symbol如何響應(yīng)
重新導(dǎo)出符號(hào)

NSLog為例:

  • 對(duì)于當(dāng)前程序來(lái)說(shuō),NSLog屬于存儲(chǔ)在間接符號(hào)表中的未定義符號(hào)

NSLog可以在當(dāng)前程序使用欺缘,如果想讓使用此程序的其他程序也能使用栋豫,就要將此符號(hào)重新導(dǎo)出。重新導(dǎo)出之后的符號(hào)會(huì)放在導(dǎo)出符號(hào)表中谚殊,此時(shí)才能被外界查看并使用

使用man ld命令查看鏈接器參數(shù):

  • -alias:只能給間接符號(hào)表中的符號(hào)創(chuàng)建別名丧鸯,別名符號(hào)具有全局可見(jiàn)性

打開(kāi)xcconfig文件,添加OTHER_LDFLAGS配置項(xiàng)

OTHER_LDFLAGS=$(inherited) -Xlinker -alias -Xlinker _NSLog -Xlinker Cat_NSLog

  • _NSLog符號(hào)創(chuàng)建Cat_NSLog別名

使用nm -m ${MACH_PATH} | grep "Cat_NSLog"命令查看符號(hào)表嫩絮,指定"Cat_NSLog"關(guān)鍵字

(indirect) external Cat_NSLog (for _NSLog)

  • 此時(shí)Cat_NSLog是一個(gè)間接外部符號(hào)丛肢,是_NSLog符號(hào)的別名

使用objdump --macho --exports-trie ${MACH_PATH}命令查看導(dǎo)出符號(hào)

Exports trie:
0x100000000  __mh_execute_header
0x100003F20  _main
0x100008018  _global_init_value
0x100008028  _global_uninit_value
[re-export] Cat_NSLog (_NSLog from Foundation)

  • Cat_NSLog為導(dǎo)出符號(hào),并且標(biāo)記為[re-export]絮记,代表重新導(dǎo)出符號(hào)

重新導(dǎo)出符號(hào)的作用

  • 將一個(gè)間接符號(hào)表中的符號(hào)聲明為重新導(dǎo)出符號(hào)摔踱,可以讓使用此程序的其他程序也能使用
  • 當(dāng)程序鏈接A動(dòng)態(tài)庫(kù),而A動(dòng)態(tài)庫(kù)又鏈接B動(dòng)態(tài)庫(kù)時(shí)怨愤,B動(dòng)態(tài)庫(kù)對(duì)于程序來(lái)說(shuō)是不可見(jiàn)的派敷。此時(shí)可以使用重新導(dǎo)出的方式,讓B動(dòng)態(tài)庫(kù)對(duì)程序可見(jiàn)

四撰洗、符號(hào)的擴(kuò)展

查看項(xiàng)目使用的三方庫(kù)和符號(hào)等信息

通過(guò)鏈接器篮愉,可以查看當(dāng)前項(xiàng)目中使用的三方庫(kù)和符號(hào)等信息

使用man ld命令查看鏈接器參數(shù):

  • -map:將所有符號(hào)詳細(xì)信息導(dǎo)出到指定文件

打開(kāi)xcconfig文件,添加OTHER_LDFLAGS配置項(xiàng)

OTHER_LDFLAGS=$(inherited) -Xlinker -map -Xlinker $(PROJECT_DIR)/export.txt

編譯項(xiàng)目差导,此時(shí)項(xiàng)目目錄下多出export.txt文件

打開(kāi)export.txt文件

# Path: /Users/zang/Library/Developer/Xcode/DerivedData/MachOAndSymbol-bdzlylfoorwnhggerxdmwocpeyad/Build/Products/Debug/MachOAndSymbol
# Arch: x86_64
# Object files:
[  0] linker synthesized
[  1] /Users/zang/Library/Developer/Xcode/DerivedData/MachOAndSymbol-bdzlylfoorwnhggerxdmwocpeyad/Build/Intermediates.noindex/MachOAndSymbol.build/Debug/MachOAndSymbol.build/Objects-normal/x86_64/LGOneObject.o
[  2] /Users/zang/Library/Developer/Xcode/DerivedData/MachOAndSymbol-bdzlylfoorwnhggerxdmwocpeyad/Build/Intermediates.noindex/MachOAndSymbol.build/Debug/MachOAndSymbol.build/Objects-normal/x86_64/main.o
[  3] /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk/System/Library/Frameworks//Foundation.framework/Foundation.tbd
# Sections:
# Address   Size        Segment Section
0x100003EF0 0x0000006F  __TEXT  __text
0x100003F60 0x00000006  __TEXT  __stubs
0x100003F68 0x0000001A  __TEXT  __stub_helper
0x100003F82 0x00000011  __TEXT  __cstring
0x100003F93 0x0000000C  __TEXT  __objc_classname
0x100003F9F 0x0000000E  __TEXT  __objc_methname
0x100003FAD 0x00000008  __TEXT  __objc_methtype
0x100003FB8 0x00000048  __TEXT  __unwind_info
0x100004000 0x00000008  __DATA_CONST    __got
0x100004008 0x00000040  __DATA_CONST    __cfstring
0x100004048 0x00000008  __DATA_CONST    __objc_classlist
0x100004050 0x00000008  __DATA_CONST    __objc_imageinfo
0x100008000 0x00000008  __DATA  __la_symbol_ptr
0x100008008 0x000000B0  __DATA  __objc_const
0x1000080B8 0x00000050  __DATA  __objc_data
0x100008108 0x00000010  __DATA  __data
0x100008118 0x00000004  __DATA  __bss
0x100008120 0x00000010  __DATA  __common
# Symbols:
# Address   Size        File  Name
0x100003EF0 0x00000027  [  1] -[LGOneObject testOneObject]
0x100003F20 0x0000003F  [  2] _main
0x100003F60 0x00000006  [  3] _NSLog
0x100003F68 0x00000010  [  0] helper helper
0x100003F78 0x0000000A  [  3] _NSLog
0x100003F82 0x0000000E  [  1] literal string: testOneObject
0x100003F90 0x00000003  [  2] literal string: %d
0x100003F93 0x0000000C  [  1] literal string: LGOneObject
0x100003F9F 0x0000000E  [  1] literal string: testOneObject
0x100003FAD 0x00000008  [  1] literal string: v16@0:8
0x100003FB8 0x00000048  [  0] compact unwind info
0x100004000 0x00000008  [  0] non-lazy-pointer-to-local: dyld_stub_binder
0x100004008 0x00000020  [  1] CFString
0x100004028 0x00000020  [  2] CFString
0x100004048 0x00000008  [  1] objc-cat-list
0x100004050 0x00000008  [  0] objc image info
0x100008000 0x00000008  [  3] _NSLog
0x100008008 0x00000048  [  1] __OBJC_METACLASS_RO_$_LGOneObject
0x100008050 0x00000020  [  1] __OBJC_$_INSTANCE_METHODS_LGOneObject
0x100008070 0x00000048  [  1] __OBJC_CLASS_RO_$_LGOneObject
0x1000080B8 0x00000028  [  1] _OBJC_METACLASS_$_LGOneObject
0x1000080E0 0x00000028  [  1] _OBJC_CLASS_$_LGOneObject
0x100008108 0x00000008  [  0] __dyld_private
0x100008110 0x00000004  [  2] _global_init_value
0x100008114 0x00000004  [  2] _static_init_value
0x100008118 0x00000004  [  2] _static_uninit_value
0x100008120 0x00000008  [  2] _global_uninit_value
0x100008128 0x00000008  [  2] _default_x

  • 文件內(nèi)包含了編譯鏈接時(shí)生成的目標(biāo)文件试躏,項(xiàng)目中使用的三方庫(kù),還包含項(xiàng)目中的SectionsSymbols等信息
Section的名稱與作用
名稱 作用
TEXT.text 可執(zhí)行的機(jī)器碼
TEXT.cstring 去重后的C字符串
TEXT.const 初始化過(guò)的常量
TEXT.stubs 符號(hào)樁设褐。lazybinding的表對(duì) 應(yīng)項(xiàng)指針指向的地址的代碼
TEXT.stub_ helper 輔助函數(shù)颠蕴。當(dāng)在lazybinding的表中沒(méi)有找到對(duì)應(yīng)項(xiàng)的指針表示的真正的符號(hào)地址的時(shí)候泣刹,指向這
TEXT.unwind_info 存儲(chǔ)處理異常情況信息
TEXT.eh_frame 調(diào)試輔助信息
DATA.data 初始化過(guò)的可變的數(shù)據(jù)
DATA.nI_symbol_ptr lazy-binding的指針表,每個(gè)表中的指針指向一個(gè)在裝載過(guò)程中犀被,被動(dòng)態(tài)鏈接器搜索完成的符號(hào)
DATA.Ia_symbol_ptr lazy-binding的指針表椅您,每個(gè)表中的指針一開(kāi)始指向stub_helper
DATA.const 沒(méi)有初始化過(guò)的常量
DATA.mod_init_func 初始化函數(shù),在main之前調(diào)用
DATA.mod_term_func 終止函數(shù)寡键,在main返回之后調(diào)用
DATA.bss 沒(méi)有初始化的靜態(tài)變量
DATA.common 沒(méi)有初始化過(guò)的符號(hào)聲明(for example, int I;
Swift符號(hào)表

打開(kāi)SwiftSymbol.swift文件掀泳,寫入以下代碼:

public class LGSwiftClassSymbol {
   func testSwiftSymbol() {
   }
}

使用objdump --macho --syms ${MACH_PATH} | grep "Swift"命令查看符號(hào)表,指定Swift關(guān)鍵字

0000000100003f8a lw    O __TEXT,__swift5_typeref _symbolic _____ 14MachOAndSymbol012LGSwiftClassC0C
0000000100003f90 l     O __TEXT,__swift5_fieldmd _$s14MachOAndSymbol012LGSwiftClassC0CMF
0000000100008020 l     O __DATA,__objc_const __METACLASS_DATA__TtC14MachOAndSymbol18LGSwiftClassSymbol
0000000100008068 l     O __DATA,__objc_const __DATA__TtC14MachOAndSymbol18LGSwiftClassSymbol
00000001000080e8 l     O __DATA,__data _$s14MachOAndSymbol012LGSwiftClassC0CMf
0000000100003d90 g     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC0C09testSwiftC0yyF
0000000100003f78 g     O __TEXT,__const _$s14MachOAndSymbol012LGSwiftClassC0C09testSwiftC0yyFTq
0000000100003e10 g     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC0CACycfC
0000000100003f80 g     O __TEXT,__const _$s14MachOAndSymbol012LGSwiftClassC0CACycfCTq
0000000100003e40 g     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC0CACycfc
0000000100003e60 g     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC0CMa
00000001000080c0 g     O __DATA,__data _$s14MachOAndSymbol012LGSwiftClassC0CMm
0000000100003f44 g     O __TEXT,__const _$s14MachOAndSymbol012LGSwiftClassC0CMn
00000001000080f8 g     O __DATA,__data _$s14MachOAndSymbol012LGSwiftClassC0CN
0000000100003dd0 g     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC0CfD
0000000100003db0 g     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC0Cfd
0000000000000000         *UND* _OBJC_CLASS_$__TtCs12_SwiftObject
0000000000000000         *UND* _OBJC_METACLASS_$__TtCs12_SwiftObject

  • 查找出所有包含Swift關(guān)鍵字的符號(hào)西轩,其中有很多標(biāo)記為g的全局符號(hào)

打開(kāi)SwiftSymbol.swift文件员舵,修改LGSwiftClassSymbol類的訪問(wèn)控制,改為private修飾

private class LGSwiftClassSymbol {
   func testSwiftSymbol() {
   }
}

使用objdump --macho --syms ${MACH_PATH} | grep "Swift"命令查看符號(hào)表藕畔,指定Swift關(guān)鍵字

0000000100003d10 l     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLC09testSwiftC0yyF
0000000100003d30 l     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCfd
0000000100003d50 l     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCfD
0000000100003d90 l     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCMa
0000000100003db0 l     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCADycfC
0000000100003de0 l     F __TEXT,__text _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCADycfc
0000000100003f1c lw    O __TEXT,__const _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCMXX
0000000100003f44 l     O __TEXT,__const _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCMn
0000000100003f78 l     O __TEXT,__const _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLC09testSwiftC0yyFTq
0000000100003f80 l     O __TEXT,__const _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCADycfCTq
0000000100003f8a lw    O __TEXT,__swift5_typeref _symbolic _____ 14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLC
0000000100003f90 l     O __TEXT,__swift5_fieldmd _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCMF
0000000100008020 l     O __DATA,__objc_const __METACLASS_DATA__TtC14MachOAndSymbolP33_66093EBE10D00815F1A5CBD65FFF466118LGSwiftClassSymbol
0000000100008068 l     O __DATA,__objc_const __DATA__TtC14MachOAndSymbolP33_66093EBE10D00815F1A5CBD65FFF466118LGSwiftClassSymbol
00000001000080c0 l     O __DATA,__data _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCMm
00000001000080e8 l     O __DATA,__data _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCMf
00000001000080f8 l     O __DATA,__data _$s14MachOAndSymbol012LGSwiftClassC033_66093EBE10D00815F1A5CBD65FFF4661LLCN
0000000000000000         *UND* _OBJC_CLASS_$__TtCs12_SwiftObject
0000000000000000         *UND* _OBJC_METACLASS_$__TtCs12_SwiftObject

  • 之前那些標(biāo)記為g的全局符號(hào)马僻,全部變?yōu)楸镜胤?hào)
$(SRCROOT)$(PROJECT_DIR)的區(qū)別
  • $(SRCROOT)代表的是項(xiàng)目根目錄下
  • $(PROJECT_DIR)代表的是整個(gè)項(xiàng)目

往項(xiàng)目添加文件時(shí),例如.a文件劫流,要先Show in Finder巫玻,復(fù)制到工程目錄中,然后再拖到xcode項(xiàng)目中

而有時(shí).a不在工程目錄中祠汇。例如在工程的父目錄仍秤,可以寫成:$(SRCROOT)/../Spark/libSDK。其中/../就是指向父目錄

五可很、 符號(hào)的剝離Strip命令

  • 符號(hào)表中有些符號(hào)是必須的诗力,但是很多符號(hào)都是去掉的 , strip經(jīng)常用來(lái)去除目標(biāo)文件中的一些符號(hào)表、調(diào)試符號(hào)表信息我抠,以減小程序的大小;
  • strip命令:移除或修改符號(hào)表中的符號(hào)苇本。App 進(jìn)行脫符號(hào),可以將本地符號(hào)菜拓,全局符號(hào)全部脫去(All symbols),只留下間接符號(hào)表中的符號(hào)瓣窄。
5.1 如何脫去調(diào)試符號(hào)
5.2 如何脫去All Symbols
  • 靜態(tài)庫(kù)進(jìn)行脫符號(hào),放置在重定位符號(hào)表中的符號(hào)不能脫去纳鼎,能脫去的只要調(diào)試符號(hào)(Debugging Symbols)

調(diào)試符號(hào):由匯編器生成.o文件時(shí)俺夕,會(huì)生成一個(gè)DWARF格式的調(diào)試信息,它會(huì)被放到__DWARF段贱鄙,在鏈接是劝贸,會(huì)將__DWARF段放到符號(hào)表中,鏈接后所有的符號(hào)都放在符號(hào)表中

  • 動(dòng)態(tài)庫(kù)進(jìn)行脫符號(hào)逗宁,只要不是全局符號(hào)都可以被干掉(Non-Global Symbols)

如何脫去Non-Global Symbols

鏈接一個(gè)靜態(tài)庫(kù)映九,靜態(tài)庫(kù)中的符號(hào)會(huì)合并到APP中,但不會(huì)存入間接符號(hào)表瞎颗,然后進(jìn)行APP脫符號(hào)時(shí)件甥,可以體積縮小

鏈接一個(gè)動(dòng)態(tài)庫(kù)捌议,動(dòng)態(tài)庫(kù)中的符號(hào)表會(huì)放到APP的間接符號(hào)表,然后進(jìn)行APP脫符號(hào)時(shí)嚼蚀,間接符號(hào)表不能被脫去

5.3 Build Setting的Strip Style選擇
  • Debugging Symbols
  • All Symbols
  • Non-Global Symbols
5.4 Strip執(zhí)行的時(shí)機(jī)

編譯完成執(zhí)行腳本后 禁灼,簽名之前管挟,所以這是后剝離符號(hào) 已經(jīng)不起作用了

5.5 Strip剝離符號(hào)
  • -x : non_global
  • 無(wú)參數(shù): 代表全部符號(hào)
  • -S : 剝離調(diào)試符號(hào)

app瘦身:

    1. 在編譯的時(shí)生產(chǎn)目標(biāo)文件時(shí)優(yōu)化 -O1轿曙,-Oz;
    1. dead code strip 在鏈接的時(shí)死代碼剝離 僻孝;
    1. strip 生產(chǎn)Macho文件后剝離符號(hào) 导帝;

給鏈接器傳參 -S (去除調(diào)試符號(hào))

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市穿铆,隨后出現(xiàn)的幾起案子您单,更是在濱河造成了極大的恐慌,老刑警劉巖荞雏,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件虐秦,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡凤优,警方通過(guò)查閱死者的電腦和手機(jī)傅瞻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門坞笙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事医窿。” “怎么了舶沛?”我有些...
    開(kāi)封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵席函,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我楚昭,道長(zhǎng)栖袋,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任抚太,我火速辦了婚禮塘幅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凭舶。我一直安慰自己晌块,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布帅霜。 她就那樣靜靜地躺著匆背,像睡著了一般。 火紅的嫁衣襯著肌膚如雪身冀。 梳的紋絲不亂的頭發(fā)上钝尸,一...
    開(kāi)封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天括享,我揣著相機(jī)與錄音,去河邊找鬼珍促。 笑死铃辖,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的猪叙。 我是一名探鬼主播娇斩,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼穴翩!你這毒婦竟也來(lái)了犬第?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤芒帕,失蹤者是張志新(化名)和其女友劉穎歉嗓,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體背蟆,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鉴分,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了带膀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片志珍。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖本砰,靈堂內(nèi)的尸體忽然破棺而出碴裙,到底是詐尸還是另有隱情,我是刑警寧澤点额,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布舔株,位于F島的核電站,受9級(jí)特大地震影響还棱,放射性物質(zhì)發(fā)生泄漏载慈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一珍手、第九天 我趴在偏房一處隱蔽的房頂上張望办铡。 院中可真熱鬧,春花似錦琳要、人聲如沸寡具。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)童叠。三九已至,卻和暖如春课幕,著一層夾襖步出監(jiān)牢的瞬間厦坛,已是汗流浹背五垮。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杜秸,地道東北人放仗。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像撬碟,于是被迫代替她去往敵國(guó)和親诞挨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容