第七章 动态链接
1. 链接时重定位(静态链接);装载时重定位(动态链接)
地址无关代码:PIC,Position-independent Code,把指令中那些需要修改的部分分离出来,跟数据部分放在一起,这样指令部分就可以保持不变,而数据部分可以再每个进程中拥有个副本。
指令跳转、调用 数据访问
模块内部 (1)相对跳转和调用 (2)相对地址访问
模块外部 (3)间接跳转和调用(GOT) (4)间接访问(GOT)
如果一个共享对象lib.so中定义了一个全局变量G,而进程A和B都使用了lib.so,那么当进程A改变这个全局变量G的值的时,进程B中G会受到影响么?
不会,因为当lib.so被两个进程加载时,它的数据段部分在每个进程中都有独立的副本,从这个角度看,共享对象中的全局变量实际上和定义在程序内部的全局变量没什么区别。如果是线程,则有影响。
2. 打印堆栈初始化信息
#include <stdio.h>
#include <elf.h>
int main(int argc, char* argv[])
{
int *p=(int *)argv;
int i;
Elf32_auxv_t* aux;
printf("Argument count: %d\n", *(p-1));
for(i=0; i<*(p-1); ++i)
{
printf("Argument %d: %s\n", i, *(p+i));
}
p+=i;
p++;
printf("Environment:\n");
while(*p)
{
printf("%s\n", *p);
p++;
}
p++;
printf("Auxiliary vectors:\n");
aux=(Elf32_auxv_t*)p;
while(aux->a_type!=AT_NULL)
{
printf("Type: %02d Value: %x\n", aux->a_type, aux->a_un.a_val);
aux++;
}
return 0;
}
3. 动态链接器本身不可以依赖于其他任何共享对象;其次动态链接器本身所需要的全局和静态变量的重定位由自己完成。(自举Bootstrap)
全局符号介入(Global Symbol Interpose),linux链接器,当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。
显示运行时链接(Explicit Run-time Linking),共享对象动态装载库的装载过程由链接器自动完成:打开动态库(dlopen)、查找符号(dlsym)、错误处理(dlerror)、关闭动态库(dlclose)。<dlfcn.h>
4. 运行时装载程序Runso(windows下由类似的rundll)
#include <stdio.h>#include <dlfcn.h>
#define SETUP_STACK \
i=2; \
while(++i<argc-1){ \
switch(argv[i][0]){ \
case 'i': \
asm volatile("push %0" :: \
"r"(atoi(&argv[i][1])) ); \
esp +=4; \
break; \
case 'd': \
atof(&argv[i][1]); \
asm volatile("subl $8, %esp\n" \
"fstpl (%esp)"); \
esp += 8; \
break; \
case 's': \
asm volatile("push %0" :: \
"r"(&argv[i][1])); \
esp += 4; \
break; \
default: \
printf("error argument type");\
goto exit_runso; \
} \
}
#define RESTORE_STACK \
asm volatile("add 50, %%esp"::"r"(esp))
int main(int argc, char *argv[])
{
void *handle;
char *error;
int i;
int esp=0;
void *func;
handle=dlopen(argv[1], RTLD_NOW);
if(handle==0)
{
printf("can't find library: %s\n", argv[1]);
return -1;
}
func=dlsym(handle , argv[2]);
if((error=dlerror())!=NULL)
{
printf("Find symbol %s error: %s\n", argv[2], error);
goto exit_runso;
}
switch(argv[argc-1][0])
{
case 'i':
{
int (*func_int)()=func;
SETUP_STACK;
int ret=fun_int();
RESTORE_STACK;
printf("ret=%d\n", ret);
break;
}
case 'd':
{
double (*func_double)()=func;
SETUP_STACK;
double ret=fun_double();
RESTORE_STACK;
printf("ret=%f\n", ret);
break;
}
case 's':
{
char* (*func_str)()=func;
SETUP_STACK;
char *ret=func_str();
RESTORE_STACK;
printf("ret=%s\n", ret);
break;
}
case 'v':
{
void (*fun_void)()=func;
SETUP_STACK;
func_void();
RESTORE_STACK;
printf("ret=void");
break;
}
}
exit_runso:
dlclose(handle);
}
本文探讨了动态链接的概念,包括地址无关代码(PIC)的作用、不同类型的指令和数据访问方式,以及共享对象如何在多个进程中独立工作。此外,还介绍了如何通过示例代码实现动态链接器的功能,如打开、关闭动态库,以及处理错误。
609

被折叠的 条评论
为什么被折叠?



