共享对象(.so文件)的代码(也就是.text部分)通常在进程间共享。你可以here读取它。
我写了一个小例子,事情似乎表现得不同。
简而言之:程序证明对共享对象的代码部分所做的更改不会影响程序的其他示例。因此,共享对象的代码部分不会在进程之间共享。
详细说明:代码由一个程序(main. c)组成,它加载一个.so文件(mylib. c)。
该库由一个返回整数的函数组成。这个整数存储在机器指令的代码段中。如果你不相信这个整数存储在代码段中,你可以运行objdump -d libmy.so
。
程序加载库,然后修改共享对象的代码段中的整数。在此之前,程序运行mprotect()
以避免分段错误。值被更改为当前时间戳。
现在我运行程序两次,延迟2秒,所以每个示例都将自己的值写入.so代码段。令人惊讶的是,第一个示例的值没有被第二个示例的值覆盖。
这怎么可能?
我只在x64上测试过这个程序,只需保存文件并运行bash脚本。
libmy.c:
int getval()
{
return 123;
}
字符串
main.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.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
型
2条答案
按热度按时间31moq8wy1#
一个共享的对象被Map到
MAP_PRIVATE
标志。这意味着内存最初是在所有用户之间共享的,但是为每个页面设置了“写时复制”标志。因此,如果一个进程修改了内存,他们会得到自己的副本,它不会影响其他与同一个对象文件链接的进程。很明显,这对于数据段是必要的,因为每个进程都希望对它进行本地更改。但是对于文本段也是如此,尽管它默认是只读的。正如你所发现的,你可以覆盖只读标志,但是因为它仍然是COW,所以当你写入它时,你会得到一个私有副本。
hxzsmxv22#
简而言之:程序证明对共享对象的代码部分所做的更改不会影响程序的其他示例。因此,共享对象的代码部分不会在进程之间共享。
当然不是!!!!* 如果你修改了其他程序的
.text
段,在它不知情的情况下,它肯定会崩溃。想想在不同的用户之间共享一个共享库,并且被一个用户任意修改。这会产生什么安全漏洞!要使
.text
段可读写,您必须特别允许它。当然,绕过所有的安全隐患。通常情况下,如果您这样做,您的.text
段将被标记为 COPY ON WRITE,并且在您第一次更改它时将被复制,使其部分不再共享。