Skip to content

Instantly share code, notes, and snippets.

@ksyx
Created December 10, 2021 20:14
Show Gist options
  • Save ksyx/d0a190e39df407c43fc1c1723833893e to your computer and use it in GitHub Desktop.
Save ksyx/d0a190e39df407c43fc1c1723833893e to your computer and use it in GitHub Desktop.
A toy that stores some global variables by writing the binary itself

Global Variable Storage

Background

VBA could store some data by simply putting those data into Excel cells or Powerpoint TextBoxes, which is kinda counterintuitive for a programming language to have persist value storage even after stopped running, though perfectly reasonable. Could we do similar thing in the magical C++? This heavily platform dependent toy (ELF + POSIX) achieves this.

Working principle

It simply utilizes the .data segment by either specifying some variables to be put there, or just utilize the fact that initialized values are put there. Some magical operations used:

  • The variables __data_start and edata provided by linker scripts
  • Read output of awk with popen

License

MIT License

#include <cstdio>
#include <string>
#include <vector>
#include <utility>
#include <iostream>
#include <tuple>
#include <fstream>
// #define DEBUGOUT
#define DATASTART __data_start
#define DATAEND edata
#define NOINITSTORE __attribute__((section(".data")))
extern char DATASTART;
extern char DATAEND;
static std::vector<std::tuple<char* /*ptr*/,size_t /*size*/,char* /*fileOffset*/>> preserveList;
NOINITSTORE int foo = 0, arr[10];
int bar = 456, foobar; char* baseAddr; char* startAddr = &DATASTART;char *endAddr = &DATAEND;
static std::string exeRelPath;
template<class T>
void preserveVal(T& val) {
char* addr = (char*)&val;
if(addr >= startAddr && addr < endAddr)
preserveList.push_back(std::make_tuple(addr, sizeof(val), addr - startAddr + baseAddr));
}
void listPreserve() {
#ifdef DEBUGOUT
for(auto x : preserveList)
printf("%x %d %x\n", std::get<0>(x), std::get<1>(x), std::get<2>(x));
#endif
}
void writePreserve(const char* fileName) {
std::fstream fs;
fs.open(fileName, std::ios::in | std::ios::out | std::ios::binary);
for(auto item : preserveList) {
fs.seekp((long)std::get<2>(item));
fs.write(std::get<0>(item),std::get<1>(item));
}
fs.close();
}
void initPreserve(char** &argv) {
exeRelPath = (std::string)(argv[0]);
#ifdef DEBUGOUT
printf("%s\n",argv[0]);
printf("%s\n", ("objdump -h " + exeRelPath + "| grep \"\\.data\" | awk '{print $6}'").c_str());
#endif
FILE* f = popen(("objdump -h " + exeRelPath + " | grep \"\\.data\" | awk '{print $6}'").c_str(),"r");
fscanf(f, "%x", &baseAddr);
pclose(f);
#ifdef DEBUGOUT
printf("%x\n", baseAddr);
#endif
}
void finalizePreserve() {
system(("cp " + exeRelPath + " " + exeRelPath + ".tmp").c_str());
writePreserve((exeRelPath+".tmp").c_str());
system(("rm -rf " + exeRelPath).c_str());
system(("cp " + exeRelPath + ".tmp" + " " + exeRelPath).c_str());
#ifdef DEBUGOUT
printf("%x %x %x %x\n",&DATASTART,&foo,&bar,&foobar);
#endif
}
int main(int argc, char* argv[]) {
printf("%d %d %d\n",foo,bar, arr[3]);
initPreserve(argv);
preserveVal(foo); preserveVal(bar); preserveVal(arr);
listPreserve();
foo++;bar+=2;
arr[3]+=5;
finalizePreserve();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment