0%

先看下示例代码

1
2
3
4
5
6
7
8
// class2_define.h

#pragma once
#include <cstdio>
class Class2 {
public:
void fun() { printf("Class2\n"); }
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// test.cpp

#include <cstdio>

#include "class2_define.h"
class Class1 {
public:
void fun() { printf("test::Class2\n"); }
};
void test()
{
printf("test start \n");
Class1 c1{};
c1.fun();
Class2 c2{};
c2.fun();
}
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
// main.cpp

#include <cstdio>

#include "class2_define.h"
class Class1 {
public:
void fun() { printf("main::Class1\n"); }
};
void test();
/**
* 输出:
*
main start
main::Class1
Class2
test start
main::Class1
Class2
* @return
*/
int main() {
printf("main start \n");
Class1 c1{};
c1.fun();
Class2 c2{};
c2.fun();
test();
}

问题

  • Class2 很正常, 类的实现inline到.h中了, 所以输出是一致的
  • Class1 重复定义了, 且func函数输出不一致, 但是能通过编译, 输出的都是main中的定义

分析

  • c++在编译时 每个编译单元(.o/.obj)都是独立编译的
    • 所有的.h会直接展开, 生成.i后, 再一起编译.s最终生成.o
  • 所以Class2在test.cpp.o和main.cpp.o中,都有一份汇编代码
    • 这么来说对翻译.s/.o的汇编器来说 Class1 和 Class2其实是一样的, 它都不知道其他.o中是否有重复定义
  • 但是这么做, 会导致二进制大小翻倍, 所以在ld过程中, ld对同一个符号, 只会保留一个定义
    • 这里main.cpp.o先链接, 采用首先找到就退出的方式, 所以全部链接main中的, test中的被丢弃了

编译/静态/动态 链接 相同符号的区别

  • 结论: 同符号函数, 只有编译直接链接非inline的函数时, 会报错, 否则使用第一个找到的
  • 编译链接:
    • 同符号的变量/函数会报错, 他们都是强符号
    • 同符号的函数, 如果被申明为inline的, 不会报错, 使用找到的第一个
    • 但是同符号的类方法, 不会报错, 类方法原地实现默认就是inline的
  • 静态链接:
    • 类似inline链接, 使用找到的第一个
      • 一些多版本依赖问题就是这个造成的
      • libA 依赖libX的v1版本, libB 依赖libX的v2版本, 最终链接后, 会定到一个版本中去, 导致数据结构和函数对不上
  • 动态链接
    • 类似静态链接, 相同符号找到第一个就退出
    • 利用这种机制, 我们可以覆盖一些libc函数

汇编分析

  • 反汇编在最后面
  • Class1::fun mangling 后的符号是: _ZN6Class23funEv
  • 可以看到_ZN6Class23funEv在main.cpp.o 和test.cpp.o中都有定义
  • 但是到了最终可执行文件中, 就只有一个了
  • 注意: data/rodata/bss段声明的数据不会被裁减, 即使完全没用到,
    • 可以看到test中声明的字符串, 在最终链接结果中还在
    • 可执行文件大小比较敏感时需要留意
1
2
3
4
5
6
7
8
9
10
11
12
# objdump -s -j .rodata ../../bin/src_cpp_language_class_link > rodata.txt


../../bin/src_cpp_language_class_link: file format elf64-x86-64

Contents of section .rodata:
2000 01000200 436c6173 7332006d 61696e3a ....Class2.main:
2010 3a436c61 73733100 6d61696e 20737461 :Class1.main sta
2020 72742000 436c6173 73320074 6573743a rt .Class2.test:
2030 3a436c61 73733200 74657374 20737461 :Class2.test sta
2040 72742000 rt .

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

main.cpp.o: file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <main>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 10 sub $0x10,%rsp
c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
13: 00 00
15: 48 89 45 f8 mov %rax,-0x8(%rbp)
19: 31 c0 xor %eax,%eax
1b: 48 8d 45 f6 lea -0xa(%rbp),%rax
1f: 48 89 c7 mov %rax,%rdi
22: e8 00 00 00 00 call 27 <main+0x27>
27: 48 8d 45 f7 lea -0x9(%rbp),%rax
2b: 48 89 c7 mov %rax,%rdi
2e: e8 00 00 00 00 call 33 <main+0x33>
33: e8 00 00 00 00 call 38 <main+0x38>
38: b8 00 00 00 00 mov $0x0,%eax
3d: 48 8b 55 f8 mov -0x8(%rbp),%rdx
41: 64 48 2b 14 25 28 00 sub %fs:0x28,%rdx
48: 00 00
4a: 74 05 je 51 <main+0x51>
4c: e8 00 00 00 00 call 51 <main+0x51>
51: c9 leave
52: c3 ret

Disassembly of section .text._ZN6Class23funEv:

0000000000000000 <_ZN6Class23funEv>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 10 sub $0x10,%rsp
c: 48 89 7d f8 mov %rdi,-0x8(%rbp)
10: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 17 <_ZN6Class23funEv+0x17>
17: 48 89 c7 mov %rax,%rdi
1a: e8 00 00 00 00 call 1f <_ZN6Class23funEv+0x1f>
1f: 90 nop
20: c9 leave
21: c3 ret

Disassembly of section .text._ZN6Class13funEv:

0000000000000000 <_ZN6Class13funEv>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 10 sub $0x10,%rsp
c: 48 89 7d f8 mov %rdi,-0x8(%rbp)
10: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 17 <_ZN6Class13funEv+0x17>
17: 48 89 c7 mov %rax,%rdi
1a: e8 00 00 00 00 call 1f <_ZN6Class13funEv+0x1f>
1f: 90 nop
20: c9 leave
21: c3 ret

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

test.cpp.o: file format elf64-x86-64


Disassembly of section .text:

0000000000000000 <_Z4testv>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 10 sub $0x10,%rsp
c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
13: 00 00
15: 48 89 45 f8 mov %rax,-0x8(%rbp)
19: 31 c0 xor %eax,%eax
1b: 48 8d 45 f7 lea -0x9(%rbp),%rax
1f: 48 89 c7 mov %rax,%rdi
22: e8 00 00 00 00 call 27 <_Z4testv+0x27>
27: 90 nop
28: 48 8b 45 f8 mov -0x8(%rbp),%rax
2c: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax
33: 00 00
35: 74 05 je 3c <_Z4testv+0x3c>
37: e8 00 00 00 00 call 3c <_Z4testv+0x3c>
3c: c9 leave
3d: c3 ret

Disassembly of section .text._ZN6Class13funEv:

0000000000000000 <_ZN6Class13funEv>:
0: f3 0f 1e fa endbr64
4: 55 push %rbp
5: 48 89 e5 mov %rsp,%rbp
8: 48 83 ec 10 sub $0x10,%rsp
c: 48 89 7d f8 mov %rdi,-0x8(%rbp)
10: 48 8d 05 00 00 00 00 lea 0x0(%rip),%rax # 17 <_ZN6Class13funEv+0x17>
17: 48 89 c7 mov %rax,%rdi
1a: e8 00 00 00 00 call 1f <_ZN6Class13funEv+0x1f>
1f: 90 nop
20: c9 leave
21: c3 ret

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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213

../../bin/src_cpp_language_class_link: file format elf64-x86-64


Disassembly of section .init:

0000000000001000 <_init>:
1000: f3 0f 1e fa endbr64
1004: 48 83 ec 08 sub $0x8,%rsp
1008: 48 8b 05 d9 2f 00 00 mov 0x2fd9(%rip),%rax # 3fe8 <__gmon_start__@Base>
100f: 48 85 c0 test %rax,%rax
1012: 74 02 je 1016 <_init+0x16>
1014: ff d0 call *%rax
1016: 48 83 c4 08 add $0x8,%rsp
101a: c3 ret

Disassembly of section .plt:

0000000000001020 <.plt>:
1020: ff 35 92 2f 00 00 push 0x2f92(%rip) # 3fb8 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: f2 ff 25 93 2f 00 00 bnd jmp *0x2f93(%rip) # 3fc0 <_GLOBAL_OFFSET_TABLE_+0x10>
102d: 0f 1f 00 nopl (%rax)
1030: f3 0f 1e fa endbr64
1034: 68 00 00 00 00 push $0x0
1039: f2 e9 e1 ff ff ff bnd jmp 1020 <_init+0x20>
103f: 90 nop
1040: f3 0f 1e fa endbr64
1044: 68 01 00 00 00 push $0x1
1049: f2 e9 d1 ff ff ff bnd jmp 1020 <_init+0x20>
104f: 90 nop

Disassembly of section .plt.got:

0000000000001050 <__cxa_finalize@plt>:
1050: f3 0f 1e fa endbr64
1054: f2 ff 25 9d 2f 00 00 bnd jmp *0x2f9d(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
105b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

Disassembly of section .plt.sec:

0000000000001060 <puts@plt>:
1060: f3 0f 1e fa endbr64
1064: f2 ff 25 5d 2f 00 00 bnd jmp *0x2f5d(%rip) # 3fc8 <puts@GLIBC_2.2.5>
106b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

0000000000001070 <__stack_chk_fail@plt>:
1070: f3 0f 1e fa endbr64
1074: f2 ff 25 55 2f 00 00 bnd jmp *0x2f55(%rip) # 3fd0 <__stack_chk_fail@GLIBC_2.4>
107b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

Disassembly of section .text:

0000000000001080 <_start>:
1080: f3 0f 1e fa endbr64
1084: 31 ed xor %ebp,%ebp
1086: 49 89 d1 mov %rdx,%r9
1089: 5e pop %rsi
108a: 48 89 e2 mov %rsp,%rdx
108d: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
1091: 50 push %rax
1092: 54 push %rsp
1093: 45 31 c0 xor %r8d,%r8d
1096: 31 c9 xor %ecx,%ecx
1098: 48 8d 3d ca 00 00 00 lea 0xca(%rip),%rdi # 1169 <main>
109f: ff 15 33 2f 00 00 call *0x2f33(%rip) # 3fd8 <__libc_start_main@GLIBC_2.34>
10a5: f4 hlt
10a6: 66 2e 0f 1f 84 00 00 cs nopw 0x0(%rax,%rax,1)
10ad: 00 00 00

00000000000010b0 <deregister_tm_clones>:
10b0: 48 8d 3d 59 2f 00 00 lea 0x2f59(%rip),%rdi # 4010 <__TMC_END__>
10b7: 48 8d 05 52 2f 00 00 lea 0x2f52(%rip),%rax # 4010 <__TMC_END__>
10be: 48 39 f8 cmp %rdi,%rax
10c1: 74 15 je 10d8 <deregister_tm_clones+0x28>
10c3: 48 8b 05 16 2f 00 00 mov 0x2f16(%rip),%rax # 3fe0 <_ITM_deregisterTMCloneTable@Base>
10ca: 48 85 c0 test %rax,%rax
10cd: 74 09 je 10d8 <deregister_tm_clones+0x28>
10cf: ff e0 jmp *%rax
10d1: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)
10d8: c3 ret
10d9: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)

00000000000010e0 <register_tm_clones>:
10e0: 48 8d 3d 29 2f 00 00 lea 0x2f29(%rip),%rdi # 4010 <__TMC_END__>
10e7: 48 8d 35 22 2f 00 00 lea 0x2f22(%rip),%rsi # 4010 <__TMC_END__>
10ee: 48 29 fe sub %rdi,%rsi
10f1: 48 89 f0 mov %rsi,%rax
10f4: 48 c1 ee 3f shr $0x3f,%rsi
10f8: 48 c1 f8 03 sar $0x3,%rax
10fc: 48 01 c6 add %rax,%rsi
10ff: 48 d1 fe sar %rsi
1102: 74 14 je 1118 <register_tm_clones+0x38>
1104: 48 8b 05 e5 2e 00 00 mov 0x2ee5(%rip),%rax # 3ff0 <_ITM_registerTMCloneTable@Base>
110b: 48 85 c0 test %rax,%rax
110e: 74 08 je 1118 <register_tm_clones+0x38>
1110: ff e0 jmp *%rax
1112: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
1118: c3 ret
1119: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)

0000000000001120 <__do_global_dtors_aux>:
1120: f3 0f 1e fa endbr64
1124: 80 3d e5 2e 00 00 00 cmpb $0x0,0x2ee5(%rip) # 4010 <__TMC_END__>
112b: 75 2b jne 1158 <__do_global_dtors_aux+0x38>
112d: 55 push %rbp
112e: 48 83 3d c2 2e 00 00 cmpq $0x0,0x2ec2(%rip) # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
1135: 00
1136: 48 89 e5 mov %rsp,%rbp
1139: 74 0c je 1147 <__do_global_dtors_aux+0x27>
113b: 48 8b 3d c6 2e 00 00 mov 0x2ec6(%rip),%rdi # 4008 <__dso_handle>
1142: e8 09 ff ff ff call 1050 <__cxa_finalize@plt>
1147: e8 64 ff ff ff call 10b0 <deregister_tm_clones>
114c: c6 05 bd 2e 00 00 01 movb $0x1,0x2ebd(%rip) # 4010 <__TMC_END__>
1153: 5d pop %rbp
1154: c3 ret
1155: 0f 1f 00 nopl (%rax)
1158: c3 ret
1159: 0f 1f 80 00 00 00 00 nopl 0x0(%rax)

0000000000001160 <frame_dummy>:
1160: f3 0f 1e fa endbr64
1164: e9 77 ff ff ff jmp 10e0 <register_tm_clones>

0000000000001169 <main>:
1169: f3 0f 1e fa endbr64
116d: 55 push %rbp
116e: 48 89 e5 mov %rsp,%rbp
1171: 48 83 ec 10 sub $0x10,%rsp
1175: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
117c: 00 00
117e: 48 89 45 f8 mov %rax,-0x8(%rbp)
1182: 31 c0 xor %eax,%eax
1184: 48 8d 05 8d 0e 00 00 lea 0xe8d(%rip),%rax # 2018 <_IO_stdin_used+0x18>
118b: 48 89 c7 mov %rax,%rdi
118e: e8 cd fe ff ff call 1060 <puts@plt>
1193: 48 8d 45 f6 lea -0xa(%rbp),%rax
1197: 48 89 c7 mov %rax,%rdi
119a: e8 4f 00 00 00 call 11ee <_ZN6Class13funEv>
119f: 48 8d 45 f7 lea -0x9(%rbp),%rax
11a3: 48 89 c7 mov %rax,%rdi
11a6: e8 21 00 00 00 call 11cc <_ZN6Class23funEv>
11ab: e8 60 00 00 00 call 1210 <_Z4testv>
11b0: b8 00 00 00 00 mov $0x0,%eax
11b5: 48 8b 55 f8 mov -0x8(%rbp),%rdx
11b9: 64 48 2b 14 25 28 00 sub %fs:0x28,%rdx
11c0: 00 00
11c2: 74 05 je 11c9 <main+0x60>
11c4: e8 a7 fe ff ff call 1070 <__stack_chk_fail@plt>
11c9: c9 leave
11ca: c3 ret
11cb: 90 nop

00000000000011cc <_ZN6Class23funEv>:
11cc: f3 0f 1e fa endbr64
11d0: 55 push %rbp
11d1: 48 89 e5 mov %rsp,%rbp
11d4: 48 83 ec 10 sub $0x10,%rsp
11d8: 48 89 7d f8 mov %rdi,-0x8(%rbp)
11dc: 48 8d 05 21 0e 00 00 lea 0xe21(%rip),%rax # 2004 <_IO_stdin_used+0x4>
11e3: 48 89 c7 mov %rax,%rdi
11e6: e8 75 fe ff ff call 1060 <puts@plt>
11eb: 90 nop
11ec: c9 leave
11ed: c3 ret

00000000000011ee <_ZN6Class13funEv>:
11ee: f3 0f 1e fa endbr64
11f2: 55 push %rbp
11f3: 48 89 e5 mov %rsp,%rbp
11f6: 48 83 ec 10 sub $0x10,%rsp
11fa: 48 89 7d f8 mov %rdi,-0x8(%rbp)
11fe: 48 8d 05 06 0e 00 00 lea 0xe06(%rip),%rax # 200b <_IO_stdin_used+0xb>
1205: 48 89 c7 mov %rax,%rdi
1208: e8 53 fe ff ff call 1060 <puts@plt>
120d: 90 nop
120e: c9 leave
120f: c3 ret

0000000000001210 <_Z4testv>:
1210: f3 0f 1e fa endbr64
1214: 55 push %rbp
1215: 48 89 e5 mov %rsp,%rbp
1218: 48 83 ec 10 sub $0x10,%rsp
121c: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax
1223: 00 00
1225: 48 89 45 f8 mov %rax,-0x8(%rbp)
1229: 31 c0 xor %eax,%eax
122b: 48 8d 05 06 0e 00 00 lea 0xe06(%rip),%rax # 2038 <_IO_stdin_used+0x38>
1232: 48 89 c7 mov %rax,%rdi
1235: e8 26 fe ff ff call 1060 <puts@plt>
123a: 48 8d 45 f6 lea -0xa(%rbp),%rax
123e: 48 89 c7 mov %rax,%rdi
1241: e8 a8 ff ff ff call 11ee <_ZN6Class13funEv>
1246: 48 8d 45 f7 lea -0x9(%rbp),%rax
124a: 48 89 c7 mov %rax,%rdi
124d: e8 7a ff ff ff call 11cc <_ZN6Class23funEv>
1252: 90 nop
1253: 48 8b 45 f8 mov -0x8(%rbp),%rax
1257: 64 48 2b 04 25 28 00 sub %fs:0x28,%rax
125e: 00 00
1260: 74 05 je 1267 <_Z4testv+0x57>
1262: e8 09 fe ff ff call 1070 <__stack_chk_fail@plt>
1267: c9 leave
1268: c3 ret

Disassembly of section .fini:

000000000000126c <_fini>:
126c: f3 0f 1e fa endbr64
1270: 48 83 ec 08 sub $0x8,%rsp
1274: 48 83 c4 08 add $0x8,%rsp
1278: c3 ret

介绍

  1. myrepos可以快速操作一批仓库, 不限于git, 且非常方便扩展
  2. 比起submodule来, 仓库间更加独立, 适合一些关系比较低的仓库
  3. 可以在某个子仓库下使用, 只操作当前仓库

工具地址

  1. http://myrepos.branchable.com/
  2. git://myrepos.branchable.com/
  3. *unix系列可以直接用包管理器安装, 搜索myrepos一般就可以了
  4. windows首先需要perl环境, 然后需要克隆库, 并把bin目录放在环境变量里面就行

eg:

  1. mr co 克隆所有
  2. mr up 更新所有
  3. mr status

批量注册

  1. ls |xargs -I {} sh -c 'cd {} && pwd && mr reg && cd -'

我的扩展后的实例

  • .mrconfig
  • 扩展了一些submodule的操作
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
[DEFAULT]
# gc
git_gc = git gc "$@"
# job num
#jobs = 8
# test cmd
git_fileLs = ls

# git submodule operators
git_subInit = git submodule update --init --recursive
git_subClean = git submodule foreach --recursive "git reset --hard HEAD && git clean -fd"
git_subBranch = git submodule foreach --recursive "git checkout $@"
git_subUpdate = git submodule foreach --recursive "git pull"
git_subRCmd = git submodule foreach --recursive "$@"

# change git local config for user and email
# eg: mr user puzzzzzzle 2359173906@qq.com
git_user = echo "$@"
git config user.name "$1"
git config user.email "$2"
echo name:
git config user.name
echo email:
git config user.email
# change git local config for user and email, include all submodule
# eg: mr ruser puzzzzzzle 2359173906@qq.com
git_ruser = echo "$@"
git config user.name "$1"
git config user.email "$2"
echo name:
git config user.name
echo email:
git config user.email
git submodule foreach --recursive "echo $@ && git config user.name $1 && git config user.email $2"

## your repos

[cpp_study]
checkout = git clone --recursive 'git@github.com:puzzzzzzle/cpp_study.git' 'cpp_study'


实现shell参数优先级:

  • 输入 > 环境变量 > 默认
  • 效果
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
#!/usr/bin/env bash
## 参数 1:set_name 2:branch 3:upload_para
## 参数优先级: 输入 > 环境变量 > 默认
## 环境变量与参数名同名

# 初始化参数
function do_update() {
# 转义空格
var=`echo $2 |sed 's/[ ]/\\\\ /g'`
echo export $1=${var}
eval export $1=${var}
}
# para : name, default, shell_input
function update_para() {
echo
echo "updata_para: $1 $2 $3"
# 命令输入
if [ -n "$3" ]; then
echo "set by input: $1 : $3"
do_update $1 $3
return
fi
# 环境变量
env_var="${!1}"
if [ -n "${env_var}" ]; then
echo "set by env: $1 : ${env_var}"
return
fi
# 默认值
echo "set by default: $1 : $2"
do_update $1 "$2"
}

# eg:
# para1: 如果第一个参数有输入, 就使用输入的, 如果环境变量中有一个名为para1的变量, 就是用环境变量中的, 否则 使用 默认值 "default_value"
update_para para1 "default_value" $1

echo
echo
# 使用变量从环境变量中取
echo para1 is ${para1}

标准md的图片格式

  • 但是,md时纯文本, 图片必须存储在正确的相对位置上
  • 可以使用图床, 把相对路径图片改为网络资源, 但是图床稳定性较差, 我更希望所有的图片都保存在post中, 离线也可以查看/编辑
  • 所有的md应该是标准的md格式

hexo 使用标准md图片的方式

  • _config.yml添加:
    • post_asset_folder: true
    • hexo new 会在source/_posts中生成同名文件夹, 将图片资源放在post_name中, 就可以用相对路径使用了.
  • md预览也没问题

无法在首页预览的问题

标签插件语法引用

  • {% asset_img image.png %}
    • 使用这种方式替代md的图片显示方式, 可以做到在首页显示
    • 但是md又无法预览了

最终解决方案

  • 我们还是使用标准md语法
  • hexo generate用正则表达式实现替换
  • generate 结束后 时还原文件(git)
  • 当然需要检查当前work dir git clean, 不然还原会导致丢文件

实现

  • 由于我不熟悉js, 就用python+shell写了
  • 在 根目录下添加
  • post_fix_tools.py
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import enum
import sys
from pathlib import Path
import os
import re

posts = "source/_posts"

valid_pic_suffix = ["png", "jpg", "jpeg", "gif", "svg"]


def check_is_pic(pic_file_name):
suffix = Path(pic_file_name).suffix.lower()
if suffix is None or suffix == "":
return False
return suffix[1:] in valid_pic_suffix


class OperateType(enum.Enum):
# 在所有图片后面, 插入一行 asset 标记
ADD_ASSET = 1
# 删除 所有 asset 标记
REM_ASSET = 2
# 用 asset 标记, 替代 md 图片, 只用于打包, 目前还没做还原, 得用Git 还原
REPLACE_BY_ASSERT = 3


def fix_one_post(file, operate_type):
new_lines = []
with open(file, encoding="utf-8") as f:
old_lines = f.readlines()
for i in range(len(old_lines)):
l = old_lines[i]
# 忽略资源行, 直接添加
if l.startswith("["):
new_lines.append(l)
continue
# 忽略 asset 行, 按需添加
if l.strip().startswith("{%"):
continue
if operate_type == OperateType.REM_ASSET:
new_lines.append(l)
else:
# {% asset_img pic.png%}
re_obj = re.search(r"(.*)!\[(.*)](.*)\((.*)\)(.*)", l)
if re_obj is not None:
groups = re_obj.groups()
# print(f"{re_obj} --- {re_obj.groups()}")
assert len(groups) == 5
pic_file_path = groups[3]
assert check_is_pic(pic_file_path)
# {% asset_img pic.png This is an example image %}
pic_file_name = str(Path(pic_file_path).name)
# 添加 asset 行时, 保留原有行
if operate_type == OperateType.ADD_ASSET:
new_lines.append(l)
asset_line = f"{{% asset_img {pic_file_name}%}}\n"
new_lines.append(asset_line)
else:
# 否则, 忽略原有行, 用asset 替代
asset_line = f"{groups[0]}{{% asset_img {pic_file_name}%}}{groups[4]}\n"
new_lines.append(asset_line)
else:
# 正则匹配失败的行直接添加
new_lines.append(l)
with open(file, "w", encoding="utf-8") as f:
f.writelines(new_lines)


def main():
argv = sys.argv
operate_type = OperateType.ADD_ASSET
if argv[1] == "add":
operate_type = OperateType.ADD_ASSET
elif argv[1] == "rem":
operate_type = OperateType.REM_ASSET
elif argv[1] == "replace":
operate_type = OperateType.REPLACE_BY_ASSERT
else:
raise RuntimeError(f"wrong arguments {argv}")
for path, dir_list, file_list in os.walk(posts):
for file_name in file_list:
if file_name.lower().endswith(".md"):
real_name = os.path.join(path, file_name)
fix_one_post(real_name, operate_type)
pass


if __name__ == '__main__':
main()

  • for *nix:
  • build.sh
1
2
3
4
5
6
7
8
9
10
#!/bin/bash
if [[ -z $(git status -s) ]]; then
echo 'clean work space, will build'
python3 post_fix_tools.py replace
hexo g
git reset --hard HEAD
else
echo 'dirty , not work'
fi

  • for win:
  • build.bat
1
2
3
4
5
6
7
8
@echo  off
for /f "delims=*" %%i in ('git status -s') do (
echo dirty , not work :
echo %%i
goto :EOF
)
echo clean work space, will build
cmd /k "python3 post_fix_tools.py replace && hexo g && git reset --hard HEAD"

使用

  • 在我们用标准的md写完后, 首先提交git, 保持仓库clean
  • 使用 build.sh/build.bat 替代 hexo g
    • 会临时把所有post的图片修改为 asset 格式, 再掉哟给你hexo g, 最后用 git reset 还原
  • 剩下的没啥区别

工具

  • censys
    • 官方说法: Censys是一款搜索引擎,它允许计算机科学家了解组成互联网的设备和网络。Censys由因特网范围扫描驱动,它使得研究人员能够找到特定的主机,并能够针将设备、网站和证书的配置和部署信息创建到一个总体报告中
    • 总之, 它可以扫描整个可达的ip4互联网, 病提供搜索
    • 类似的还有shodan

方法

  • jetbrains 对各种机构通过licence server的方式提供许可,licence server 通过http请求传输数据
  • 肯定有些server会暴露到外网上
  • 可以利用搜索工具检索关键字

实施

  • 利用 censys 搜索 account.jetbrains.com
    • 更精准: services.http.response.headers.location: account.jetbrains.com/fls-auth
  • 在结果中尝试几个, 注意观察, 开放的端口是302/200的
  • 注意:有些服务不是用的80端口, 留意下account所在的端口
  • 这样就蹭到了 莫斯科工程物理学院 的许可证
  • 一般这种公网IP不怎么变, 变了再找一个就行 0V0

留意端口

按照文件行数排序

find . -iregex ".*\.h\|.*\.hpp\|.*\.c\|.*\.cpp" -type f | xargs -I {} wc -l {} |sort -n -r

idea部分替换

  • 贪婪模式
    • hello\((.*)\,.*\)
    • $1 表示上面被括号包起来的部分,类似sed, $0 表示全部, $1 表示第一个
  • 非贪婪模式
    • 使用?表示这次匹配是非贪婪的, 采用最小匹配原则
    • hello\((.*)?\,.*\)

协程总览

什么是协程

协程, 又称微线程, 纤程. 英文名Coroutine.

协程十分表现上十分类似线程, 在线程的基础之上通过分时复用的方式运行多个协程, 协程的切换在用户态完成, 切换的代价比线程从用户态到内核态的代价小很多.

理论上, 有栈协程可以实现c级别任意位置yield出, 但是实际使用上

类似的设计思路

状态机

有栈协程

有栈协程的汇编原理

独享栈协程

共享栈协程

无栈协程

生成器(generator)模式

Python,c++20,rust,c#,js等新兴语言

对称与非对称

说明

  • 支持: 已有文件, 剪切板最后一个图片
  • 成功: 直接复制md lable格式的base64值到剪切板了, 没有弹窗
  • 失败: 弹窗报错原因
  • 代码地址: 传送门
  • 注意: 目前github的markdown解析器还不支持

安装和使用

安装

  • 依赖: Python3 已经安装
  • 执行根目录下的reg.bat 即可
  • 移动目录后需要重新安装

转化已有文件为base64格式到剪切板

file_from_img

转化剪切板最后一个图片为base64到剪切板

clip_from

markdown 中添加base64图片

1
2
3
4
![name][win_args]


[win_args]:data:image/jpeg;base64,xxxxxxxxxxx

原理

winreg:注册表操作右键菜单

1
2
3
4
5
6
7
8
9
10
11
12
13
# 打开名称父键
key = reg.OpenKey(reg_root_key_path, reg_key_path)
# 为key创建一个名称为menu_name的sub_key,并设置sub_key的值为menu_name加上快捷键,数据类型为REG_SZ字符串类型
reg.SetValue(key, menu_name, reg.REG_SZ, menu_name + f'(&{shortcut_key})')

# 打开刚刚创建的名为menu_name的sub_key
sub_key = reg.OpenKey(key, menu_name)
# 为sub_key添加名为'command'的子键,并设置其值为command 数据类型为REG_SZ字符串类型
reg.SetValue(sub_key, 'command', reg.REG_SZ, command)

# 关闭sub_key和key
reg.CloseKey(sub_key)
reg.CloseKey(key)
  • 指定的位置下创建key, 并指定可执行文件地址, 就可以添加右键菜单了
  • 常用地址说明
    • win_dir
  • 参数说明
    • win_args

pyperclip3 操作剪贴板

  • 从剪贴板获取图片
1
2
3
4
5
6
7
8
image = ImageGrab.grabclipboard()
if not isinstance(image, Image.Image):
# tkinter.messagebox.showinfo("picture2base64", "not a image in clipboard.")
logger.info("not a image in clipboard.")
raise Exception("not a image in clipboard.")
img_buffer = BytesIO()
image.save(img_buffer, format=picture_format, optimize=True, quality=40)
byte_data = img_buffer.getvalue()
  • 复制到剪贴板
1
2
msg = '[image]:data:image/' + picture_format + ';base64,' + base64_str
pyperclip3.copy(msg)

问题

  1. gcc编译器会做很多优化, c++规范规定空对象大小不能为0, sizeof返回值也必须大于0
  2. gcc中对空对象强制保留1byte
  3. 那么, 空对象构成的数组会做优化吗?

验证

1
2
3
4
5
6
7
8
9
10
11
12
struct Empty {};
void to_leaf()
{
}
/*
0x0000000000405098 <+0>: push %rbp
0x0000000000405099 <+1>: mov %rsp,%rbp
0x000000000040509c <+4>: sub $0x70,%rsp
0x00000000004050a0 <+8>: callq 0x405092 <to_leaf()>
=> 0x00000000004050a5 <+13>: leaveq
0x00000000004050a6 <+14>: retq
*/
  1. 只有非叶子函数才会真的执行开辟栈空间, 我们使用 一个空调用把使用栈的函数强转为非叶子函数
  2. 可以看到 Empty arr[100]分配了112个byte, 说明这里确实没有做处理, 不过也是合理的
  3. 至于为啥是112, 这就是涉及栈对齐了,对比下面这个函数就知道了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void test() {
Empty arr[100]{};
to_leaf();
}
/*
0x00000000004050a7 <+0>: push %rbp
0x00000000004050a8 <+1>: mov %rsp,%rbp
0x00000000004050ab <+4>: sub $0x70,%rsp
0x00000000004050af <+8>: callq 0x405092 <to_leaf()>
=> 0x00000000004050b4 <+13>: leaveq
0x00000000004050b5 <+14>: retq
*/

void test_1() {
Empty arr[112]{};
to_leaf();
}

结论: 即使是空对象构成的数组, 每个元素也会占用1byte, 不会优化掉

空函数

1
void void_ret_func() { return; }
1
2
3
4
5
6
void_ret_func:
pushq %rbp
movq %rsp, %rbp
nop
popq %rbp
ret

返回int, 栈上分配一个数据

1
2
3
4
int ret_int_func() {
int i = 42;
return i;
}
1
2
3
4
5
6
7
8
9
ret_int_func:
pushq %rbp
movq %rsp, %rbp

movl $42, -4(%rbp) # 直接向rbp 指针向下4 的位置写入数值, 没有挪动rsp , 目测是因为这个函数是叶子函数, 不需要
movl -4(%rbp), %eax # 写入返回值

popq %rbp # 没有挪动rsp, 随意不需要 movq %rbp, %rsp
ret

传入int

1
void int_arg_func(int arg) {}
1
2
3
4
5
6
7
8
9
10
int_arg_func:
pushq %rbp
movq %rsp, %rbp

// 从di寄存器获取参数, 放到栈上, 所以对于c传参不用栈而用寄存机加速的优化优化了个寂寞? 刚进函数就要全部在栈上保存一遍?
// int 只有4个字节, 使用edi就行, 不用rdi
movl %edi, -4(%rbp)

popq %rbp
ret

返回 小的结构体

1
2
3
4
SmallStruct return_SmallStruct() {
SmallStruct data{};
return data;
}
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
return_SmallStruct:
pushq %rbp
movq %rsp, %rbp

// 分配栈空间
// 这里和面的叶子函数不一样
// 前面只是用栈空间, 没有改rsp
// 这里同时改了rsp指针
// 因为 struct 的构造, 在c++中也是要调用构造函数的, 这也是一次隐式的函数调用
// 这会导致当前函数不是叶子函数
subq $16, %rsp

// 初始化栈变量为0
movl $0, -16(%rbp)

// 计算栈上的结构体的地址, 放在 rax 中
leaq -16(%rbp), %rax

// 准备调用构造函数的参数
// 在汇编中, 没有所谓的对象
// 构造函数只是一个第一个参数以结构体指针的普通函数
// 根据调用规则, 第一个参数放在rdi上
movq %rax, %rdi
// 本地没有什么药保存的寄存器, 直接调用构造函数
// call 相当于 pushq %rip; jmp _ZN11SmallStructC1Ev
// 当然, 这其中还有 FAR Near 跳转, 不同的寻址方式等隐藏在底层了, 这里只是一个简化
// 待定 : far 时 同时修改 CS IP, 我记得再64位下 CS 默认一直填0了
// 压栈下一条指令, 然后强制跳转
call _ZN11SmallStructC1Ev

// 准备返回值
// 对于较小的对象, 直接放在 rax中返回
movl -16(%rbp), %eax
// leave 相当于 movq %rbp, %rsp; popq %rbp;
// 先恢复 rsp 寄存器 为分配栈 前的状态, 然后恢复rbp寄存器
// 非叶子函数由于修改过rsp, 所以需要恢复
leave
// 返回 相当于 popq %rip
// 待定 : far 时 同时修改 CS IP, 我记得再64位下 CS 默认一直填0了
ret

返回大结构体

  1. 对于在函数中声明一个结构体, 初始化后直接返回的, 现代编译器可以做到0次拷贝, 放心的用
  2. 对于如下代码:
1
2
3
4
5
6
7
8
9
OperatorLogClass get_obj()
{
OperatorLogClass obj{};
return obj;
}
int main(int argc, char **argv) {
auto obj = get_obj();
return 0;
}
  1. 在现代编译器下会优化为(伪代码, 有些只能用汇编实现):
1
2
3
4
5
6
7
8
9
10
11
OperatorLogClass * get_obj(OperatorLogClass * inObj)
{
memset(inObj,sizeof(OperatorLogClass0,0) // 初始化内存
OperatorLogClass::OperatorLogClass(inObj) // 调用构造函数
return inObj;
}
int main(int argc, char **argv)
{
OperatorLogClass obj; // 不做任何初始化, 栈内存只是分配, 给下面的函数初始化
get_obj(&obj);
}
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
return_BigStruct:
pushq %rbp
movq %rsp, %rbp

// 分配栈
// 注意, 在这个栈上并没有直接分配结构体, 而是直接使用调用方传入的地址
// 编译器在这里优化掉了栈上的分配
subq $16, %rsp

// 返回的大结构体是通过在上层调用函数栈上分配空间, 向下传入地址实现的
// 保存返回结构体的地址
movq %rdi, -8(%rbp)

// 把 返回结构体 地址存储在 rsi 中
movq -8(%rbp), %rax
movq %rax, %rsi

// 初始化数据为0
movl $0, %eax
movl $10, %edx
movq %rsi, %rdi // 把返回结构体地址存储在rdi中
movq %rdx, %rcx
// rep 重复当前指令, 次数为rcx指定的次数, 当前为 $10, %edx 10次 , 共 8*10 = 80 byte
// stos 将eax寄存器(当前为0) 传送到 rdi 指定的内存中, 即: 返回值所在的地址处
// 这样就完成了将目标结构体所有值置为0的操作
rep stosq

// 调用构造函数
movq -8(%rbp), %rax
movq %rax, %rdi
call _ZN9BigStructC1Ev

nop
// 写入返回值, 这里返回了上层栈给到的地址
movq -8(%rbp), %rax
leave
ret

调用返回大结构体的函数

1
2
3
4
void test_func()
{
auto obj = return_BigStruct();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
test_func:
pushq %rbp
movq %rsp, %rbp

// 在父栈上申请空间
subq $80, %rsp

// 把父栈上的地址作为参数传入
leaq -80(%rbp), %rax
movq %rax, %rdi
// 调用函数
call return_BigStruct

leave
ret

大结构体作为参数拷贝输入

  1. 其实是在调用方分配了两份数据, 一份作为参数传入
1
2
3
4
5
6
7
8
void input_BigStruct_1(BigStruct st) {
st.value_1 = 1;
}
void test_func_1()
{
BigStruct obj{};
input_BigStruct_1(obj);
}
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
input_BigStruct_1:
pushq %rbp
movq %rsp, %rbp

// rbp 向上就是参数
movq $1, 16(%rbp)

popq %rbp
ret

test_func_1:
pushq %rbp
movq %rsp, %rbp
// 分配栈空间
subq $160, %rsp


// 初始化第一份数据
leaq -80(%rbp), %rsi
movl $0, %eax
movl $10, %edx
movq %rsi, %rdi
movq %rdx, %rcx
rep stosq
leaq -80(%rbp), %rax
movq %rax, %rdi
call _ZN9BigStructC1Ev

// 复制第二份数据
movq -80(%rbp), %rax
movq %rax, (%rsp)
movq -72(%rbp), %rax
movq %rax, 8(%rsp)
movq -64(%rbp), %rax
movq %rax, 16(%rsp)
movq -56(%rbp), %rax
movq %rax, 24(%rsp)
movq -48(%rbp), %rax
movq %rax, 32(%rsp)
movq -40(%rbp), %rax
movq %rax, 40(%rsp)
movq -32(%rbp), %rax
movq %rax, 48(%rsp)
movq -24(%rbp), %rax
movq %rax, 56(%rsp)
movq -16(%rbp), %rax
movq %rax, 64(%rsp)
movq -8(%rbp), %rax
movq %rax, 72(%rsp)

// 直接调用, 即: 参数放在栈上传入了, 上面那份复制的直接用于参数
call input_BigStruct_1

leave
ret