静态链接过程概述

现代的链接器,其静态链接分为两个步骤(两部链接):

一 空间和地址的分配
1 搜集各个输入文件的各个段的长度和特性,合并相似特性的段。
2 搜集各个输入文件的符号表,合并为全局符号表,并对符号的VMA(虚拟内存地址)进行确定。

二 符号解析和重定位
1 读取重定位表,在重定位表获得重定位入口的偏移,找到相关地址值的位置。
2 根据该地址值的指令类型,与该重定位入口的符号,在全局符号表找到VMA,,根据指令类型和VMA,修正这个地址值。

c++编译器的名称修饰

c++编译一个源文件为目标文件时,会将符号的名称修饰一下在存入目标文件的符号表,比如:

namespace sss{
 int a=10;   //__ZN3sss1aE  表示sss::a
 extern "C" int b=20;  //_b
};

_Z或__Z开头,然后如果是嵌套结构就接着跟一个字母N(nested),然后是长度前导的各层命名空间或类名,最后是长度前导的符号名,然后大写的E结尾,然后如果是函数的话,会是一个小写字母来表示返回值(比如i表示返回int,f表示返回float)。

用正则来表示规则为_ZN?(\d+\w+)+E[a-z],可以用binutils的c++flit逆向原函数名;微软编译器的话,dbghelp.h文件中声明了UnDecorateSymbolName函数来还原函数名。c++编译器在符号管理方面比c编译器设计的复杂,因为c++支持类,命名空间,继承和虚机制,特别还有重载。

可以用gcc来编译一个c文件,然后用g++来编译一个c++文件,在c++文件中使用extern “C”关键字来导出一个符号给c文件来使用,,然后用g++来链接这两个目标文件,如:

t1.c:


编译:gcc -c t1.c -o t1.o

t2.cpp:


编译:g++ -c t2.cpp -o t2.o

查看两个目标文件的符号表:
readelf -s t1.o
objdump -t t2.o

链接:
g++ t1.o t2.o -o test

使用gcc预编译,编译,汇编,链接

如helloworld.c

#include <stdio.h>
int main(int argc, char *argv[]){
	printf("hello world");
	return 0;
}

预处理:
gcc -E helloworld.c -o helloworld.i //处理宏,包含等预处理指令(这个是依靠-E参数,加上该参数只会输出预处理后代码文件)

编译:
gcc -S helloworld.i -o helloworld.s //生成汇编代码文件(这个是依靠.s后缀,如果是.s后缀就会生成汇编代码文件)

汇编:
as helloworld.s -o helloworld.o //生成目标文件
gcc -c helloworld.s -o helloworld.o //也可以用这个命令从汇编代码文件生成目标文件
gcc -c helloworld.c -o helloworld.o //直接从c源代码,自动执行预编译,编译,汇编直至生成目标文件

链接:
ld helloworld.o -o helloworld
gcc helloworld.o -o helloworld //可能过不去,当helloworld.c引用了静态库的头文件时,需要在链接时携带静态库

===============================

vim的键映射介绍

有五种映射存在
- 用于普通模式: 输入命令时。
- 用于可视模式: 可视区域高亮并输入命令时。
- 用于操作符等待模式: 操作符等待中 (“d”,”y”,”c” 等等之后)。
见下: |omap-info|。
- 用于插入模式: 也用于替换模式。
? 用于命令行模式: 输入 “:” 或 “/” 命令时。

下表是map绑定中,对应的模式代号。现在先了解一下,等看完之后再回过头看这个模式代号就会明白了。
字 符 模 式 ~
普通、可视、选择和操作符等待
n 普通
v 可视和选择
s 选择 (在可视模式下Ctrl+G进入)
x 可视
o 操作符等待
! 插入和命令行
i 插入
l 插入、命令行和 Lang-Arg 模式的 “:lmap” 映射
c 命令行

我主要讲解一下“n(普通模式)”下的两个绑定命令,等看完之后就对应的明白别的模式下的命令了。
适用于普通模式的映射命令主要有:
1. :map
[语法] :map {lhs} {rhs} |mapmode-nvo| *:map*
1.1 作用模式: n、v、o (普通、可视和选择、操作符等待)
1.2 命令格式:
:map {lhs} {rhs}
含义: 在:map作用的模式中把键系列 {lhs} 映射为 {rhs},{rhs}可进行映射扫描,也就是可递归映射。
1.3 举例:
:map td :tabnew .
含义:在其作用模式(普通、可视、操作符)下,输入td等价于输入 :tabnew . 。而普通模式下输入:tabnew . 就是打开当前目录
如果再定义绑定 :map ts td,就是指在其作用模式下输入ts等价于td,也就是打开当前目录。不过如果没有特殊需要,一般不建议递归映射。

2. :noremap
:moremap和:map命令相对,作用模式和命令格式都相同,只不过不允许再对{rhs}进行映射扫描,也就是{lhs}定义后的映射就是{rhs}的键序列,不会再对{rhs}键序列重新解释扫描。它一般用于重定义一个命令,当然如果:map不需要递归映射的话,建议试用:noremap
比如:
:noremap ts td
它的意思是在其作用模式下,输入ts就是输入td,但是和:map不同的是,此时td再不会做进一步扫描解释。虽然之前已经定义了td,但是不会对td再做扫描

3. :unmap
:unmap是对应取消:map绑定的{lhs},作用模式相同,命令格式 :unmap {lhs}。
例如:
:unmap td
就是取消在其作用模式中td的绑定,比如之前td被绑定为:tabnew .,此时此绑定消失。
4. :mapclear
:mapclear时对应取消所有:map绑定的,慎用!

5. :nmap
:nmap是:map的普通模式板,也就是说其绑定的键只作用于普通模式。
例如:
:nmap td :tabnew . 和 :map td :tabnew . 在普通模式下等效
6. :nnoremap
:nnorempa和:nmap的关系和:noremap和:map的关系一样,只是:nmap的非递归版
7. :nunmap
:nunmap和:nmap的关系和:unmap和:map的关系一样,取消:nmap的绑定。
8. :nmapclear
:nmapclear是对应取消所有:map绑定的,慎用!

看完以上,应该可以发现一个规律,前4个是一组,后4个时一组,后一组比前一组多一个n就是指只作用于普通模式。其中每组内*nore*是其对应的非递归版、*un*是取消绑定某个绑定、clear后缀是取消所有绑定。发现了这个规律,再翻到前面的模式代号表,你大体可以猜到vmap、xmap、smap、omap是什么意思了吧,以及相对应的nore版本、un版本、clear版本。

另外:
{rhs} 之前可能显示一个特殊字符:
* 表示它不可重映射
& 表示仅脚本的局部映射可以被重映射
@ 表示缓冲区的局部映射

到这一步你可以轻松的长吸一口气,因为相关的命令已经都了解了,记不住没关系,可以随时:help map一下。不过别急,后面还有map更多的选项等着去攻克。

键表 |key-notation|
小键盘 0 到 9 *keypad-0* *keypad-9*
Shift+键 *shift* * Control+键 *control* *ctrl* * Alt+键 或 meta+键 *meta* *alt* * * termcap 里的 “xx” 入口键

特殊参数:
1.
2.
3.
4.