PDB符号文件详解

0 前言

作为Windows程序员,对PDB文件肯定不陌生,不管是平时的本机调试还是现网Crash的Dump文件分析,如有PDB文件的辅助,可以大大提高调试效率。

本文主要从PDB的文件格式,PDB的生成与存储以及PDB的使用几个方面介绍PDB的相关知识。

1 PDB简介

PDB(Program DataBase)是Vsiual Studio构建工程时生成的用于调试的符号文件。主要包含了如下信息:

下面是与PDB相关的几个VS工程配置选项:

  • 指定调试信息格式,设置为/Zi或/ZI时会生成PDB文件:
    Project -> Properites -> Configuration Properties -> C/C++ -> General -> Debug Information Format

    调试信息格式设置

  • 指定编译器(Compiler)生成的PDB文件名,默认为VCx0.pdb,其中x代表当前VS的版本,例如VS2010默认PDB名为VC100.pdb:
    Project -> Properites -> Configuration Properties -> C/C++ -> Output Files -> Program Database File Name

    编译器生成PDB文件名设置

  • 指定连接器(Linker)是否生成调试信息,YES为生成,NO为不生成。
    Project -> Properites -> Configuration Properties -> Linker -> Debugging -> Generate Debug Info

  • 指定连接器(Linker)生成的PDB文件名,默认为TargetName.pdb,其中TargetName为连接后生成的文件名。
    Project -> Properites -> Configuration Properties -> Linker -> Debugging -> Generate Program Database File

    链接器生成PDB文件名设置

1.1 PE文件格式

连接器在连接时将PDB文件名及路径信息存放到可执行文件的"CODEVIEW"调试目录中。该目录包含如下PDB相关信息:

偏移地址 数据类型 注释
+0h dword "RSDS"签名
+4h GUID 16字节的GUID
+14h dword "age"
+18h byte string 以0结尾的UTF-8格式文件路径和文件名

下面我们就通过PEView来查看一个PE文件。

PE CODEVIEW信息

上图中对应的数据信息如下所示:

偏移地址 数据类型 注释
+0h dword RSDS
+4h GUID E6AC12193E049A47AD6BD95644534850
+14h dword 1
+18h byte string d:\Test\MyTest\Output\MyTest\MyTest.pdb

1.2 PDB文件格式

PDB文件格式并未公开,但是Microsoft提供了API来读取PDB中的数据,可以参考CCI开源项目。

虽然官方未公开格式,但是The RSDS pdb format对PDB的文件格式做了较详细的介绍。

PDB的文件格式类似于磁盘的文件系统,每个磁盘会被划分成很多个大小一样的扇区,文件中的数据就存放在不同的扇区中,而且无需保证这些扇区在磁盘上是连续的。PDB文件用page进行划分,类似于扇区,stream就类似于文件,stream directory类似于文件目录。

下面我们就用winhex来看下PDB中到底存放了那些信息吧。

1.2.1 PDB头部结构

PDB头部结构信息

上图中PDB头部信息解析如下:

偏移地址 数据类型 数据 备注
+0h byte string Microsoft C/C++ MSF 7.00 PDB版本信息,不同版本字符串长度不一样
+1Ah byte EOF 标志PDB版本信息字符串结束
+1Bh byte string DS 签名
+1Eh byte null-terminator 终结符
+20h dword 00000400h(1024) page的大小(单位:字节)
+24h dword 00000002h(2) 未知
+28h dword 00000293h(659) 整个文件有占用多少个page
+2ch dword 00000AE0h(2784) stream directory占用大小(单位:字节)
+30h dword 00000000h(0) 未知
+34h dword 00000291h(657) stream directory指针的页地址(单位:页)

1.2.2 PDB Stream Directory Pointers信息结构

根据上述信息可以计算出stream directory指针地址为A4400h,对应信息如下:


PDB Stream Directory Pointers信息

从图中可以看出存放了三个stream的页地址指针,分别为028Eh, 028Fh, 0290h,之所以是三个是因为stream directory占用了0AE0(2784)字节,需要三个page才能存放。

注意:这里的指针并不需要是连续的,只是这里刚好是连续的而已。

1.2.3 PDB Stream Directory信息结构

Stream Directory的数据结构如下所示:

偏移地址 数据类型 注释
+0h dword stream的个数
+4h dword stream大小(单位:字节),忽略0和-1(FFFFFFFF)
+?h dword stream的指针数组

以028Eh为例,乘以400h之后得到A3800h,该地址对应信息如下:


PDB_STREAM_DIRECTORY.png

从上图可以看出该stream directory中存在002Fh(47)个stream,而接下来的47个dword分别表示每个stream的大小。因此page指针将从A38C0h处开始。

前3个stream信息解析如下:

Streams size page pointers
Stream1 0000001Ch 00000004h
Stream2 00000072h 0000028Ch
Stream3 00050FD0h 0000028Dh,00000134h,...

1.2.4 PDB Stream信息结构

不同的stream包含不同信息,这里主要介绍下存放PDB文件真实性相关的信息。一般Stream2包含了这些信息。通过计算可以得到Stream2在文件中的地址为A3000h,对应地址的信息如下:


PDB文件真实性信息

红框中记录的就是前面通过PEView工具查看CODEVIEW中的GUID,在它前面的dword就是对应的age字段。

2 PDB的生成与存储

一般在本机编译工程的时候,PDB文件会在本机指定的目录生成,如果要到其它计算机上调试,需要同时拷贝二进制文件和PDB文件。

而对于发布版本,我们一般都会在编译机上编译,并且将生成的PDB文件保存到符号服务器,这样不管在哪台计算机上都可以通过配置符号服务器路径来拉取到对应的PDB文件。

那么PDB文件在符号服务器是如何存储的呢?调试工具又是如何定位到对应的PDB文件的呢?答案就是SysStroe

下面我们来详细介绍下整个过程。

2.1 SysStore

SymStore(symstore.exe)是用于创建符号存储的工具。包含在Windows调试工具包中。

SymStore按照某种格式存储符号,使调试器可以通过时间戳、映像大小(对于.dbg或可执行文件)、签名和寿命(.pdb文件)来查找符号。 使用符号存储而不是常规的符号存储格式的好处是,所有符号都可以在同一个服务器上进行存储或引用,而调试器不需要知道具有哪些产品对应的符号。

注意,不同版本的.pdb符号文件(例如公有和私有符号)不能保存在相同的符号服务器中,因为他们具有相同的签名和寿命。

2.1.1 SymStore事务

  • SymStore的每次调用都被记录为事务。事务分两种:添加和删除。
  • 当创建符号存储时,会在服务器根目录下创建一个"000admin"目录。它为每个事务都包含一个文件以及作为日志记录的server.txt和history.txt。
    • server.txt包含当前服务器上的所有事务的列表。
    • history.txt包含所有历史事务,按时间排序。
  • 每次添加或删除符号文件都会创建一个新的事务号,然后在000admin中创建一个以事务号为名字的文件。文件中包含所有在该事务中添加到符号存储服务器的文件或文件指针的列表。如果某个事务被删除,SymStore会读取它的事务文件来决定哪些文件和指针需要删除。
  • add和del选项用来指定是否进行事务添加或删除。在添加操作中包含/p选项表示添加一个指针,忽略/p指定添加实际的符号文件。
  • 也可以分两步来创建符号存储。
    1. 使用SymStore和/x选项来创建一个索引文件。
    2. 使用SymStore 和/y选项通过索引文件中的信息来创建真正的存储文件或指针。
  • 这是一种很有用的技术。比如:
    1. 方便符号存储由于某些原因丢失后的重建,只要索引文件还存在。
    2. 当包含符号文件的计算机和创建符号存储的计算机之间网络连接速度很慢时。可以在符号文件所在机器上创建索引文件,并将该文件传输到第二台机器,在第二台机器上创建符号存储。
  • SymStore参数的完整列表,请参阅SymStore命令行选项

注意: SymStore不支持多个用户同时发起事务。建议指派一个用户作为符号存储的"管理员"并处理所有的add和del事务。

2.1.2 事务示例

  1. 在\sampledir\symsrv中添加Windows Server 2003 build 3790符号指针。
symstore add /r /p /f \\BuildServer\BuildShare\3790free\symbols\*.*
   /s \\sampledir\symsrv /t "Windows Server 2003" /v "Build 3790 x86 free"
   /c "Sample add"
symstore add /r /p /f \\BuildServer\BuildShare\3790Chk\symbols\*.* 
   /s \\sampledir\symsrv /t "Windows Server 2003" /v "Build 3790 x86 checked"
   /c "Sample add"
  1. 将\largeapp\appserver\bins中的项目符号文件添加到\testdir\symsrv。
symstore add /r /f \\largeapp\appserver\bins\*.* /s \\testdir\symsrv /t "Large Application" /v "Build 432" /c "Sample add"
  1. 下面是使用索引文件的示例。
    1. 创建\largeapp\appserver\bins\上的符号文件的索引文件,并放在第三台计算机\hubserver\hubshare上。 使用/g选项来指定前缀"\largeapp\appserver"可能会在未来改变。
    2. 将\largeapp\appserver机器上的所有符号文件移动到\myarchive\appserver上,然后通过使用\hubserver\hubshare\myindex.txt索引文件来创建符号存储。
symstore add /r /p /g \\largeapp\appserver /f \\largeapp\appserver\bins\*.* /x \\hubserver\hubshare\myindex.txt
symstore add /y \\hubserver\hubshare\myindex.txt /g \\myarchive\appserver /s \\sampledir\symsrv /p /t "Large Application" /v "Build 432" /c "Sample Add from Index"
  1. 删除之前的事务中添加的文件,假设事物ID为0000000096。
symstore del /i 0000000096 /s \\sampledir\symsrv

2.1.3 压缩文件

  • SymStore可以通过两种方法使用压缩文件:
    1. 使用SymStore和/p选项来保存符号文件的指针。SymStore完成之后,压缩指针指向的文件。
    2. 使用SymStore和/x选项来创建索引文件。SymStore完成之后,压缩索引文件中列出的文件。然后,使用SymStore和/y选项(也可以使用/p)来将这些文件或指针保存到符号存储。(在这个操作中无需解压文件。)
  • 符号服务器会负责在适当的时候解压这些文件。
  • 如果使用SymSrv作为符号服务器,必须使用随Microsoft Windows SDK发布的compress.exe 工具来进行压缩。压缩文件扩展名的最后一个字符必须是下划线(例如module1.pd_或module2.db_)。详细信息,请参阅SymSrv

2.1.4 server.txt和history.txt 文件

添加事务之后,会在server.txt和history.txt中添加一些信息用于未来查询使用。下面是一次添加事务在server.txt和history.txt中产生的一行记录:

0000000096,add,ptr,10/09/99,00:08:32,Windows XP,x86 fre 1.156c-RTM-2,Added from \\mybuilds\symbols,

每行记录用逗号分隔。下面是每条记录中各个字段的含义。

字段 说明
0000000096 事务ID号,由SymStore创建
add 事务类型,这个字段可以是add或del。
ptr 添加的是文件或指针。该字段可以是file或ptr。
10/09/99 事务发生的日期。
00:08:32 事务开始的时间。
Windows XP 产品
x86 fre 版本(可选)。
Added from 注释(可选)。
Unused (保留为以后使用)。

下面是事务文件0000000096中的一些示例行。每行都记录了添加到目录中的文件或指针的目录和位置。

canon800.dbg\35d9fd51b000,\\mybuilds\symbols\sp4\dll\canon800.dbg
canonlbp.dbg\35d9fd521c000,\\mybuilds\symbols\sp4\dll\canonlbp.dbg
certadm.dbg\352bf2f48000,\\mybuilds\symbols\sp4\dll\certadm.dbg
certcli.dbg\352bf2f1b000,\\mybuilds\symbols\sp4\dll\certcli.dbg
certcrpt.dbg\352bf04911000,\\mybuilds\symbols\sp4\dll\certcrpt.dbg
certenc.dbg\352bf2f7f000,\\mybuilds\symbols\sp4\dll\certenc.dbg

如果使用del事务来撤销以前的add事务,这些行会从server.txt中删除,并添加下面这行到history.txt中:

0000000105,del,0000000096

删除事务的字段说明如下。

字段 说明
0000000105 事务ID号,SymStore创建。
del 事务类型。该字段可以是add或del。
0000000096 被删除的事务。

2.1.5 符号存储格式

SymStore使用文件系统本身作为数据库。它创建一个大的目录树,目录名基于符号文件的时间戳、签名、寿命和其他数据。

例如一些不同的acpi.dbg添加到服务器之后,目录可能像下面一样:

Directory of \\mybuilds\symsrv\acpi.dbg
10/06/1999  05:46p      <DIR>          .
10/06/1999  05:46p      <DIR>          ..
10/04/1999  01:54p      <DIR>          37cdb03962040
10/04/1999  01:49p      <DIR>          37cdb04027740
10/04/1999  12:56p      <DIR>          37e3eb1c62060
10/04/1999  12:51p      <DIR>          37e3ebcc27760
10/04/1999  12:45p      <DIR>          37ed151662060
10/04/1999  12:39p      <DIR>          37ed15dd27760
10/04/1999  11:33a      <DIR>          37f03ce962020
10/04/1999  11:21a      <DIR>          37f03cf7277c0
10/06/1999  05:38p      <DIR>          37fa7f00277e0
10/06/1999  05:46p      <DIR>          37fa7f01620a0

本例中,acpi.dbg的查找路径可能像这样:\\mybuilds\symsrv\acpi.dbg\37cdb03962040

在查找目录中可能有三个文件:

  1. acpi.dbg,如果保存了该文件。
  2. file.ptr,如果保存的是指针,它包含实际的符号文件路径。
  3. refs.ptr,包含当前被添加到符号存储中的,具有该时间戳和映像大小的acpi.dbg当前位置的完整列表。
2.1.5.1 只包含file.ptr和refs.ptr的情况

\\mybuilds\symsrv\acpi.dbg\37cdb03962040目录显示如下:

10/04/1999  01:54p                  52 file.ptr
10/04/1999  01:54p                  67 refs.ptr

file.ptr文件包含字符串"\\mybuilds\symbols\x86\2128.chk\symbols\sys\acpi.dbg"。因为该目录中没有acpi.dbg 文件,所以调试器会试图从\\mybuilds\symbols\x86\2128.chk\symbols\sys\acpi.dbg中去查找。

refs.ptr中的内容仅被SymStore使用,而不会被调试器使用。这个文件记录该目录中发生过的所有事务的记录。refs.ptr中的一个示例行可能像这样:

0000000026,ptr,\\mybuilds\symbols\x86\2128.chk\symbols\sys\acpi.dbg

这表明指向\\mybuilds\symbols\x86\2128.chk\symbols\sys\acpi.dbg的指针由事务"0000000026"添加。

一些符号文件在各种产品、版本或特殊产品中保持不变。比如:msvcrt.pdb。列举\\mybuilds\symsrv\msvcrt.pdb目录显示只有两个版本的msvcrt.pdb被添加到了符号服务器上:

Directory of \\mybuilds\symsrv\msvcrt.pdb
10/06/1999  05:37p      <DIR>          .
10/06/1999  05:37p      <DIR>          ..
10/04/1999  11:19a      <DIR>          37a8f40e2
10/06/1999  05:37p      <DIR>          37f2c2272

但是,列举\\mybuilds\symsrv\msvcrt.pdb\37a8f40e2目录表明refs.ptr中有一些指针。

Directory of \\mybuilds\symsrv\msvcrt.pdb\37a8f40e2
10/05/1999  02:50p              54     file.ptr
10/05/1999  02:50p           2,039     refs.ptr

\\mybuilds\symsrv\msvcrt.pdb\37a8f40e2\refs.ptr的内容如下:

0000000001,ptr,\\mybuilds\symbols\x86\2137\symbols\dll\msvcrt.pdb
0000000002,ptr,\\mybuilds\symbols\x86\2137.chk\symbols\dll\msvcrt.pdb
0000000003,ptr,\\mybuilds\symbols\x86\2138\symbols\dll\msvcrt.pdb
0000000004,ptr,\\mybuilds\symbols\x86\2138.chk\symbols\dll\msvcrt.pdb
0000000005,ptr,\\mybuilds\symbols\x86\2139\symbols\dll\msvcrt.pdb
0000000006,ptr,\\mybuilds\symbols\x86\2139.chk\symbols\dll\msvcrt.pdb
0000000007,ptr,\\mybuilds\symbols\x86\2140\symbols\dll\msvcrt.pdb
0000000008,ptr,\\mybuilds\symbols\x86\2140.chk\symbols\dll\msvcrt.pdb
0000000009,ptr,\\mybuilds\symbols\x86\2136\symbols\dll\msvcrt.pdb
0000000010,ptr,\\mybuilds\symbols\x86\2136.chk\symbols\dll\msvcrt.pdb
0000000011,ptr,\\mybuilds\symbols\x86\2135\symbols\dll\msvcrt.pdb
0000000012,ptr,\\mybuilds\symbols\x86\2135.chk\symbols\dll\msvcrt.pdb
0000000013,ptr,\\mybuilds\symbols\x86\2134\symbols\dll\msvcrt.pdb
0000000014,ptr,\\mybuilds\symbols\x86\2134.chk\symbols\dll\msvcrt.pdb
0000000015,ptr,\\mybuilds\symbols\x86\2133\symbols\dll\msvcrt.pdb
0000000016,ptr,\\mybuilds\symbols\x86\2133.chk\symbols\dll\msvcrt.pdb
0000000017,ptr,\\mybuilds\symbols\x86\2132\symbols\dll\msvcrt.pdb
0000000018,ptr,\\mybuilds\symbols\x86\2132.chk\symbols\dll\msvcrt.pdb
0000000019,ptr,\\mybuilds\symbols\x86\2131\symbols\dll\msvcrt.pdb
0000000020,ptr,\\mybuilds\symbols\x86\2131.chk\symbols\dll\msvcrt.pdb
0000000021,ptr,\\mybuilds\symbols\x86\2130\symbols\dll\msvcrt.pdb
0000000022,ptr,\\mybuilds\symbols\x86\2130.chk\symbols\dll\msvcrt.pdb
0000000023,ptr,\\mybuilds\symbols\x86\2129\symbols\dll\msvcrt.pdb
0000000024,ptr,\\mybuilds\symbols\x86\2129.chk\symbols\dll\msvcrt.pdb
0000000025,ptr,\\mybuilds\symbols\x86\2128\symbols\dll\msvcrt.pdb
0000000026,ptr,\\mybuilds\symbols\x86\2128.chk\symbols\dll\msvcrt.pdb
0000000027,ptr,\\mybuilds\symbols\x86\2141\symbols\dll\msvcrt.pdb
0000000028,ptr,\\mybuilds\symbols\x86\2141.chk\symbols\dll\msvcrt.pdb
0000000029,ptr,\\mybuilds\symbols\x86\2142\symbols\dll\msvcrt.pdb
0000000030,ptr,\\mybuilds\symbols\x86\2142.chk\symbols\dll\msvcrt.pdb

这表明多次版本构建都用到了相同的msvcrt.pdb并保存到\mybuilds\symsrv。

2.1.5.2 同时包含文件和附加指针的情况

下面是同时包含文件和附加指针的目录示例:

Directory of E:\symsrv\dbghelp.dbg\38039ff439000
10/12/1999  01:54p         141,232     dbghelp.dbg
10/13/1999  04:57p              49     file.ptr
10/13/1999  04:57p             306     refs.ptr

refs.ptr包含的内容如下:

0000000043,file,e:\binaries\symbols\retail\dll\dbghelp.dbg
0000000044,file,f:\binaries\symbols\retail\dll\dbghelp.dbg
0000000045,file,g:\binaries\symbols\retail\dll\dbghelp.dbg
0000000046,ptr,\\sampledir\bin\symbols\retail\dll\dbghelp.dbg
0000000047,ptr,\\sampledir2\bin\symbols\retail\dll\dbghelp.dbg

可以看出,事务43、44和45向服务器添加了同样的文件,事务46和47添加了指针。如果事务43、44和45被删除,dbghelp.dbg 文件也会从目录中删除掉。这时,目录包含的内容如下:

Directory of e:\symsrv\dbghelp.dbg\38039ff439000
10/13/1999  05:01p                   49 file.ptr
10/13/1999  05:01p                 130 refs.ptr

新的file.ptr包含:"\\sampledir2\bin\symbols\retail\dll\dbghelp.dbg"
新的refs.ptr包含:

0000000046,ptr,\\sampledir\bin\symbols\retail\dll\dbghelp.dbg
0000000047,ptr,\\sampledir2\bin\symbols\retail\dll\dbghelp.dbg

只要refs.ptr的最后一项为指针,就会存在file.ptr文件,并且包含相关文件的路径。当refs.ptr最后一项为文件时,目录中不会存在file.ptr。因此,任何对refs.ptr中最后一项的删除操作都可能使得file.ptr被创建、删除或修改。

3 PDB的使用

一般用PDB调试主要是为了能够进行源码调试,因此在使用SysStroe存储之前还需要将PDB与源码关联起来,具体操作请参阅Source Indexing

3.1 VS中如何配置

可以通过以下VS工程属性来配置符号路径。

Tools -> Options -> Debugging -> Symbols

3.2 Windbg中如何配置

可以通过以下方式设置符号路径。

  1. File -> Symbol File Path,快捷键为Ctrl+S
  2. 通过.sympath命令添加符号路径。比如将C:\Symbols目录添加到符号路径列表。
.sympath+ C:\Symbols

3.3 PDB的加载顺序

PDB的加载顺序如下:

  1. 首先是二进制文件所在的目录;
  2. 其次是二进制文件中硬编码的构建目录,例如obj\debug*.pdb;
  3. 再次是根据symbol server的设置,在本地的symbol server的cache中查找;
  4. 最后到远程的symbol server中查找。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 教程一:视频截图(Tutorial 01: Making Screencaps) 首先我们需要了解视频文件的一些基...
    90后的思维阅读 4,648评论 0 3
  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • 之前我对于爱情的理解,是一生一世、海誓山盟、忠贞不二和符合道德伦理的爱恋。后来随着年龄的增长,阅历的增加,才发现,...
    迟来的逐梦人阅读 1,807评论 3 0
  • 我在ATM前查着银行卡的余额,前天发生的事情还历历在目,哥哥最近的这个项目出现了问题,压力太大晚上带着弟弟去兜风,...
    幺小怪阅读 211评论 1 1