程序员的自我修养
p247
共享库构造和析构函数
GCC提供共享库的构造函数,早函数声明时加上“__attribute__((constructor))”属性即可成为共享库构造函数,会在共享库被加载时执行,即在main函数之前执行。使用dlopen()打开时会在dlopen()返回前执行。
同样“__attribute__((destructor))”表示共享库析构函数,在main()函数返回后会被执行。如果使用dlclose()卸载共享库则会在dlclose()返回前执行。
如果有多个构造函数可以传一个优先级参数如:
void __attribute__((constructor(5))) init_function1(void);
void __attribute__((constructor(10))) init_function2(void);
优先级数字越小,优先级越高。析构函数则刚好相反。
共享库脚本
共享库不止是动态链接的ELF的共享文件(.so),还可以是符合格式的链接脚本文件。
例如可以把C运行库和数学库组成一个新的libfoo.so,内容可以如下:
GROUP { /lib/libc.so.6 /lib/libm.so.2 }
程序的内存布局
p284
32位系统中windows会将高地址的2GB分给内核,linux会将高地址的1GB分给内核,剩下的成为用户空间。
应用程序的地址空间有如下几个默认区:
- 栈 :用于维护函数调用的上下文,通常在用户控件的最高地址处分配,由高到低增长,大概几M大小。
- 堆 :用来容纳程序动态分配的内存,位于栈的下方,由低到高增长。一般没有固定存储区域,几百M大小往上。
- 可执行文件映像 :将可执行文件的内存读取或映射到这个区。
- 保留区 :对内存中收到保护而禁止访问的内存区域的总称。
linux下如果可执行文件以来其他共享库,那么系统就会为它在从0x40000000开始的地址分配响应的空间,并将共享库装入该空间。
和栈相关的寄存器
i386下栈顶由esp寄存器进行定位,esp指向栈顶。
由于栈的生长方向从上到下,亚展会使esp减小,弹栈会使esp增大。
ebp用于标识访问的参数地址,ebp又称为帧指针(Frame Pointer)
编译完的汇编中,在函数地址后跟着几个nop地址,可以插入用于跳转的指令,用此机制实现Hook