Lab2 : ARM指令 - 還是實驗報告誒

連接圖

由于在實驗一中已經(jīng)配置好Acadia的網(wǎng)絡設置,所以這次直接插上網(wǎng)線蟋字,使用ssh進行遠程登錄橄霉。

實驗目的

  1. 深入理解ARM指令和Thumb指令的區(qū)別和編譯選項沧奴;
  2. 深入理解某些特殊的ARM指令,理解如何編寫C代碼來得到這些指令歇竟;
  3. 深入理解ARM的BL指令和C函數(shù)的堆棧保護挥唠;
  4. 深入理解如何實現(xiàn)C和匯編函數(shù)的互相調(diào)用。

實驗步驟

1. arm與thumb指令集

經(jīng)過查閱gcc的編譯選項焕议,找到了編譯為arm的編譯指令-marm以及編譯為thumb的編譯指令-mthumb宝磨。

使用程序驗證如下,

root@Acadia:~/tmp/hello# cat hello.c 
#include<stdio.h>
#include<time.h>

int hello(){
    printf("Hello Acadia! At %lu\n", time(NULL));
}

int main(){
    hello();
    return 0;
}
root@Acadia:~/tmp/hello# gcc hello.c -marm -o arm.out
root@Acadia:~/tmp/hello# gcc hello.c -mthumb -o thumb.out
root@Acadia:~/tmp/hello# ll *.out
-rwxr-xr-x 1 root root 7856 Mar 26 10:44 arm.out*
-rwxr-xr-x 1 root root 7856 Mar 26 10:45 thumb.out*
root@Acadia:~/tmp/hello# echo disas hello | gdb arm.out > arm_hello.s
root@Acadia:~/tmp/hello# echo disas hello | gdb thumb.out > thumb_hello.s
root@Acadia:~/tmp/hello# vimdiff thumb_hello.s arm_hello.s 
vimdiff兩個文件的結(jié)果

首先盅安,測試程序中調(diào)用了內(nèi)部的函數(shù)hello唤锉,同時也調(diào)用了外部函數(shù)printf以及time。而通過gcc編譯后别瞭,兩個文件大小無差異窿祥,故不能從中直接獲知是否為不同指令集的文件。

此時蝙寨,需要使用gdb反匯編hello晒衩,進行比較后方可以看出生成程序的不同。測試命令中使用echo是為了命令的清晰籽慢,并且容易使用輸出重定向比較結(jié)果浸遗。

程序之間最大的區(qū)別是在函數(shù)主體部分。主要區(qū)別有幾點:

  1. thumb的指令地址一次只增加2箱亿,即每條指令只占16位跛锌。相對的,arm每個地址增加4,每條指令占位32位髓帽。
  2. thumb的指令集訪問寄存器 r8 ~ r15 受到一定限制菠赚,所以在thumb程序中可以看出,使用的是r7寄存器代替了arm程序中的r11寄存器郑藏。
  3. thumb程序可以調(diào)用arm指令集的函數(shù)庫衡查,但是地址跳轉(zhuǎn)需要使用blx進行指令集轉(zhuǎn)換。而arm程序直接使用bl跳轉(zhuǎn)即可必盖。

2. arm上的條件命令

root@Acadia:~/tmp/if# cat if.c 
#include <stdio.h>

int main(){
    int n, m;
    scanf("%d", &m);
    if (m > 32){
        n = 5;
    }else{
        n = 1;
    }
    printf("%d\n", n);
    return 0;
}
root@Acadia:~/tmp/if# gcc if.c -marm -o if.out -O2
...
root@Acadia:~/tmp/if# echo disas main | gdb if.out > if_main.s
root@Acadia:~/tmp/if# cat if_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r1, sp, #4
   0x00008380 <+12>:    ldr r0, [pc, #40]   ; 0x83b0 <main+60> 
   0x00008384 <+16>:    bl  0x835c <__isoc99_scanf>   ; r0 = "%d", r1 = &m, 調(diào)用scanf
   0x00008388 <+20>:    ldr r2, [sp, #4]
   0x0000838c <+24>:    mov r0, #1
   0x00008390 <+28>:    ldr r1, [pc, #28]   ; 0x83b4 <main+64>
   0x00008394 <+32>:    cmp r2, #32  ; 執(zhí)行if命令拌牲,使用cmp后條件賦值
   0x00008398 <+36>:    movle   r2, r0
   0x0000839c <+40>:    movgt   r2, #5
   0x000083a0 <+44>:    bl  0x8350 <__printf_chk> ; r2直接傳入printf進行輸出
   0x000083a4 <+48>:    mov r0, #0
   0x000083a8 <+52>:    add sp, sp, #12
   0x000083ac <+56>:    pop {pc}
   0x000083b0 <+60>:    andeq   r8, r0, r4, lsl #9
   0x000083b4 <+64>:    andeq   r8, r0, r8, lsl #9
End of assembler dump.
(gdb) quit

可以看出,if指令被編譯成了3條指令歌粥,即cmp, movle, movgt分別用以條件賦值塌忽。其中,mov為基礎指令失驶,后方跟著的le表示小于等于土居,gt表示大于。

3. 設計 C 的代碼場景,觀察是否產(chǎn)生了寄存器移位尋址

root@Acadia:~/tmp/shift# cat shift.c 
#include <stdio.h>

int main(){
    int a;
    scanf("%d", &a);
    a = a * 9;
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/shift# gcc shift.c -marm -O2 -o shift.out
root@Acadia:~/tmp/shift# echo disas main | gdb shift.out > shift_main.s
root@Acadia:~/tmp/shift# cat shift_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r1, sp, #4
   0x00008380 <+12>:    movw    r0, #33920  ; 0x8480
   0x00008384 <+16>:    movt    r0, #0
   0x00008388 <+20>:    bl  0x835c <__isoc99_scanf>
   0x0000838c <+24>:    ldr r2, [sp, #4]
   0x00008390 <+28>:    mov r0, #1
   0x00008394 <+32>:    movw    r1, #33924  ; 0x8484
   0x00008398 <+36>:    movt    r1, #0
   0x0000839c <+40>:    add r2, r2, r2, lsl #3    ; a = a * 9 = a << 3 + a
   0x000083a0 <+44>:    str r2, [sp, #4]
   0x000083a4 <+48>:    bl  0x8350 <__printf_chk>
   0x000083a8 <+52>:    mov r0, #0
   0x000083ac <+56>:    add sp, sp, #12
   0x000083b0 <+60>:    pop {pc}

由于9在二進制中表示為1001嬉探,所以使用移位加法的方式能夠很好得避過消耗較大的乘法操作擦耀。此時add中使用的就是寄存器移位尋址功能。

4. 設計 C 的代碼場景,觀察一個復雜的 32 位數(shù)是如何裝載到寄存器的

root@Acadia:~/tmp/loadword# cat loadword.c 
#include <stdio.h>

int main(){
    int a = 2123456789;
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/loadword# gcc loadword.c -O2 -marm -o loadword.out
root@Acadia:~/tmp/loadword# echo disas main | gdb loadword.out > loadword_main.s
root@Acadia:~/tmp/loadword# cat loadword_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008320 <+0>: push    {r3, lr}
   0x00008324 <+4>: mov r0, #1
   0x00008328 <+8>: movw    r1, #33808  ; 0x8410
   0x0000832c <+12>:    movw    r2, #24853  ; 0x6115 ; r2 = 0x00006115
   0x00008330 <+16>:    movt     r1, #0
   0x00008334 <+20>:    movt     r2, #32401 ; 0x7e91 ; r2 = 0x7e916115
   0x00008338 <+24>:    bl  0x8308 <__printf_chk>
   0x0000833c <+28>:    mov r0, #0
   0x00008340 <+32>:    pop {r3, pc}

使用計算器涩堤,可以求得2123456789 = 0x7E916115眷蜓。所以,代碼中的movw與movt分別將低位與高位載入寄存器中進行運算定躏。而r1的movw主要是提供了printf的第一個操作數(shù)即格式字符串的位置账磺。

5. 寫一個 C 的多重函數(shù)調(diào)用的程序,觀察和分析

  1. 調(diào)用時的返回地址在哪里?
  2. 傳入的參數(shù)在哪里?
  3. 本地變量的堆棧分配是如何做的?
  4. 寄存器是 caller 保存還是 callee 保存?是全體保存還是部分保存?
root@Acadia:~/tmp/func# cat func.c 
#include <stdio.h>

int fibo(int n){
    if (n <= 1) return 1;
    return fibo(n-2) + fibo(n-1);
}

int main(){
    int a;
    scanf("%d", &a);
    a = fibo(a);
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/func# vim func.c 
root@Acadia:~/tmp/func# gcc func.c -O2 -marm -o func.out
...
root@Acadia:~/tmp/func# echo disas main | gdb func.out > func_main.s
root@Acadia:~/tmp/func# echo disas fibo | gdb func.out > func_fibo.s
root@Acadia:~/tmp/func# cat func_fibo.s
...
(gdb) Dump of assembler code for function fibo:
   0x00008434 <+0>: cmp r0, #1
   0x00008438 <+4>: push    {r3, r4, r5, lr}
   0x0000843c <+8>: ble 0x8468 <fibo+52> ; n <= 1 直接退出并返回1
   0x00008440 <+12>:    sub r4, r0, #2
   0x00008444 <+16>:    mov r5, #0
   0x00008448 <+20>:    mov r0, r4
   0x0000844c <+24>:    sub r4, r4, #1  ; r4 = r0 - 1 即 r4 = n - 1
   0x00008450 <+28>:    bl  0x8434 <fibo>
   0x00008454 <+32>:    cmn r4, #1  ; r4 -= 1, 即r4 = n - 2
   0x00008458 <+36>:    add r5, r5, r0
   0x0000845c <+40>:    bne 0x8448 <fibo+20> ; 尾遞歸優(yōu)化
   0x00008460 <+44>:    add r0, r5, #1
   0x00008464 <+48>:    pop {r3, r4, r5, pc}
   0x00008468 <+52>:    mov r0, #1
   0x0000846c <+56>:    pop {r3, r4, r5, pc}
root@Acadia:~/tmp/func# cat func_main.s
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r1, sp, #4
   0x00008380 <+12>:    movw    r0, #33988  ; 0x84c4
   0x00008384 <+16>:    movt    r0, #0
   0x00008388 <+20>:    bl  0x835c <__isoc99_scanf>
   0x0000838c <+24>:    ldr r0, [sp, #4]
   0x00008390 <+28>:    bl  0x8434 <fibo> ; 調(diào)用fibo函數(shù)
   0x00008394 <+32>:    movw    r1, #33992  ; 0x84c8
   0x00008398 <+36>:    movt    r1, #0
   0x0000839c <+40>:    mov r3, r0
   0x000083a0 <+44>:    mov r0, #1
   0x000083a4 <+48>:    mov r2, r3
   0x000083a8 <+52>:    str r3, [sp, #4]
   0x000083ac <+56>:    bl  0x8350 <__printf_chk>
   0x000083b0 <+60>:    mov r0, #0
   0x000083b4 <+64>:    add sp, sp, #12
   0x000083b8 <+68>:    pop {pc}
End of assembler dump.
(gdb) quit

a. 程序的返回地址在調(diào)用的時候默認存入lr寄存器,而在函數(shù)內(nèi)為了保證返回結(jié)果的正確性痊远,將其push進堆棧中垮抗。而后在函數(shù)結(jié)束的時候,將其pop至pc寄存器實現(xiàn)跳轉(zhuǎn)返回碧聪。
b. 從fibo函數(shù)中看出冒版,r0為函數(shù)的第一個參數(shù)。而在參數(shù)少于4個的時候逞姿,使用寄存器r0~r4傳遞參數(shù)辞嗡。
c. 本地變量的堆棧分配使用push操作將原本的寄存器值存在堆棧中,當返回時再pop出來滞造。而從 push {lr} === str lr, [sp, #-4]可以看出续室,堆棧是自頂向下伸展的。
d. 寄存器的值是collee保存谒养。因為外部調(diào)用者不知道內(nèi)部函數(shù)所需要的寄存器挺狰,保存也就無從談起。
而函數(shù)內(nèi)部用到的所有寄存器均會被保存,因為函數(shù)內(nèi)部并不知道外部會使用哪些寄存器丰泊。

6. MLA 是帶累加的乘法,嘗試要如何寫 C 的表達式能編譯得到 MLA 指令薯定。

root@Acadia:~/tmp/mla# cat mla.c 
#include <stdio.h>

int main(){
    int a, tot;
    scanf("%d %d", &a, &tot);
    tot += a * a;
    printf("%d\n", tot);
    return 0;
}
root@Acadia:~/tmp/mla# gcc mla.c -marm -O2 -o mla.out
...
root@Acadia:~/tmp/mla# echo disas main | gdb mla.out > mla_main.s
root@Acadia:~/tmp/mla# cat mla_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r2, sp, #4
   0x00008380 <+12>:    movw    r0, #33932  ; 0x848c
   0x00008384 <+16>:    mov r1, sp
   0x00008388 <+20>:    movt    r0, #0
   0x0000838c <+24>:    bl  0x835c <__isoc99_scanf>
   0x00008390 <+28>:    ldr r2, [sp, #4]
   0x00008394 <+32>:    ldr r3, [sp]
   0x00008398 <+36>:    mov r0, #1
   0x0000839c <+40>:    movw    r1, #33940  ; 0x8494
   0x000083a0 <+44>:    movt    r1, #0
   0x000083a4 <+48>:    mla r3, r3, r3, r2 ; tot += a * a => r3 = r3 * r3 + r2
   0x000083a8 <+52>:    mov r2, r3
   0x000083ac <+56>:    str r3, [sp, #4]
   0x000083b0 <+60>:    bl  0x8350 <__printf_chk>
   0x000083b4 <+64>:    mov r0, #0
   0x000083b8 <+68>:    add sp, sp, #12
   0x000083bc <+72>:    pop {pc}
End of assembler dump.
(gdb) quit

值得一提的是,在不使用-O2選項的情況下瞳购,編譯的結(jié)果并不含mla话侄。由此可見,代碼優(yōu)化的一種方式是將程序指令盡可能的貼合CPU学赛,手動編寫功能的運行效率較低年堆。

7. BIC是對某一個比特清零的指令,嘗試要如何寫 C 的表達式能編譯得到 BIC 指令罢屈。

root@Acadia:~/tmp/bic# cat bic.c 
#include <stdio.h>
#include <string.h>

int main(){
    int a, b;
    scanf("%d %d", &a, &b);
    a &= ~b;
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/bic# gcc bic.c -marm -O2 -o bic.out
...
root@Acadia:~/tmp/bic# echo disas main | gdb bic.out > bic_main.s
root@Acadia:~/tmp/bic# cat bic_main.s 
...
(gdb) Dump of assembler code for function main:
   0x00008374 <+0>: push    {lr}        ; (str lr, [sp, #-4]!)
   0x00008378 <+4>: sub sp, sp, #12
   0x0000837c <+8>: add r2, sp, #4
   0x00008380 <+12>:    movw    r0, #33928  ; 0x8488
   0x00008384 <+16>:    mov r1, sp
   0x00008388 <+20>:    movt    r0, #0
   0x0000838c <+24>:    bl  0x835c <__isoc99_scanf>
   0x00008390 <+28>:    ldr r3, [sp]
   0x00008394 <+32>:    ldr r2, [sp, #4]
   0x00008398 <+36>:    mov r0, #1
   0x0000839c <+40>:    movw    r1, #33936  ; 0x8490
   0x000083a0 <+44>:    movt    r1, #0
   0x000083a4 <+48>:    bic r2, r3, r2 ; a &= ~b
   0x000083a8 <+52>:    str r2, [sp]
   0x000083ac <+56>:    bl  0x8350 <__printf_chk>
   0x000083b0 <+60>:    mov r0, #0
   0x000083b4 <+64>:    add sp, sp, #12
   0x000083b8 <+68>:    pop {pc}
End of assembler dump.
(gdb) quit

bic指令將某些由標示數(shù)指定的bit清零嘀韧,其原理是通過將標示數(shù)進行取反后取and,即所有原本在標示數(shù)中為1的位缠捌,經(jīng)過取反之后為0。通過and操作译蒂,將所有的0位覆蓋至目標曼月,同時卻又不覆蓋其他bit的值。

8. 編寫一個匯編函數(shù)柔昼。

編寫要求:接受一個整數(shù)和一個指針做為輸入哑芹,指針所指應為一個字符串,該匯編函數(shù)調(diào)用C語言的 printf()函數(shù)輸出這個字符串的前n個字符捕透,n即為那個整數(shù)聪姿。在C語言寫的main()函數(shù)中調(diào)用并傳遞參數(shù)給這個匯編函數(shù) 來得到輸出。

root@Acadia:~/tmp/asm# cat cutprint.S 
.global cutprint
cutprint:
    push {R5, R6, R7, lr}
    MOV R5, R0
    MOV R6, R1
    MOV R7, #0 ; 首先乙嘀,將r0, r1保存起來末购,同時將計數(shù)器r7置零
    CMP R7, R6
    BGE exit  ; 如果r7大于r6的話,直接返回虎谢,因為r6必定是負數(shù)
begin:
    LDR R0, =char ; 由于函數(shù)返回值在r0的位置盟榴,所以每次調(diào)用玩函數(shù),"%c"就會被覆蓋婴噩,需要再載入一次
    LDR R1, [R5, R7] ; 根據(jù)字符串以及r7計數(shù)器的下標擎场,載入字符
    CMP R1, #0 ; 如果遇到了c風格字符串結(jié)尾'\0',直接跳出循環(huán)
    BEQ exit
    bl printf
    ADD R7, R7, #1 ; 計數(shù)器+1
    CMP R7, R6 
    BLT begin ;判斷是否循環(huán)結(jié)束几莽,未結(jié)束則跳到begin進行下一輪循環(huán)
exit:
    LDR R0, =newline ; 輸出結(jié)束換行
    bl printf
    MOV R0, R7 ; 把輸出的計數(shù)器作為返回值
    pop {R5, R6, R7, pc}

.data
    char: .asciz "%c"
    newline: .asciz "\n"
root@Acadia:~/tmp/asm# cat cutprint.c 
#include <stdio.h>

extern int cutprint(char*, int);

int main(){
    int a;
    char s[100];
    scanf("%s %d", s, &a);
    a = cutprint(s, a);
    printf("Print %d character.\n", a);
    return 0;
}
root@Acadia:~/tmp/asm# gcc cutprint.c cutprint.S -o cutprint -g -marm
root@Acadia:~/tmp/asm# ./cutprint
123456789 -1

Print 0 character.
root@Acadia:~/tmp/asm# ./cutprint
123456789 5
12345
Print 5 character.
root@Acadia:~/tmp/asm# ./cutprint
123456789 19999
123456789
Print 9 character.

其中迅办,cutpinrt.s的程序邏輯類似如下:

int cutprint(cahr *s, int a){
    int i = 0;
    for (i; i<a; i++)
        printf(“%c”, s[i]);
    return i;
}

9. 編寫測試程序,測試ARM指令和Thumb指令的執(zhí)行效率

root@Acadia:~/tmp/speed# cat speed.c 
#include <stdio.h>

int fibo(int n){
    if (n <= 1) return 1;
    return fibo(n - 2) + fibo(n - 1);
}

int main(){
    int a;
    scanf("%d", &a);
    printf("%d\n", fibo(a));
    return 0;
}
root@Acadia:~/tmp/speed# gcc speed.c -marm -o arm.out
root@Acadia:~/tmp/speed# gcc speed.c -mthumb -o thumb.out
root@Acadia:~/tmp/speed# echo 40 > speed.in
root@Acadia:~/tmp/speed# time ./arm.out < speed.in
165580141

real    0m5.951s
user    0m5.940s
sys 0m0.000s
root@Acadia:~/tmp/speed# time ./thumb.out < speed.in
165580141

real    0m6.425s
user    0m6.400s
sys 0m0.010s
root@Acadia:~/tmp/speed# gcc speed.c -marm -o arm.out -O2
...
root@Acadia:~/tmp/speed# gcc speed.c -mthumb -o thumb.out -O2
...
root@Acadia:~/tmp/speed# time ./arm.out < speed.in
165580141

real    0m2.190s
user    0m2.180s
sys 0m0.000s
root@Acadia:~/tmp/speed# time ./thumb.out < speed.in
165580141

real    0m3.988s
user    0m3.990s
sys 0m0.000s
root@Acadia:~/tmp/speed# gcc speed.c -marm -o arm.out -O3
...
root@Acadia:~/tmp/speed# gcc speed.c -mthumb -o thumb.out -O3
...
root@Acadia:~/tmp/speed# time ./arm.out < speed.in
165580141

real    0m2.520s
user    0m2.510s
sys 0m0.000s
root@Acadia:~/tmp/speed# time ./thumb.out < speed.in
165580141

real    0m2.723s
user    0m2.710s
sys 0m0.010s

程序中章蚣,主要消耗時間的地方在于fibo(40)的遞歸調(diào)用站欺,可以看出,thumb整體而言還是偏慢的,但是如果開了編譯優(yōu)化之后镊绪,差別不會過大匀伏。

10. 編寫測試程序,測試使用帶條件的ARM指令和不使用時的執(zhí)行效率蝴韭。

root@Acadia:~/tmp/ifspeed# cat noif.S
.global add
add:
    CMP R0, #0
    ADD R0, R0, R1
    MOV pc, lr
root@Acadia:~/tmp/ifspeed# cat useif.S
.global add
add:
    CMP R0, #0
    ADDNE R0, R0, R1
    MOV pc, lr
root@Acadia:~/tmp/ifspeed# cat test.c
#include <stdio.h>

extern int add(int, int);

int main(){
    int a, b, i, times;
    scanf("%d %d %d", &a, &b, &times);
    for (i=0; i<times; i++)
        a = add(a, b);
    printf("%d\n", a);
    return 0;
}
root@Acadia:~/tmp/ifspeed# cat testif.in
1 2 20000000
root@Acadia:~/tmp/ifspeed# gcc useif.S test.c -o useif -g -marm -O2
...
root@Acadia:~/tmp/ifspeed# gcc noif.S test.c -o noif -g -marm -O2
...
root@Acadia:~/tmp/ifspeed# time ./useif < testif.in 
40000001

real    0m0.254s
user    0m0.240s
sys 0m0.010s
root@Acadia:~/tmp/ifspeed# time ./noif < testif.in 
40000001

real    0m0.259s
user    0m0.240s
sys 0m0.010s

經(jīng)過多次比對够颠,雖然時間略有波動。但是總體而言榄鉴,兩程序執(zhí)行時間基本沒有區(qū)別履磨,即ADDNE與ADD的執(zhí)行基本沒有時間差。

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末庆尘,一起剝皮案震驚了整個濱河市剃诅,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驶忌,老刑警劉巖矛辕,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異付魔,居然都是意外死亡聊品,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門几苍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來翻屈,“玉大人,你說我怎么就攤上這事妻坝∩炜簦” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵刽宪,是天一觀的道長厘贼。 經(jīng)常有香客問我,道長纠屋,這世上最難降的妖魔是什么涂臣? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮售担,結(jié)果婚禮上赁遗,老公的妹妹穿的比我還像新娘。我一直安慰自己族铆,他們只是感情好岩四,可當我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哥攘,像睡著了一般剖煌。 火紅的嫁衣襯著肌膚如雪材鹦。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天耕姊,我揣著相機與錄音桶唐,去河邊找鬼。 笑死茉兰,一個胖子當著我的面吹牛尤泽,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播规脸,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼坯约,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了莫鸭?” 一聲冷哼從身側(cè)響起闹丐,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎被因,沒想到半個月后卿拴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡氏身,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年巍棱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛋欣。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖如贷,靈堂內(nèi)的尸體忽然破棺而出陷虎,到底是詐尸還是另有隱情,我是刑警寧澤杠袱,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布尚猿,位于F島的核電站,受9級特大地震影響楣富,放射性物質(zhì)發(fā)生泄漏凿掂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一纹蝴、第九天 我趴在偏房一處隱蔽的房頂上張望庄萎。 院中可真熱鬧,春花似錦塘安、人聲如沸糠涛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽忍捡。三九已至集漾,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間砸脊,已是汗流浹背具篇。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留凌埂,地道東北人驱显。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像侨舆,于是被迫代替她去往敵國和親秒紧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,044評論 2 355

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