Albert World


  • 首页

  • 归档

  • 标签

  • 分类

  • 工具

  • 关于

  • 搜索

对称加密、非对称加密、公钥、私钥究竟是个啥?

发表于 2020-06-07 | 更新于 2020-06-15 | 分类于 加密与认证 |
热度 ℃
  |   字数统计: 3,104(字)   |   阅读时长: 11(分)

前言

进入正题之前先唠叨几句,不久前听到一个名词叫——费曼学习法,核心思想就是用通俗的话语把复杂道理或技术讲清楚,如果你已经明白了这个方法的含义,那么我好像离成功又进了一步。其实这个方法一直在尝试使用,但是没想到它居然有个“洋气”的名字。

阅读全文 »

git在回退版本时HEAD\~和HEAD^的作用和区别

发表于 2020-05-30 | 更新于 2020-06-15 | 分类于 Git |
热度 ℃
  |   字数统计: 2,827(字)   |   阅读时长: 11(分)

前言

今天总结一个小知识点,虽然不难,但是对新手有很强的迷惑性,了解一下也挺好。我们在使用 Git 回退到版本的时候,可能见过这种写法 git reset --hard HEAD~,有时候也会遇到这种写法 git reset --hard HEAD^,这两个语句都是将代码库还原到上一个版本,但是只差了一个符号,他们究竟有什么区别呢?这里先给出结论:HEAD~ 和 HEAD^ 含义不同,功能一样!

阅读全文 »

配置Beyond Compare 4作为git mergetool来解决git merge命令导致的文件冲突

发表于 2020-05-21 | 更新于 2020-05-23 | 分类于 Git |
热度 ℃
  |   字数统计: 3,791(字)   |   阅读时长: 16(分)

前言

使用 git merge 命令合并代码的时候可能会产生文件冲突,产生这种冲突的根本原因是文件的同一处同时被多次修改,这种同时修改常体现的不同分支上,当多个分支修改了同一处代码,再合并代码的时候就会产生冲突,因为 git 程序也不知道我们想要保留哪一份修改,这时就需要我们手动修改产生冲突的文件。

当冲突内容很少的时候我们可以打开文本编辑器,找到 >>>>>>>>>>>>、=========== 和 <<<<<<<<<<<< 这三行字符包裹的内容就是需要解决冲突的部分,但是当冲突内容特别多时我们还是习惯于通过可视化的工具来处理,Beyond Compare 就是这样一款工具,可以用来比较不同的文本文件、表格文件,还可以比较文件夹内容,之前用着比较习惯,所以在处理 git 冲突的时候也想使用这个工具来做,通过查找技术文档发现了下面的方法。

解决方案

鉴于大家都比较急,查找问题时想要直接找到答案,所以我这里直接说明配置步骤,送给不求甚解的小伙伴,也方便今后我可以直接找到,不过配置之前还是要先看一下前提。

前提

  • 在 Windows 上安装了 git 客户端,可以执行 git 命令(废话!没装 git 怎么产生冲突的)
  • 安装了 Beyond Compare 4 这个软件,下载链接很多,自己找一个吧,实在找不到,那就放弃吧(找我要)

配置

首先找到 Beyond Compare 的安装路径,比如我的软件安装路径是 D:\mybc4\BComp.exe,然后在 git 命令行客户端中执行下面命令:

1
2
3
4
git config --global merge.tool bc4
git config --global mergetool.bc4.cmd "\"D:\\mybc4\\BComp.exe\" \"\$LOCAL\" \"\$REMOTE\" \"\$BASE\" \"\$MERGED\""
git config --global mergetool.bc4.trustExitCode true
git config --global mergetool.keepBackup false

至此,git mergetool 就配置完了,当下次冲突的时候,直接使用 git mergetool 命令就可以调用 Beyond Compare 解决冲突文件了,但是你不好奇,这些设置命令都是什么意思吗?为什么执行完这些命令就能调用 Beyond Compare 4 这个软件了,如果你感兴趣可以接下往下看一看。

Beyond Compare

这是一款强大的比较工具,前面提到它可以比较文本、比较表格、比较文件夹,但是它的能力不仅限于此,它甚至可以比较MP3、比较图片、比较注册表,我们的目的是调用它的比较功能,但是前提是这款软件允许你调用,如果它不给你提供接口,你就是想调用也得绕上八百个圈才可以。

这一点我们可以查询文档确定,文档是安装软件时自带的,名字为 BCompare.chm,如果找不到,安利你一个叫做 Everything 的软件,装上它以后,电脑中的一切东西都能搜索找到。

这个文档应该很容易找到的,与软件的可执行文件在同一目录,其实我们使用的比较工具应该是 BCompare.exe,但是为什么在配置 git mergetool 的是后用的是 BComp.exe 呢?这一点文档中有写:

BCompare.exe: This is the main application. Only one copy will run at a time, regardless of how many windows you have open. If you launch a second copy it will tell the existing copy to start a comparison and exit immediately.
BComp.exe: This is a Win32 GUI program. If launched from a version control system, it should work just fine. If launched from a console window, the console (or batch file) will not wait for it.

文档是英文的,但是比较容易理解,总的来说 BCompare.exe 是主程序,BComp.exe 用在版本控制工具中更加优秀,至于文档中提到的主程序只能启动一个副本的说明,我试了一下并不是这样的,但是这不是重点,根据文档建议,我们应该调用 BComp.exe 程序。

关于调用参数,文档中对于每种形式的比较也给出了说明,我们这里只列举两个文件和四个文件这两种参数,两个文件作为参数时常用来对比,我直接使用主程序对比文件就是这种形式,参数格式为 BCompare.exe "C:\Left File.ext" "C:\Right File.ext",但是使用时我常把文件直接拖拽到软件上进行比较。四个文件作为参数时常用来处理文件冲突,参数类型为 BCompare.exe C:\Left.ext C:\Right.ext C:\Center.ext C:\Output.ext,参数中文件的名字表明处理时的位置和作用,看下面这个图就明白了。

解决文件合并冲突1

从红框圈定的位置就可以发现和文件的对应关系了,最下面是最终的输出文件,也是我们可以手动修改的文件。

文件冲突及处理

产生冲突

先看一下 git 仓库的原始情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
albert@home-pc MINGW64 /d/gitstart (dev)
$ git status
On branch dev
Your branch is up to date with 'origin/dev'.

nothing to commit, working tree clean

albert@home-pc MINGW64 /d/gitstart (dev)
$ ls
README.md

albert@home-pc MINGW64 /d/gitstart (dev)
$ cat README.md
learn git branch command
m2
test checkout

在此基础上新建两个分支 dev1 和 dev2

1
2
3
4
5
6
7
8
9
10
11
12
13
albert@home-pc MINGW64 /d/gitstart (dev)
$ git checkout -b dev1
Switched to a new branch 'dev1'

albert@home-pc MINGW64 /d/gitstart (dev1)
$ git checkout -b dev2
Switched to a new branch 'dev2'

albert@home-pc MINGW64 /d/gitstart (dev2)
$ git branch | grep dev
dev
dev1
* dev2

在 dev2 分支上修改 README.md 文件后提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
albert@home-pc MINGW64 /d/gitstart (dev2)
$ echo "this is dev2 test">>README.md

albert@home-pc MINGW64 /d/gitstart (dev2)
$ git add README.md
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory

albert@home-pc MINGW64 /d/gitstart (dev2)
$ git commit -m"update readme at dev2"
[dev2 d8d80b7] update readme at dev2
1 file changed, 1 insertion(+)

albert@home-pc MINGW64 /d/gitstart (dev2)
$ cat README.md
learn git branch command
m2
test checkout
this is dev2 test

切换回 dev1 分支修改 README.md 文件后提交

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
albert@home-pc MINGW64 /d/gitstart (dev2)
$ git checkout dev1
Switched to branch 'dev1'

albert@home-pc MINGW64 /d/gitstart (dev1)
$ echo "this is dev1 test">>README.md

albert@home-pc MINGW64 /d/gitstart (dev1)
$ git add README.md
warning: LF will be replaced by CRLF in README.md.
The file will have its original line endings in your working directory
git com -
albert@home-pc MINGW64 /d/gitstart (dev1)
$ git commit -m"update readme at dev1"
[dev1 3136341] update readme at dev1
1 file changed, 1 insertion(+)

albert@home-pc MINGW64 /d/gitstart (dev1)
$ cat README.md
learn git branch command
m2
test checkout
this is dev1 test

这时在 dev1 分支上合并 dev2 分支上的修改就会产生冲突

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
albert@home-pc MINGW64 /d/gitstart (dev1)
$ git merge dev2
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.

albert@home-pc MINGW64 /d/gitstart (dev1|MERGING)
$ cat README.md
learn git branch command
m2
test checkout
<<<<<<< HEAD
this is dev1 test
=======
this is dev2 test
>>>>>>> dev2

albert@home-pc MINGW64 /d/gitstart (dev1|MERGING)
$ ls
README.md

冲突产生了,文档中同一位置被两个分支修改后合并导致的,内容里出现了 <<<、===、>>>,包裹的内容被分成了两部分,上面一部分是当前分支修改的,下面一部分是从 dev2 分支合并过来的,还要注意虽然产生了产生了冲突,但是目录中并没有产生其他多余的文件。

解决冲突

这样的冲突比较简单,我们只要使用文本工具删除不想要的内容,保存后 git add README.md,然后再 git commit 就完成了冲突的解决,但是因为配置了 git mergetool,我们可以用它来解决冲突,直接在命令行敲命令 git mergetool 就可以:

1
2
3
4
5
6
7
8
albert@home-pc MINGW64 /d/gitstart (dev1|MERGING)
$ git mergetool
Merging:
README.md

Normal merge conflict for 'README.md':
{local}: modified file
{remote}: modified file

这时光标不会退出,一闪一闪并且打开 BComp.exe 工具,截图如下:

解决文件合并冲突2

这时如果你打开 git 库所在目录会发现除了 README.md 还多了下面4个文件:

1
2
3
4
5
README.md
README_BACKUP_584.md
README_BASE_584.md
README_LOCAL_584.md
README_REMOTE_584.md

按照自己的实际情况修改最下面的文件,然后点击箭头所指的保存按钮,关闭 Beyond Compare,查询一下仓库状态

1
2
3
4
5
6
7
8
9
10
11
12
albert@home-pc MINGW64 /d/gitstart (dev1|MERGING)
$ git status
On branch dev1
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)

Changes to be committed:
modified: README.md

albert@home-pc MINGW64 /d/gitstart (dev1|MERGING)
$ ls
README.md

不但冲突文件没有了,还给我们自动执行 git add README.md 命令,我们只需要执行 git commit 就解决完了冲突。

1
2
3
4
5
6
7
8
9
10
11
12
albert@home-pc MINGW64 /d/gitstart (dev1|MERGING)
$ git commit
[dev1 b348ae6] Merge branch 'dev2' into dev1

albert@home-pc MINGW64 /d/gitstart (dev1)
$ git adog
* b348ae6 (HEAD -> dev1) Merge branch 'dev2' into dev1
|\
| * d8d80b7 (dev2) update readme at dev2
* | 3136341 update readme at dev1
|/
* 5f4181e (origin/dev, dev) add comments

工具配置的参数含义

回过头来再看看 git mergetool 的4句配置到底有什么用

1
2
3
4
git config --global merge.tool bc4
git config --global mergetool.bc4.cmd "\"D:\\mybc4\\BComp.exe\" \"\$LOCAL\" \"\$REMOTE\" \"\$BASE\" \"\$MERGED\""
git config --global mergetool.bc4.trustExitCode true
git config --global mergetool.keepBackup false

git config

首先你需要知道 git config 的作用,就是用来配置 git 的,加上了 --global 表示调整全局 git 配置,不加的话就是调整当前库的 git 配置。windows上的全局配置一般在 C:\Users\用户名\.gitconfig,如果你之前用过 git,一般会执行过 git config --global user.name xxx 对吧,这些命令都是来调整 git 配置的,打开这个 .gitconfig 你会看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[user]
name = albert
email = albert@163.com
[core]
autocrlf = true
[alias]
st = status
adog = "log --all --decorate --oneline --graph"
[merge]
tool = bc4
[mergetool "bc4"]
cmd = \"D:\\mybc4\\BComp.exe\" \"$LOCAL\" \"$REMOTE\" \"$BASE\" \"$MERGED\"
trustExitCode = true
[mergetool]
keepBackup = false

看看最后几行就是我们添加的4项配置,只不过到文件中变成了键值对的形式,经过测试后发现,这些属性最少两级,比如 user.name 、core.autocrlf,最多三级比如 mergetool.bc4.cmd、 mergetool.bc4.trustExitCode,如果级数再多会怎么办,你可以试试 git config --global a.b.c.d.e test,它最终也会被拆成三级如下

1
2
[a "b.c.d"]
e = test

git mergetool

这个需要查一下官方文档了,git mergetool --help 就能打开git官方文档,文档写得真不错,排版格式看着就很舒服。

文档提到添加 --tool-help 选项可以列举可以的合并工具,展示如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
albert@home-pc MINGW64 /d/gitstart (dev1)
$ git mergetool --tool-help
'git mergetool --tool=<tool>' may be set to one of the following:
vimdiff
vimdiff2
vimdiff3

user-defined:
bc4.cmd "D:\Program Files\Beyond Compare 4\BComp.exe" "$LOCAL" "$REMOTE" "$BASE" "$MERGED"

The following tools are valid, but not currently available:
araxis
bc
bc3
codecompare
deltawalker
diffmerge
diffuse
ecmerge
emerge
examdiff
guiffy
gvimdiff
gvimdiff2
gvimdiff3
kdiff3
meld
opendiff
p4merge
smerge
tkdiff
tortoisemerge
winmerge
xxdiff

Some of the tools listed above only work in a windowed
environment. If run in a terminal-only session, they will fail.

这一查才发现,原来 git mergetool 支持的工具有这么多,不过下面这些我都没安装,用一下上面列举的3个,试试 git mergetool --tool=vimdiff,果然打开了一个界面

解决文件合并冲突3

幸亏不如 Beyond Compare 好用,不然我不是白配置了,不过这些工具确实方便,都不需要配置,只要安装了参数中指定一下就可以用了,比如这个 bc3,我猜它是 Beyond Compare 3,只不过我安装的是 Beyond Compare 4 这个版本。

这些内置工具使用的前提是已经安装了,并且安装软件的目录放在了环境变量 Path 中,如果没有放在这个变量中需要通过 mergetool.<tool>.path 参数来配置,比如我把 Beyond Compare 3 安装在了 D 盘根目录,就可以设置 git config --global mergetool.bc3.path "D:\\"。

我们在可用工具中没有找到 Beyond Compare 4 为什么我们可以用呢?因为 git mergetool 命令还支持自定义合并解决冲突的工具,只要指定 mergetool.<tool>.cmd 就可以调用了,就像 git mergetool --tool-help 查询结果中提到的 user-defined: bc4.cmd "D:\Program Files\Beyond Compare 4\BComp.exe" "$LOCAL" "$REMOTE" "$BASE" "$MERGED",git mergetool 把 bc4 作为了一个等同于内置合并工具的软件。

再来看看这4句配置的含义:

1
2
3
4
git config --global merge.tool bc4
git config --global mergetool.bc4.cmd "\"D:\\mybc4\\BComp.exe\" \"\$LOCAL\" \"\$REMOTE\" \"\$BASE\" \"\$MERGED\""
git config --global mergetool.bc4.trustExitCode true
git config --global mergetool.keepBackup false

第一句 git config --global merge.tool bc4 是说把 git mergetool 的默认工具配置成 bc4,如果不指定默认工具在使用时就需要写成 git mergetool --tool=bc4 或者 git mergetool -t bc4 了,可是 bc4 是我们自己起的名字,根本就没有这个名字啊,接着往下看。

第二句 git config --global mergetool.bc4.cmd "\"D:\\mybc4\\BComp.exe\" \"\$LOCAL\" \"\$REMOTE\" \"\$BASE\" \"\$MERGED\"" 指定了工具 bc4 的调用路径和参数,后面的这4个参数都是 git mergetool 命令提供的,依次代表本地修改,被合并分支修改,两端未修改前版本文件,最终合并导出的文本文件。

第三句 git config --global mergetool.bc4.trustExitCode true, 设置为 true 表示信任软件的返回码,并依据返回码确定合并是否成功,如果设置成 false 就会在合并完成后问你是否解决完冲突,设置成 true 会方便很多。

第四句 git config --global mergetool.keepBackup false, 是指定在合并完成后删除备份文件 *.orig,这个文件会在调用 git mergetool 是产生 *.orig 备份文件,成功合并后自动删除就可以了。

思考

至此终于弄明白这个 git mergetool 是怎么工作的了,但是想这样一个问题,这个 <tool>.cmd 一定得调用冲突解决工具吗?如果你从头看到这里应该会明白,这里只是给用户提供了一个调用自定义工具的方式,至于你调用什么它是不关心的,你完全可以在 git mergetool 的时候让电脑关机,这些都是可以的,在你明白了原理以后,一切都变得简单了。

总结

  • Beyond Compare 是一款强大的比较工具,合理的使用可以有效的提升工作效率
  • git mergetool 内置了很多可以使用的合并工具,并且支持调用自定义的合并工具
  • git 的官方文档写得真的挺详细,有时间可以多看一看,你会发现很多有意思的功能
  • 急于解决问题时可以不求甚解,解决问题后最好可以明白其中的缘由,这其实就是一种进步

尽管科技很发达,但有些人一旦分开可能真的就是一生不见了

使用c++filt命令还原C++编译后的函数名

发表于 2020-05-16 | 更新于 2020-05-17 | 分类于 C/C++ |
热度 ℃
  |   字数统计: 2,511(字)   |   阅读时长: 12(分)

前言

这个命令功能单一,但是非常强大,可以用来还原C++编译后的函数名,为什么C++的函数名需要单独的命令来还原,因为他们看起来都是这样 _ZNK4Json5ValueixEPKc、这样 _Z41__static_initialization_and_destruction_0ii 或者这样的 _ZN6apsara5pangu15ScopedChunkInfoINS0_12RafChunkInfoEED1Ev,仅通过这一串字母很难知道原函数的名字是什么,参数类型就更难分析了,实际上C++在编译函数时有一套命名函数的规则,每种参数使用什么字母表示都是有约定的,但是通过学习这些约定来还原函数太麻烦了,还好有人编写了 c++filt 命令可以让我们直接得到编译前的函数名,真好……

C++编译后的函数名

C++ 编译后的函数名字非常古怪,相比而言 C 语言编译后的函数看起来就正常许多了,extern "C"、函数重载、name mangling 这些知识点都与 C++ 这个奇怪的函数名有些关系,extern "C" 的作用简而言之就是告诉编译器和链接器被“我”修饰的变量和函数需要按照 C 语言方式进行编译和链接,这样做是由于 C++ 支持函数重载,而 C 语言不支持,结果导致函数被 C++ 编译后在符号库中的名字和被 C语言编译后的名字是不一样的,程序编译和连接就会出现问题,此类问题一般出现在 C++ 代码调用 C 语言写的库函数的时候。

而 name mangling 就是实现 C++ 函数重载的一种技术或者叫做方式,要求同名的 C++ 函数参数个数不同或参数类型不同,如果只有返回值类型不同,那么两个函数被认为是相同的函数,无法成功通过编译。接下来我们就来看几个例子,看看 C++ 编译后的函数名有什么变化。

C++和C语言编译后的函数名对比

我们来写一段相同的代码,分别使用 gcc 和 g++ 进行编译,从代码到可执行文件需要经历“预处理、编译、汇编、链接”4个步骤,接下来为了看到编译后函数名的不同,我们只进行前两步,生成汇编代码,再来比较不同。

gcc编译simple.c文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// simple.c

int myadd(int a, int b)
{
return a + b;
}

int main()
{
int a = 110;
int b = 119;
int c = myadd(a, b);

return 0;
}

gcc simple.c -S 生成汇编代码文件simple.s内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
    .file   "simple.c"
.text
.globl myadd
.type myadd, @function
myadd:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size myadd, .-myadd
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $110, -12(%rbp)
movl $119, -8(%rbp)
movl -8(%rbp), %edx
movl -12(%rbp), %eax
movl %edx, %esi
movl %eax, %edi
call myadd
movl %eax, -4(%rbp)
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits

g++编译simple.cpp文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// simple.cpp

int myadd(int a, int b)
{
return a + b;
}

int main()
{
int a = 110;
int b = 119;
int c = myadd(a, b);

return 0;
}

g++ simple.cpp -S 生成汇编代码文件simple.s内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    .file   "simple.cpp"
.text
.globl _Z5myaddii
.type _Z5myaddii, @function
_Z5myaddii:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -20(%rbp)
movl %esi, -24(%rbp)
movl $0, -4(%rbp)
movl -20(%rbp), %edx
movl -24(%rbp), %eax
addl %edx, %eax
movl %eax, -4(%rbp)
movl -4(%rbp), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size _Z5myaddii, .-_Z5myaddii
.globl main
.type main, @function
main:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $110, -12(%rbp)
movl $119, -8(%rbp)
movl $0, -4(%rbp)
movl -8(%rbp), %edx
movl -12(%rbp), %eax
movl %edx, %esi
movl %eax, %edi
call _Z5myaddii
movl %eax, -4(%rbp)
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits

虽然只有几行代码,可是生成汇编文件之后变成了50多行,我们只需要关注 myadd() 这个函数编译之后变成了什么就可以了,汇编代码虽然不好读,但是查找一个函数名应该没问题的,对照着上面的代码我们发现,myadd() 这个函数通过 gcc 编译之后的函数名还是 myadd,而通过 g++ 编译之后的函数名变成了 _Z5myaddii,可以明显感觉到最后的两个字母 i 代表的是参数 int,使用 c++filt 命令还原如下:

1
2
$ c++filt _Z5myaddii
myadd(int, int)

C++函数重载编译后的函数名对比

我们还是在刚才的代码的基础上增加一个参数类型不同的 myadd 函数,修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int myadd(int a, int b)
{
return a + b;
}

float myadd(float a, float b)
{
return a + b;
}

int main()
{
int c = myadd(110, 119);
float d = myadd(52.0f, 13.14f);

return 0;
}

g++ simple.cpp -S 生成汇编代码文件simple.s内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
    .file   "simple.cpp"
.text
.globl _Z5myaddii
.type _Z5myaddii, @function
_Z5myaddii:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -4(%rbp), %edx
movl -8(%rbp), %eax
addl %edx, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size _Z5myaddii, .-_Z5myaddii
.globl _Z5myaddff
.type _Z5myaddff, @function
_Z5myaddff:
.LFB1:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movss %xmm0, -4(%rbp)
movss %xmm1, -8(%rbp)
movss -4(%rbp), %xmm0
addss -8(%rbp), %xmm0
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE1:
.size _Z5myaddff, .-_Z5myaddff
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl $119, %esi
movl $110, %edi
call _Z5myaddii
movl %eax, -8(%rbp)
movss .LC0(%rip), %xmm1
movss .LC1(%rip), %xmm0
call _Z5myaddff
movd %xmm0, %eax
movl %eax, -4(%rbp)
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
.section .rodata
.align 4
.LC0:
.long 1095908721
.align 4
.LC1:
.long 1112539136
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits

这次一共3个函数,生成的汇编代码更长,但是我们一眼就能看见汇编代码中包含 _Z5myaddii 和 _Z5myaddff 两个函数,这就是函数重载的产物,两个参数类型不同的同名函数编译之后生成了不同的名字,_Z5myaddff 函数末尾的两个 f 应该指的就是参数类型 float。

使用c++filt定位问题示例

c++filt的作用就是还原函数名字,它可以帮我们查找动态链接库中缺少的函数,还原崩溃堆栈中一大串的函数名字母等等,下面来看一个崩溃堆栈的例子,代码内容尽量简写,只为了说明问题,现实情况可能要复杂的多。

首先定义一个打印函数堆栈的函数,参考之前的总结《linux环境下C++代码打印函数堆栈调用情况》,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <execinfo.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <iostream>

void show_stack(int nSignal)
{
static const int MAX_STACK_FRAMES = 12;
void *pStack[MAX_STACK_FRAMES];
static char szStackInfo[1024 * MAX_STACK_FRAMES];

char ** pStackList = NULL;
int frames = backtrace(pStack, MAX_STACK_FRAMES);
pStackList = backtrace_symbols(pStack, frames);
if (NULL == pStackList)
return;

strcpy(szStackInfo, "stack traceback:\n");
for (int i = 0; i < frames; ++i)
{
if (NULL == pStackList[i])
break;

strncat(szStackInfo, pStackList[i], 1024);
strcat(szStackInfo, "\n");
}

std::cout << szStackInfo; // 输出到控制台,也可以打印到日志文件中
}

再写一段隐藏着崩溃问题的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <string>
class CTest
{
public:
const std::string& get_string() {return s;}
void set_string(const std::string& str) {s = str;}
private:
std::string s;
};

void foo(float z)
{
int *p = nullptr;
*p = 110;
std::cout << z;
}

void test(std::string str)
{
CTest* pTest = new CTest();
pTest->set_string("20200517");
const std::string& s = pTest->get_string();
delete pTest;

std::cout << str << std::endl;
if (s == "20200517") foo(13.14);
}

void func(int a, int b)
{
std::string s = std::to_string(a) + std::to_string(b);
test(s);
}

int main()
{
signal(SIGSEGV, show_stack);
func(250, 520);
return 0;
}

编译运行,果然崩溃了:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ g++ simple.cpp --std=c++11
$ ./a.out
stack traceback:
./a.out() [0x401aff]
/lib/x86_64-linux-gnu/libc.so.6(+0x354b0) [0x7fd5f98b54b0]
/lib/x86_64-linux-gnu/libc.so.6(+0x16eff6) [0x7fd5f99eeff6]
/usr/lib/x86_64-linux-gnu/libstdc++.so.6(_ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7compareEPKc+0x3a) [0x7fd5f9f9145a]
./a.out() [0x4022b6]
./a.out() [0x401d30]
./a.out() [0x401e27]
./a.out() [0x401ed8]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7fd5f98a0830]
./a.out() [0x4019f9]

这时崩溃的堆栈中发现了一个特别长的函数 _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7compareEPKc,使用 c++filt 命令来还原函数:

1
2
$ c++filt _ZNKSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEE7compareEPKc
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::compare(char const*) const

从函数名来看是一个与字符串相关的 compare 函数,查看代码发现是 s == "20200517" 这一句的问题,所以说能确切的知道函数名对我们查找问题来说还是挺有帮助的。

总结

  • c++filt 命令可以还原 C++ 为实现函数重载采用 name mangling 搞出来的奇奇怪怪的函数名
  • 注册信号回调函数方式:signal(SIGSEGV, show_stack);,SIGSEGV代表无效的内存引用
  • 注意 C 语言和 C++ 在编译后函数命名方式的不同,C 语言不支持严格意义的重载,C++支持

阳光、空气、水,这些真的是好东西,当你真的快要失去它们才意识的到的话就有些晚了…

汇编指令入门级整理

发表于 2020-05-09 | 更新于 2020-05-10 | 分类于 ASM |
热度 ℃
  |   字数统计: 2,803(字)   |   阅读时长: 10(分)

前言

我们大都是被高级语言惯坏了的一代,源源不断的新特性正在逐步添加到各类高级语言之中,汇编作为最接近机器指令的低级语言,已经很少被直接拿来写程序了,不过我还真的遇到了一个,那是之前的一个同事,因为在写代码时遇到了成员函数权限及可见性的问题,导致他无法正确调用想执行的函数,结果他就开始在 C++ 代码里嵌入汇编了,绕过了种种限制终于如愿以偿,但是读代码的我们傻眼了…

阅读全文 »

C++11在左值引用的基础上增加右值引用

发表于 2020-05-05 | 更新于 2020-05-05 | 分类于 C/C++ |
热度 ℃
  |   字数统计: 3,102(字)   |   阅读时长: 13(分)

前言

右值引用这个词是最开始是学习 easylogging++ 这个日志开源项目的时候遇到的,当时遇到 && 这样的写法先是一愣,还有这种写法?难道是引用的地址?结果查询资料才明白这叫做右值引用。

阅读全文 »

简单聊聊C/C++中的左值和右值

发表于 2020-04-24 | 更新于 2020-04-25 | 分类于 C/C++ |
热度 ℃
  |   字数统计: 2,788(字)   |   阅读时长: 10(分)

前言

为什么标题要写成简单聊聊,而不是写成什么“C++中左值与右值详解”或者现在很流行的“惊了!看了这一篇左值与右值讲解,他吊打了面试官”,其实带有详解这个词是需要勇气的,最起码要融会贯通之后才敢这么说吧,本来是学习右值引用的,结果涉及到了左值和右值,然后去了解他们历史发现也是有些混乱,操作中又经常涉及到运算符优先级,真是越学越乱了。

阅读全文 »

.bat批处理(九):替换带有等号=的字符串的子串

发表于 2020-04-18 | 更新于 2020-04-19 | 分类于 bat |
热度 ℃
  |   字数统计: 2,675(字)   |   阅读时长: 10(分)

前言

今天写这篇记录要解决的问题来源于最近一名读者的提问,之前写过一篇名为《.bat批处理(六):替换字符串中匹配的子串》的总结文章,结果有读者在评论区提问说,如果想要替换的子串中包含等号 =,那么就无法替换了,问有没有什么办法可以解决。遇到这个问题的第一感觉应该挺好处理的吧,如果批处理程序在替换操作中认为等号 = 比较特殊,那就加个转义字符应该就可以了,但事实却证明这种想法有些天真了。

阅读全文 »

C++11中的时间库std::chrono(引发关于时间的思考)

发表于 2020-04-08 | 更新于 2020-04-12 | 分类于 C/C++ |
热度 ℃
  |   字数统计: 4,650(字)   |   阅读时长: 19(分)

时间都去哪了?还没好好感受年轻就…


前言

时间是宝贵的,我们无时无刻不在和时间打交道,这个任务明天下班前截止,你点的外卖还有5分钟才能送到,那个程序已经运行了整整48个小时,既然时间和我们联系这么紧密,我们总要定义一些术语来描述它,像前面说到的明天下班前、5分钟、48个小时都是对时间的描述,程序代码构建的程序世界也需要定义一些术语来描述时间。

阅读全文 »

Win10通过带命令行的安全模式清除顽固的广告弹窗文件

发表于 2020-04-02 | 更新于 2020-04-06 | 分类于 windows |
热度 ℃
  |   字数统计: 2,008(字)   |   阅读时长: 7(分)

前言

最近电脑开机后偶尔会出现一个弹窗,这种广告弹窗见的多了也就麻木了,本来也没放在心上,随手一关就准备去做其他事情了,但是点击关闭按钮后这个广告弹窗居然还弹出了二次确认框,想想也忍了,毕竟广告商做半天就是为了让你多看几眼,当我用鼠标的光标接近这个确认按钮时,确认框消失了,整个广告页面居然还在!

阅读全文 »
1…141516…23
Albert Shi

Albert Shi

阳光总在风雨后,大雨过后是冰雹

225 日志
36 分类
539 标签
RSS
GitHub 简书 StackOverflow CSDN 知乎 墨天轮
Links
  • Web前端导航
  • 文字编码导航
  • 在线代码编译
  • 在线工具集合
  • 在线教程集合
© 2018 - 2023 Albert Shi
Hexo 强力驱动
主题 - NexT.Mist
Unless otherwise specified, this blog is licensed under a CC BY-NC-ND 4.0 International License.
 京公网安备11010102007022号     冀 ICP 备 2022029255 号 - 1.