zom*_*ega 2 c linux shared-libraries shared-objects
共享对象(.so 文件)的代码(也称为 .text 部分)通常在进程之间共享。你可以在这里阅读它。
我写了一个小例子,其中事情似乎表现不同。
简而言之:程序证明对共享对象的代码部分所做的更改不会影响程序的其他实例。因此,共享对象的代码部分不在进程之间共享。
长解释:该代码由一个加载 .so 文件(mylib.c)的程序(main.c)组成。
该库由一个返回整数的函数组成。该整数与机器指令一起存储在代码段中。如果您不相信整数存储在代码部分中,您可以运行objdump -d libmy.so.
程序加载库,然后修改共享对象代码部分中的整数。在此之前,程序运行mprotect()以避免分段错误。该值正在更改为当前时间戳。
现在,我运行该程序两次,延迟 2 秒,因此每个实例都将其自己的值写入 .so 代码部分。令人惊讶的是,第一个实例的值没有被第二个实例的值覆盖。
这怎么可能?
我仅在 x64 上测试了该程序。只需保存文件并运行 bash 脚本即可。
libmy.c:
int getval()
{
return 123;
}
Run Code Online (Sandbox Code Playgroud)
主要.c:
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
int getval();
void unprotect(uint64_t addr)
{
uint64_t pagesize = sysconf(_SC_PAGE_SIZE);
addr -= addr % pagesize;
if (mprotect((void*)addr, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC) != 0)
{
printf("mprotect failed\n");
exit(1);
}
}
void main()
{
for (int i = 0; i < 30; i++)
{
int* ip = (int*)((char*)&getval + i);
if (*ip == 123)
{
printf("found value at offset %i\n", i);
unprotect((uint64_t)ip);
unprotect((uint64_t)ip + 3);
*ip = (int)time(NULL);
printf("value changed successfully\n");
break;
}
}
while (1)
{
printf("getval() returns %i\n", getval());
sleep(1);
}
}
Run Code Online (Sandbox Code Playgroud)
运行.bash:
#!/bin/bash
set -e
gcc -shared -fPIC -o libmy.so libmy.c
gcc main.c -L. -lmy -Wl,-rpath . -o bin
./bin &
sleep 2
./bin
Run Code Online (Sandbox Code Playgroud)
共享对象使用该MAP_PRIVATE标志进行映射。这意味着内存最初在所有用户之间共享,但为每个页面设置了“写入时复制”标志。因此,如果一个进程修改了它们自己的副本的内存,则不会影响与同一目标文件链接的其他进程。
显然,这对于数据部分是必要的,因为每个进程都需要对此进行本地更改。但它也适用于文本部分,尽管默认情况下它是只读的。正如您所发现的,您可以覆盖只读标志,但由于它仍然是 COW,因此在写入时您会获得一个私有副本。
| 归档时间: |
|
| 查看次数: |
86 次 |
| 最近记录: |