windows搭建gcc开发环境(msys2) objdump Linux objdump命令

前言

可能你并不太了解msys2,但是作为一个程序员,你一定知道mingw,而msys2就集成了mingw,同时msys2还有一些其他的特性,例如包管理器等。
msys2可以在windows下搭建一个完美的类linux环境,包括bash、vim、gcc、make等工具都可以通过包管理器来添加和卸载
msys2的包管理器是使用的pacman,用过archlinux的应该都知道pacman了。
我们现在的目标是要集成make+gcc+gdb的一条编译工具

安装

下载地址:
msys2
如果是exe,直接双击安装,如果是zip,直接解压到安装的地方即可。然后双击msys2.exe运行
[图片上传失败...(image-295493-1523187370956)]

给 MSYS2 添加中科大的源

使用过archlinux的应该会知道,pacman在安装的时候,如果源没有设置好,下载是很慢的。
需要修改的文件是:
1. \etc\pacman.d\mirrorlist.mingw32
2. \etc\pacman.d\mirrorlist.mingw64
3. \etc\pacman.d\mirrorlist.msys

简单的说 msys64\etc\pacman.d 目录下有三个文件。
mirrorlist.msys
mirrorlist.mingw64
mirrorlist.mingw32

这三个文件记录了都有哪些源。下面把我添加之后的文件内容贴上来。
首先是 mirrorlist.msys:

##
## MSYS2 repository mirrorlist
##

## Primary
## msys2.org Server = http://mirrors.ustc.edu.cn/msys2/msys/$arch/ Server = http://repo.msys2.org/msys/$arch Server = http://downloads.sourceforge.net/project/msys2/REPOS/MSYS2/$arch Server = http://www2.futureware.at/~nickoe/msys2-mirror/msys/$arch/ 

mirrorlist.mingw64:

##
## 64-bit Mingw-w64 repository mirrorlist
##

## Primary
## msys2.org Server = http://mirrors.ustc.edu.cn/msys2/mingw/x86_64/ Server = http://repo.msys2.org/mingw/x86_64 Server = http://downloads.sourceforge.net/project/msys2/REPOS/MINGW/x86_64 Server = http://www2.futureware.at/~nickoe/msys2-mirror/x86_64/ Server = http://mirror.bit.edu.cn/msys2/REPOS/ 

mirrorlist.mingw32:

##
## 32-bit Mingw-w64 repository mirrorlist
##

## Primary
## msys2.org Server = http://mirrors.ustc.edu.cn/msys2/mingw/i686/ Server = http://repo.msys2.org/mingw/i686 Server = http://downloads.sourceforge.net/project/msys2/REPOS/MINGW/i686 Server = http://www2.futureware.at/~nickoe/msys2-mirror/i686/ 
MSYS2 镜像使用帮助
收录架构
MINGW: i686, x86_64
MSYS: i686, x86_64
安装
请访问该镜像目录下的 distrib/ 目录(x86_64 、i686),找到名为 msys2-<架构>-<日期>.exe 的文件(如 msys2-x86_64-20141113.exe),下载安装即可。

pacman 的配置
编辑 /etc/pacman.d/mirrorlist.mingw32 ,在文件开头添加:

Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/i686 编辑 /etc/pacman.d/mirrorlist.mingw64 ,在文件开头添加: Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/mingw/x86_64 编辑 /etc/pacman.d/mirrorlist.msys ,在文件开头添加: Server = https://mirrors.tuna.tsinghua.edu.cn/msys2/msys/$arch 然后执行 pacman -Sy 刷新软件包数据即可。 

安装可以跳过直接看pacman即可,参照这修改
中国科学技术大学方法可以参考清华的。

下载工具包

下载工具包这一部分就是纯粹的pacman知识了,涉及的指令有:
pacman -Sy 更新软件包数据
pacman -Syu 更新所有
pacman -Ss xx 查询软件xx的信息
pacman -S xx 安装软件xx

下载make

我们先通过命令pacman -Ss make查询完整的make包名
我们可以看到,每个软件大致分为了三大类:mingw32、mingw64、msys,如果想下载mingw那么就要对应着自己的系统下载,32位和64位要区分
我们下载msys类就行。比如我们搜到的make是这样的:

msys/automake1.9 1.9.6-2 (base-devel)
    A GNU tool for automatically creating Makefiles msys/cmake 3.6.2-1 A cross-platform open-source make system msys/colormake-git r8.9c1d2e6-1 Colorized build output msys/make 4.2.1-1 (base-devel) [已安装] GNU make utility to maintain groups of programs msys/make-git 4.1.8.g292da6f-1 GNU make utility to maintain groups of programs msys/perl 5.24.1-2 (base-devel) 

即msys/make,所以我们安装的时候只需要输入:

pacman -S msys/make

或者直接输入

pacman -S make

就会默认安装msys/make

下载gcc

我们先通过命令pacman -Ss gcc查询完整的gcc包名

mingw64/mingw-w64-x86_64-gcc-objc 6.3.0-1 (mingw-w64-x86_64-toolchain)
    GNU Compiler Collection (ObjC,Obj-C++) for MinGW-w64 mingw64/mingw-w64-x86_64-lcov 1.12-1 front-end for GCC's coverage testing tool gcov msys/gcc 6.3.0-1 (msys2-devel) The GNU Compiler Collection - C and C++ frontends msys/gcc-fortran 6.3.0-1 (msys2-devel) Fortran front-end for GCC 

即msys/gcc,所以我们安装的时候只需要输入:

pacman -S msys/gcc

或者

pacman -S gcc

测试

安装后我们测试一下:

make -v

显示类似如下内容说明安装成功:

GNU Make 4.2.1
为 x86_64-pc-msys 编译
Copyright (C) 1988-2016 Free Software Foundation, Inc.
许可证:GPLv3+:GNU 通用公共许可证第 3 版或更新版本<http://gnu.org/licenses/gpl.html>。 本软件是自由软件:您可以自由修改和重新发布它。 在法律允许的范围内没有其他保证。 

然后测试gcc:

使用内建 specs。
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-msys/6.3.0/lto-wrapper.exe
目标:x86_64-pc-msys
配置为:/msys_scripts/gcc/src/gcc-6.3.0/configure --build=x86_64-pc-msys --prefix=/usr --libexecdir=/usr/lib --enable-bootstrap --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --with-arch=x86-64 --with-tune=generic --disable-multilib --enable-__cxa_atexit --with-dwarf2 --enable-languages=c,c++,fortran,lto --enable-graphite --enable-threads=posix --enable-libatomic --enable-libcilkrts --enable-libgomp --enable-libitm --enable-libquadmath --enable-libquadmath-support --enable-libssp --disable-win32-registry --disable-symvers --with-gnu-ld --with-gnu-as --disable-isl-version-check --enable-checking=release --without-libiconv-prefix --without-libintl-prefix --with-system-zlib --enable-linker-build-id --with-default-libstdcxx-abi=gcc4-compatible 线程模型:posix gcc 版本 6.3.0 (GCC) 

其他的软件都是这么玩的,我就不介绍了。

编译测试

我们先新建一个demo.c文件,然后编写一段测试代码:

#include <stdio.h>

int main() { printf("abcDEF\r\n"); } 

保存
然后我们通过cd 命令将msys的目录切换到这个c文件处
后执行

$gcc demo.c -o demo

如果编译没有错误,那么会在当前目录下生成demo.exe,然后运行:

$./demo.exe

即可看到运行结果
运行过程如下:

xml@DESKTOP-B0SS2KE MSYS /f/xml/test/DEMO1
$ gcc demo.c -o demo

xml@DESKTOP-B0SS2KE MSYS /f/xml/test/DEMO1
$ ls
demo.c  demo.exe

xml@DESKTOP-B0SS2KE MSYS /f/xml/test/DEMO1
$ ./demo.exe
abcDEF

当然,如果我们需要进行细致的分析代码,在编译的时候就带上-g参数,然后通过objdump来分析汇编源码吧

xml@DESKTOP-B0SS2KE MSYS /f/xml/test/DEMO1
$ gcc demo.c -g -o demo

xml@DESKTOP-B0SS2KE MSYS /f/xml/test/DEMO1
$ objdump -f ./demo.exe

./demo.exe:     文件格式 pei-x86-64
体系结构:i386:x86-64,标志 0x0000013a:
EXEC_P, HAS_DEBUG, HAS_SYMS, HAS_LOCALS, D_PAGED
起始地址 0x0000000100401000 xml@DESKTOP-B0SS2KE MSYS /f/xml/test/DEMO1 $ objdump -S ./demo.exe ./demo.exe: 文件格式 pei-x86-64 Disassembly of section .text: 00000001004010e0 <main>: #include <stdio.h> int main() { 1004010e0: 55 push %rbp 1004010e1: 48 89 e5 mov %rsp,%rbp 1004010e4: 48 83 ec 20 sub $0x20,%rsp 1004010e8: e8 33 00 00 00 callq 100401120 <__main> printf("abcDEF\r\n"); 1004010ed: 48 8d 0d 3c 1f 00 00 lea 0x1f3c(%rip),%rcx # 100403030 <.rdata> 1004010f4: e8 37 00 00 00 callq 100401130 <puts> 1004010f9: b8 00 00 00 00 mov $0x0,%eax } 

当然我们也可以安装gdb,然后通过gdb来调试代码



作者:苦境名人
链接:https://www.jianshu.com/p/713f8588ba18
来源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
 

一、简介

objdump命令是用查看目标文件或者可执行的目标文件的构成的gcc工具。

二、选项

三、实例

1)显示文件头信息

objdump -f test

2)显示Section Header信息

objdump -h test

3)显示全部Header信息

objdump -x test

4)显示全部Header信息,并显示对应的十六进制文件代码

objdump -s test

5)输出目标文件的符号表

objdump -t obj

6)输出目标文件的所有段概述

objdump -h obj

7)反汇编test中的需要执行指令的那些section

objdump -d test

8)反汇编test中的所有section

objdump -D test

9)反汇编出源码(指定section)

objdump -Slj .text obj

10)对任意二进制文件进行反汇编

objdump -D -b binary -m i386 a.bin

如何使用 objdump 查看源代码 
1. 在编译时必须使用-g选项,-g意为debug,一般可以修改源代码的 Makefile来实现 如: 
CC =$(CROSSCOM_PILE)gcc 为 
CC =$(CROSSCOM_PILE)gcc -g 
使成生的vmlinux中含有debug信息 
2. 所有生成 .o 的 rule 中再加一条 /*其他参数除了-c外抄生成.o文件用的参数*/ 
CC -E -dD -C $< > /preprocessing/$(shell pwd)/$< 
生成预处理文件从这个文件里面能很容易找到 c 源文件的宏定义 
3. objdump -h vmlinux > vmlinux.txt 
显示 linux 内核段信息,如段的开始虚拟地址,段的长度 
4. objdump -S -l -z vmlinux > vmlinux.txt 
反汇编 vmlinux 到vmlinux.txt, vmlinux.txt 含有汇编和 c 源文件的混合代码,看起来很方 
便。而且能一步步看linux怎么一步步运行的。 
5. objdump -S -l -z -j xxxx(section name) vmlinux > vmlinux.txt 
反汇编 linux 内核段 xxxx 到文件 vmlinux.txt 中。 
6. objdump -x vmlinux > x.txt 
vmliux中所有段的头信息,其中包口vmlinux的入口地址等 
7. objdump --debugging vmlinux > debugging.txt 
很多有用的debug信息,如函数名,结构体定义等 
我觉的用根据以上信息,ultraedit看很方便。尤其在vmlinux.txt中选中文件名, 
用ultraedit右键的open能马上打开文件,很方便。

 

objdump -x obj 以某种分类信息的形式把目标文件的数据组织(被分为几大块)输出 <可查到该文件的所有动态库>

  objdump -t obj 输出目标文件的符号表()

  objdump -h obj 输出目标文件的所有段概括()

  objdump -j .text/.data -S obj 输出指定段的信息,大概就是反汇编源代码把

  objdump -S obj C语言与汇编语言同时显示

  以下为网上摘录文章。

  关于nm -s的显示请自己man nm查看

  objdump命令的man手册

  objdump - 显示二进制文件信息

  objdump

  [-a] [-b bfdname |

  --target=bfdname] [-C] [--debugging]

  [-d] [-D]

  [--disassemble-zeroes]

  [-EB|-EL|--endian={big|little}] [-f]

  [-h] [-i|--info]

  [-j section | --section=section]

  [-l] [-m machine ] [--prefix-addresses]

  [-r] [-R]

  [-s|--full-contents] [-S|--source]

  [--[no-]show-raw-insn] [--stabs] [-t]

  [-T] [-x]

  [--start-address=address] [--stop-address=address]

  [--adjust-vma=offset] [--version] [--help]

  objfile...

  --archive-headers

  -a 显示档案库的成员信息,与 ar tv 类似

  objdump -a libpcap.a

  和 ar -tv libpcap.a 显示结果比较比较显然这个选项没有什么意思。

  --adjust-vma=offset

  When dumping information, first add offset to all

  the section addresses. This is useful if the sec-

  tion addresses do not correspond to the symbol

  table, which can happen when putting sections at

  particular addresses when using a format which can

  not represent section addresses, such as a.out.

  -b bfdname

  --target=bfdname

  指定目标码格式。这不是必须的,objdump能自动识别许多格式,比如:objdump -b oasys -m vax -h fu.o

  显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys编译器生成的目标文件。objdump -i将给出这里可以指定的目标码格式列表

  --demangle

  -C 将底层的符号名解码成用户级名字,除了去掉所有开头的下划线之外,还使得C++函数名以可理解的方式显示出来。

  --debugging

  显示调试信息。企图解析保存在文件中的调试信息并以C语言的语法显示出来。仅仅支持某些类型的调试信息。

  --disassemble

  -d 反汇编那些应该还有指令机器码的section

  --disassemble-all

  -D 与 -d 类似,但反汇编所有section

  --prefix-addresses

  反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。

  显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。

  --disassemble-zeroes

  一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。

  -EB

  -EL

  --endian={big|little}

  这个选项将影响反汇编出来的指令。

  little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址,x86都是这种。

  --file-headers

  -f 显示objfile中每个文件的整体头部摘要信息。

  --section-headers

  --headers

  -h 显示目标文件各个section的头部摘要信息。

  --help 简短的帮助信息。

  --info

  -i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。

  --section=name

  -j name 仅仅显示指定section的信息

  --line-numbers

  -l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求编译时使用了-g之类的调试编译选项。

一、GCC编译器和常用命令

 

gcc编译器不仅支持c语言,还支持Ada、C++、Objective-C语言。一般编译的过程可以分为四个阶段:预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)

 

1、  一步到位的编译指令

包含四个阶段

gcc test.c –o test

2、  预处理

将头文件内容插入到test.c文件,-E是预处理结束后停止,并输出预处理结果

gcc –E test.c –o test.i

3、  编译

对.i文件编译,生成汇编代码,-S表示生成汇编代码后停止,-o输出汇编代码文件

gcc –S test.i –o test.s

4、  汇编

将汇编代码文件编译为目标文件

gcc –c test.s –o test.o

5、  连接

将其与C标准输入输出库进行连接,最终生成程序test

gcc test.o –o test

6、  执行

在命令行窗口,执行./test

 

不同文件代表的意义:

test.c(源文件)->test.i(-E预处理后文件)->test.s(-S汇编文件)->test.o(-c目标文件)->test(-o可执行文件)

 

对于多个源文件进行编译:

1、  一步到位

gcc  test1.c  test2.c –o test

2、  复杂一些的(执行过程和一步到位的是一样的,都是预处理->编译->连接)

gcc -c test1.c -o test1.o
gcc -c test2.c -o test2.o
gcc test1.o test2.o -o test

 

常用命令选项的意义:

1、-c 只编译不连接

2、-O 优化编译后的代码,后面加数字代表优化级别

3、-o 指定输出的文件名

4、-v 显示编译过程中每一步用到的命令

 

二、objdump命令的使用

 

objdump是Linux下面的反汇编目标文件或者可执行文件的命令

 

1、objdump –d test

反悔表test中需要执行指令的那些section

 

2、  objdump –h test

显示test的Section Header信息

 

3、  objdump –S test.o

输出C源代码和反汇编出来的指令对照的格式

 

4、  objdump –h

显示目标文件各个section的头部摘要信息

 

5、  objdump –r

显示文件的重定位入口

 

6、  objdump –t显示文件的符号表入口

 

三、file命令

 

file命令是检测文件类型的命令,可以读取文件的编码体系以及一些其他信息

gcc命令之 objdump 

---------------objdump是用查看目标文件或者可执行的目标文件的构成的GCC工具----------
以下3条命令足够那些喜欢探索目标文件与源代码之间的丝丝的关系的朋友。
objdump -x obj 以某种分类信息的形式把目标文件的数据组织(被分为几大块)输出 <可查到该文件的所有动态库>   
objdump -t obj 输出目标文件的符号表()
objdump -h obj 输出目标文件的所有段概括()
objdump -j .text/.data -S obj 输出指定段的信息,大概就是反汇编源代码把
objdump -S obj C语言与汇编语言同时显示
以下为网上摘录文章。

关于nm -s的显示请自己man nm查看
objdump命令的man手册
objdump - 显示二进制文件信息
objdump
      [-a] [-b bfdname |
      --target=bfdname] [-C] [--debugging]
      [-d] [-D]
      [--disassemble-zeroes]
      [-EB|-EL|--endian={big|little}] [-f]
      [-h] [-i|--info]
      [-j section | --section=section]
      [-l] [-m machine ] [--prefix-addresses]
      [-r] [-R]
      [-s|--full-contents] [-S|--source]
      [--[no-]show-raw-insn] [--stabs] [-t]
      [-T] [-x]
      [--start-address=address] [--stop-address=address]
      [--adjust-vma=offset] [--version] [--help]
      objfile...

--archive-headers
-a 显示档案库的成员信息,与 ar tv 类似
    objdump -a libpcap.a
    和 ar -tv libpcap.a 显示结果比较比较
    显然这个选项没有什么意思。
--adjust-vma=offset
    When dumping information, first add offset to all
    the section addresses. This is useful if the sec-
    tion addresses do not correspond to the symbol
    table, which can happen when putting sections at
    particular addresses when using a format which can
    not represent section addresses, such as a.out.
-b bfdname
--target=bfdname
    指定目标码格式。这不是必须的,objdump能自动识别许多格式,
    比如:objdump -b oasys -m vax -h fu.o
    显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys
    编译器生成的目标文件。objdump -i将给出这里可以指定的
    目标码格式列表
--demangle
-C 将底层的符号名解码成用户级名字,除了去掉所有开头
   的下划线之外,还使得C++函数名以可理解的方式显示出来。
--debugging 
    显示调试信息。企图解析保存在文件中的调试信息并以C语言
    的语法显示出来。仅仅支持某些类型的调试信息。
--disassemble
-d 反汇编那些应该还有指令机器码的section
--disassemble-all
-D 与 -d 类似,但反汇编所有section
--prefix-addresses
    反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。
    显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。
--disassemble-zeroes
    一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。
-EB
-EL
--endian={big|little}
    这个选项将影响反汇编出来的指令。
    little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址,
    x86都是这种。

--file-headers
-f 显示objfile中每个文件的整体头部摘要信息。

--section-headers
--headers
-h 显示目标文件各个section的头部摘要信息。

--help 简短的帮助信息。

--info
-i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。

--section=name
-j name 仅仅显示指定section的信息

--line-numbers
-l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用
   使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求
   编译时使用了-g之类的调试编译选项。

--architecture=machine
-m machine
    指定反汇编目标文件时使用的架构,当待反汇编文件本身没有描述
    架构信息的时候(比如S-records),这个选项很有用。可以用-i选项
    列出这里能够指定的架构

--reloc
-r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇
   编后的格式显示出来。

--dynamic-reloc
-R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些
   共享库。

--full-contents
-s 显示指定section的完整内容。

    objdump --section=.text -s inet.o | more

--source
-S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,
   效果比较明显。隐含了-d参数。

--show-raw-insn
    反汇编的时候,显示每条汇编指令对应的机器码,除非指定了
    --prefix-addresses,这将是缺省选项。

--no-show-raw-insn
    反汇编时,不显示汇编指令的机器码,这是指定 --prefix-addresses
    选项时的缺省设置。

--stabs
    Display the contents of the .stab, .stab.index, and
    .stab.excl sections from an ELF file. This is only
    useful on systems (such as Solaris 2.0) in which
    .stab debugging symbol-table entries are carried in
    an ELF section. In most other file formats, debug-
    ging symbol-table entries are interleaved with
    linkage symbols, and are visible in the --syms output.

--start-address=address
    从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。

--stop-address=address
    显示数据直到指定地址为止,该选项影响-d、-r和-s选项的输出。

--syms
-t 显示文件的符号表入口。类似于nm -s提供的信息

--dynamic-syms
-T 显示文件的动态符号表入口,仅仅对动态目标文件有意义,比如某些
   共享库。它显示的信息类似于 nm -D|--dynamic 显示的信息。

--version 版本信息

    objdump --version

--all-headers
-x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于
   -a -f -h -r -t 同时指定。

    objdump -x inet.o

参看 nm(1)

★ objdump应用举例(待增加)

/*
g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c
*/
#include 
#include 
int main ( int argc, char * argv[] )
{
    execl( "/bin/sh", "/bin/sh", "-i", 0 );
    return 0;
}

g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c
objdump -j .text -Sl objtest | more
/main(查找)

08048750:
main():
/home/scz/src/objtest.c:7
*/
#include 
#include 
int main ( int argc, char * argv[] )
{
8048750:       55                      pushl %ebp
8048751:       89 e5                   movl   %esp,%ebp
/home/scz/src/objtest.c:8
        execl( "/bin/sh", "/bin/sh", "-i", 0 );
8048753:       6a 00                   pushl $0x0
8048755:       68 d0 87 04 08          pushl $0x80487d0
804875a:       68 d3 87 04 08          pushl $0x80487d3
804875f:       68 d3 87 04 08          pushl $0x80487d3
8048764:       e8 db fe ff ff          call   8048644 <_init+0x40>
8048769:       83 c4 10                addl   $0x10,%esp
/home/scz/src/objtest.c:9
        return 0;
804876c:       31 c0                   xorl   %eax,%eax
804876e:       eb 04                   jmp    8048774 
8048770:       31 c0                   xorl   %eax,%eax
8048772:       eb 00                   jmp    8048774 
/home/scz/src/objtest.c:10
}
8048774:       c9                      leave 
8048775:       c3                      ret    
8048776:       90                      nop

如果说上面还不够清楚,可以用下面的命令辅助一下:

objdump -j .text -Sl objtest --prefix-addresses | more
objdump -j .text -Dl objtest | more

用以上不同的命令去试会得到惊喜!

猜你喜欢

转载自www.cnblogs.com/marklove/p/10851275.html