iOS高級(jí)強(qiáng)化--004:Symbol

  • Symbol Table:?來(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)表也是通過(guò)讀取Load Command找到符號(hào)表的具體位置


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

  • LC_SYMTAB:當(dāng)前Mach-O中的符號(hào)表信息
  • LC_DYSYMTAB:描述動(dòng)態(tài)鏈接器使用其他的Symbol Table信息
LC_SYMTAB

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

symtab_command

定義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;
};
搭建測(cè)試項(xiàng)目

符號(hào)可以使用終端命令進(jìn)行查看坡椒,但每次手動(dòng)操作過(guò)于繁瑣汗唱。這里介紹一個(gè)更高效的方案:使用Sellp腳本,在項(xiàng)目編譯后丈攒,自動(dòng)將符號(hào)展示到終端

創(chuàng)建xcode_run_cmd.sh文件哩罪,寫(xiě)入以下代碼,將文件放到項(xiàng)目根目錄

該腳本的作用:執(zhí)行命令并將結(jié)果展示到終端

#!/bin/sh

RunCommand() {

  if [[ -n "$VERBOSE_SCRIPT_LOGGING" ]]; then

      if [[ -n "$TTY" ]]; then
          echo "? $@" 1>$TTY
      else
          echo "? $*"
      fi
      echo "------------------------------------------------------------------------------" 1>$TTY
  fi

  if [[ -n "$TTY" ]]; then
        eval "$@" &>$TTY
  else
      "$@"
  fi

  return $?
}

EchoError() {

    if [[ -n "$TTY" ]]; then
        echo "$@" 1>&2>$TTY
    else
        echo "$@" 1>&2
    fi
    
}

RunCMDToTTY() {
    if [[ ! -e "$TTY" ]]; then
        EchoError "=========================================="
        EchoError "ERROR: Not Config tty to output."
        exit -1
    fi

    if [[ -n "$CMD" ]]; then
        RunCommand $CMD
    else
        EchoError "=========================================="
        EchoError "ERROR:Failed to run CMD. THE CMD must not null"
    fi
}

RunCMDToTTY

腳本內(nèi)定義了兩個(gè)變量:

  • CMD:命令+參數(shù)
  • TTY:終端的標(biāo)識(shí)

打開(kāi)終端巡验,使用tty命令际插,可獲取終端標(biāo)識(shí)

打開(kāi)項(xiàng)目,創(chuàng)建xcconfig文件显设,定義腳本中需要的兩個(gè)變量

MACH_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/${PRODUCT_NAME}
CMD=nm -pa $MACHO_PATH
TTY=/dev/ttys001
  • MACHO_PATH:定義變量框弛,存儲(chǔ)Mach-O文件的路徑
  • ${BUILD_DIR}:當(dāng)前編譯路徑
  • $(CONFIGURATION):構(gòu)建產(chǎn)品目錄
  • $(EFFECTIVE_PLATFORM_NAME)Mach-O所在目錄
  • ${PRODUCT_NAME}:項(xiàng)目名稱,也是Mach-O文件的名稱
  • nm:是names的簡(jiǎn)稱捕捂,通過(guò)該命令可以列舉文件中的符號(hào)
  • -p:符號(hào)不進(jìn)行排序
  • -a:顯示所有符號(hào)瑟枫,包含調(diào)試符號(hào)

點(diǎn)擊Target,選擇Build Phases指攒,在Run Script中輸入:/bin/sh "$SRCROOT/xcode_run_cmd.sh"

  • 通過(guò)/bin/bash命令運(yùn)行xcode_run_cmd.sh腳本
  • $SRCROOT代表的是項(xiàng)目根目錄下

項(xiàng)目編譯后慷妙,自動(dòng)將Mach-O中的符號(hào)展示到終端,無(wú)需手動(dòng)操作

查看項(xiàng)目在Build時(shí)允悦,腳本的執(zhí)行時(shí)機(jī)

  • 在編譯鏈接之后景殷,簽名之前
C語(yǔ)言符號(hào)

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

#import <Foundation/Foundation.h>

int global_uninit_value;

int global_init_value = 10;
double default_x __attribute__((visibility("hidden")));

static int static_init_value = 9;
static int static_uninit_value;

int main(int argc, char *argv[]) {
    static_uninit_value = 10;
    NSLog(@"%d", static_init_value);
    return 0;
}

代碼里包含了全局變量和static關(guān)鍵字修飾的靜態(tài)變量澡屡,二者最大的差異在于作用域:

  • 全局變量:對(duì)整個(gè)項(xiàng)目可見(jiàn)
  • 靜態(tài)變量:僅對(duì)當(dāng)前文件可見(jiàn)
案例1:

使用nm -pa ${MACH_PATH}命令查看符號(hào)表

0000000100008008 d __dyld_private
0000000100008014 d _static_init_value
0000000100008018 b _static_uninit_value
0000000100008028 s _default_x
0000000000000000 - 00 0000    SO /Users/zang/Zang/Spark/MachOAndSymbol/
0000000000000000 - 00 0000    SO main.m
0000000060335e1d - 03 0001   OSO /Users/zang/Library/Developer/Xcode/DerivedData/MachOAndSymbol-bdzlylfoorwnhggerxdmwocpeyad/Build/Intermediates.noindex/MachOAndSymbol.build/Debug/MachOAndSymbol.build/Objects-normal/x86_64/main.o
0000000100003f50 - 01 0000 BNSYM
0000000100003f50 - 01 0000   FUN _main
000000000000003f - 00 0000   FUN
000000000000003f - 01 0000 ENSYM
0000000000000000 - 00 0000  GSYM _global_init_value
0000000100008014 - 0a 0000 STSYM _static_init_value
0000000100008018 - 0b 0000 STSYM _static_uninit_value
0000000000000000 - 00 0000  GSYM _global_uninit_value
0000000000000000 - 00 0000  GSYM _default_x
0000000000000000 - 01 0000    SO
0000000100000000 T __mh_execute_header
0000000100008010 D _global_init_value
0000000100008020 S _global_uninit_value
0000000100003f50 T _main
                 U _NSLog
                 U ___CFConstantStringClassReference
                 U dyld_stub_binder

第二列:按照符號(hào)種類劃分的標(biāo)識(shí),參見(jiàn)以下列表

按照符號(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ù)中的?寫(xiě)u表示?個(gè)未定義引?對(duì)同?庫(kù)中另?個(gè)模塊中私有外部符號(hào)

注:標(biāo)記Type铣墨,?寫(xiě)代表本地符號(hào)(local symbol

案例2:

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

SYMBOL TABLE:
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
0000000000000000 l    d  *UND* /Users/zang/Zang/Spark/study/iOS高級(jí)強(qiáng)化/20210118-iOS強(qiáng)化第二節(jié)課:符號(hào)與鏈接(下)/上課代碼/MachOAndSymbol/
0000000000000000 l    d  *UND* main.m
0000000060335b53 l    d  *UND* /Users/zang/Library/Developer/Xcode/DerivedData/MachOAndSymbol-bdzlylfoorwnhggerxdmwocpeyad/Build/Intermediates.noindex/MachOAndSymbol.build/Debug/MachOAndSymbol.build/Objects-normal/x86_64/main.o
0000000100003f50 l    d  *UND*
0000000100003f50 l    d  *UND* _main
000000000000003f l    d  *UND*
000000000000003f l    d  *UND*
0000000000000000 l    d  *UND* _global_init_value
0000000100008014 l    d  *UND* _static_init_value
0000000100008018 l    d  *UND* _static_uninit_value
0000000000000000 l    d  *UND* _global_uninit_value
0000000000000000 l    d  *UND* _default_x
0000000000000000 l    d  *UND*
0000000100000000 g     F __TEXT,__text __mh_execute_header
0000000100008010 g     O __DATA,__data _global_init_value
0000000100008020 g     O __DATA,__common _global_uninit_value
0000000100003f50 g     F __TEXT,__text _main
0000000000000000         *UND* _NSLog
0000000000000000         *UND* ___CFConstantStringClassReference
0000000000000000         *UND* dyld_stub_binder
  • 第二列:l代表本地符號(hào)(local)室埋,g代表全局符號(hào)(global
  • 第三列:按照功能劃分的標(biāo)識(shí),參見(jiàn)以下列表
按照功能劃分:
Type 說(shuō)明
f File
F Function
O Data
d Debug
ABS Absolute
COM Common
UND 未定義
剝離調(diào)試符號(hào)

調(diào)試符號(hào):當(dāng)文件編譯成.o文件時(shí),它會(huì)生成DWARF格式的調(diào)試信息姚淆,放在__DWARF段孕蝉。鏈接時(shí),將__DWARF段變成符號(hào)放到符號(hào)表中腌逢。

上面的打印信息中降淮,包含大量調(diào)試符號(hào)。為了避免干擾搏讶,下面介紹如何剝離調(diào)試符號(hào)佳鳖。

方式一:

使用Build SettingsStrip配置項(xiàng)

  • Deployment PostprocessingDebugRelease下均默認(rèn)為NO,它相當(dāng)于是Deployment的總開(kāi)關(guān)媒惕。將其改為YES
  • Strip StyleDebugRelease下均默認(rèn)All Symbols系吩,剝離除間接符號(hào)表以外的全部符號(hào)。將其改為Debugging Symbols妒蔚,剝離調(diào)試符號(hào)
  • 當(dāng)Deployment PostprocessingNO穿挨,Strip Style的配置是無(wú)法生效的

編譯項(xiàng)目,Strip操作已經(jīng)執(zhí)行肴盏。但它是在腳本執(zhí)行后才觸發(fā)科盛,故此該方式在使用Sellp腳本自動(dòng)化的場(chǎng)景下不可行

方式二:

xcconfig中使用鏈接器配置

鏈接器的作用就是將多個(gè)目標(biāo)文件合并到一起,此時(shí)可以對(duì)符號(hào)進(jìn)行指定處理

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

使用/-S向下查找-S關(guān)鍵字

  • -S:不要將調(diào)試信息放置到輸出文件中叁鉴,本質(zhì)上和StripDebugging Symbols配置項(xiàng)是一樣的效果

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

OTHER_LDFLAGS = -Xlinker -S
  • Xcode使用Target編譯時(shí),執(zhí)行的是clang命令幌墓。但此時(shí)-S需要傳遞給ld鏈接器但壮,所以前面要加上-Xlinker關(guān)鍵字

Build Settings中的Other Linker Flags配置已生效

來(lái)到終端,先command+k清空內(nèi)容常侣,再編譯項(xiàng)目蜡饵。此時(shí)打印出除調(diào)試符號(hào)以外的所有符號(hào)

SYMBOL TABLE:
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
0000000100003f50 g     F __TEXT,__text _main
0000000000000000         *UND* _NSLog
0000000000000000         *UND* ___CFConstantStringClassReference
0000000000000000         *UND* dyld_stub_binder
  • global_uninit_valueglobal_init_value為全局符號(hào)
  • static_init_valuestatic_uninit_value為本地符號(hào)
  • default_x并沒(méi)有使用static關(guān)鍵字修飾,本應(yīng)是全局符號(hào)胳施,但使用了__attribute__((visibility("hidden")))溯祸,變?yōu)楸镜胤?hào)

__attribute__()的作用:將編譯器支持的參數(shù)傳遞給編譯器。例如:對(duì)default_x全局變量進(jìn)行visibility("hidden")設(shè)置舞肆,就是隱藏其可見(jiàn)性

符號(hào)可見(jiàn)性

全局符號(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)

通過(guò)上述兩個(gè)案例卫袒,理解符號(hào)的可見(jiàn)性:

  • 全局符號(hào)對(duì)整個(gè)項(xiàng)目可見(jiàn)
  • 本地符號(hào)僅對(duì)當(dāng)前文件可見(jiàn)
案例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

案例4:

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

同樣在LGApp中仲吏,打開(kāi)AppDelegate.m文件不铆,也實(shí)現(xiàn)global_object函數(shù)

同一個(gè)Project中,實(shí)現(xiàn)兩個(gè)global_object函數(shù)裹唆。此時(shí)編譯報(bào)錯(cuò)誓斥,提示出現(xiàn)重復(fù)符號(hào)

  • 這說(shuō)明在同一作用域中,不能出現(xiàn)相同的全局符號(hào)
導(dǎo)入符號(hào)和導(dǎo)出符號(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文件,寫(xiě)入以下代碼:

#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
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文件戚炫,寫(xiě)入以下代碼:

#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文件媳纬,寫(xiě)入以下代碼:

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文件,寫(xiě)入以下代碼:

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文件,寫(xiě)入以下代碼:

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文件峦失,寫(xiě)入以下代碼:

#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)
圖解
查看項(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文件,寫(xiě)入以下代碼:

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不在工程目錄中。例如在工程的父目錄搓谆,可以寫(xiě)成:$(SRCROOT)/../Spark/libSDK炒辉。其中/../就是指向父目錄

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市泉手,隨后出現(xiàn)的幾起案子黔寇,更是在濱河造成了極大的恐慌,老刑警劉巖斩萌,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缝裤,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡颊郎,警方通過(guò)查閱死者的電腦和手機(jī)憋飞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)姆吭,“玉大人榛做,你說(shuō)我怎么就攤上這事。” “怎么了检眯?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵厘擂,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我锰瘸,道長(zhǎng)刽严,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任避凝,我火速辦了婚禮舞萄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘管削。我一直安慰自己鹏氧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布佩谣。 她就那樣靜靜地躺著,像睡著了一般实蓬。 火紅的嫁衣襯著肌膚如雪茸俭。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,365評(píng)論 1 302
  • 那天安皱,我揣著相機(jī)與錄音调鬓,去河邊找鬼。 笑死酌伊,一個(gè)胖子當(dāng)著我的面吹牛腾窝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播居砖,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼虹脯,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了奏候?” 一聲冷哼從身側(cè)響起循集,我...
    開(kāi)封第一講書(shū)人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔗草,沒(méi)想到半個(gè)月后咒彤,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡咒精,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年镶柱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片模叙。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歇拆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情查吊,我是刑警寧澤谐区,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站逻卖,受9級(jí)特大地震影響宋列,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜评也,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一炼杖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盗迟,春花似錦坤邪、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至邮弹,卻和暖如春黔衡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背腌乡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工盟劫, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人与纽。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓侣签,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親急迂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子影所,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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