Спасибо, @Brian. Вот мой минимальный пример использования mmap, как он предлагает, с последующим размещением нового для использования этой памяти и затем mprotect, чтобы сделать ее доступной только для чтения:
#include <iostream>
#include <sys/mman.h>
#include <unistd.h>
using namespace std;
struct MySingleton
{
int some_value;
static MySingleton* init(int a_value)
{
// Get the system's page size.
const auto pagesize = getpagesize();
// mmap one page worth of memory, initially writable.
void* map = mmap(0, pagesize, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0);
// Use placement new using that memory.
MySingleton::_instance = new(map) MySingleton(a_value);
// Now make that memory read-only.
mprotect(map, pagesize, PROT_READ);
return MySingleton::_instance;
}
static MySingleton* instance()
{
return _instance;
}
private:
MySingleton(int a_value)
: some_value{a_value}
{
}
static MySingleton *_instance;
};
MySingleton *MySingleton::_instance = nullptr;
int
main(int argc, char* argv[])
{
MySingleton *instance = MySingleton::init(10);
// Read is OK.
cout << instance->some_value << endl;
// This should crash;
instance->some_value = 5;
cout << instance->some_value << endl;
return 0;
}
Когда я компилирую и запускаю это, я получаю cra sh что я желаю:
g++ -g -Wall -Werror -std=c++17 test.cc -o test
./test
10
runit: line 4: 18029 Bus error: 10 ./test
Отладчик указывает прямо на запись:
$ lldb test
(lldb) target create "test"
Current executable set to 'test' (x86_64).
(lldb) run
Process 18056 launched: '<snip>' (x86_64)
10
Process 18056 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=2, address=0x10011d000)
frame #0: 0x0000000100000c50 test`main(argc=1, argv=0x00007ffeefbff9a8) at test.cc:50:26
47 cout << instance->some_value << endl;
48
49 // This should crash;
-> 50 instance->some_value = 5;
51 cout << instance->some_value << endl;
52
53 return 0;
Target 0: (test) stopped.