You can create a shared library that does:
- In a constructor, based on environment variable
HEAPFILE
, does mmap
so heap
is R/W mapped to HEAPFILE
(Note: trickery involved, see code below)
- Target program will have
heap
setup [magically ;-)].
- When target program completes, shared library destructor will copy contents of [possibly modified]
heap
to the file HEAPOUT
Of course, these filenames could be program arguments and the target program could call the constructor and destructor functions explictly but you wanted the target program to be [largely] unaware of what data it's been given.
You can have many different target programs (i.e. programs that you want to "inject" data into). They only need to be compiled and linked once but many different data files can be used.
So, you can easily do: Test N programs with M data inputs.
Here is the shared library code. Note that although it has some error checking, and it works, it could be cleaned up a bit.
// libtst.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#if DEBUG
#define dbgprt(_fmt...) \
printf(_fmt)
#else
#define dbgprt(_fmt...) \
do { } while (0)
#endif
// exported to target program
char *heap; // pointer to heap
size_t heapsize; // bytes in heap
// private
static char *heap_file; // input file
static char *heap_ofile; // output file
static int heap_fd; // open file descriptor for heap_file
static struct stat heap_st; // result of stat for heap_file
void __attribute__((constructor))
fakeinit(void)
{
do {
// get input file name
heap_file = getenv("HEAPFILE");
if (heap_file == NULL)
break;
// get output file name
heap_ofile = getenv("HEAPOUT");
if (heap_ofile == NULL)
break;
// open input file
heap_fd = open(heap_file,O_RDONLY);
if (heap_fd < 0)
break;
// get input file size (and export this for target program)
if (fstat(heap_fd,&heap_st) < 0)
break;
heapsize = heap_st.st_size;
// map this for the target program
// NOTE: by using PROT_READ/PROT_WRITE target program will think this
// is an ordinary array, but MAP_PRIVATE will prevent original file
// from being altered
heap = mmap(NULL,heapsize,
PROT_READ | PROT_WRITE,
MAP_PRIVATE,
heap_fd,0);
if (heap == MAP_FAILED) {
dbgprt("fakeinit: FAILED\n");
heap = NULL;
break;
}
} while (0);
}
void __attribute__((destructor))
fakefini(void)
{
do {
// open the output file
int fdout = open(heap_ofile,O_RDWR | O_CREAT,0644);
if (fdout < 0)
break;
// enlarge output file to correct size
dbgprt("fakefini: heapsize=%zu\n",heapsize);
ftruncate(fdout,heapsize);
// map the output file R/W
char *outbuf = mmap(NULL,heapsize,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fdout,0);
if (outbuf == MAP_FAILED) {
dbgprt("fakefini: FAILED\n");
break;
}
// put data into output file
memcpy(outbuf,heap,heapsize);
// unmap/close output file
munmap(outbuf,heapsize);
close(fdout);
// unmap/close input file
munmap(heap,heapsize);
close(heap_fd);
} while (0);
}
Here is the sample program to run under test:
// test.c -- sample program to be run under test
#include <stddef.h>
// set by shared library
extern char *heap;
extern size_t heapsize;
int
main(void)
{
heap[0] += 1;
heap[1] += 1;
heap[2] += 1;
return 0;
}
Here is a build/run script:
#!/bin/bash
cc -o libtst.so -shared -fpic libtst.c -g -DDEBUG=1
cc -o test test.c ./libtst.so -g
echo "abc" > old
rm -f new
env HEAPFILE=old HEAPOUT=new ./test
head -1000 old new
Here is the output of sh -x ./build
:
+ cc -o libtst.so -shared -fpic libtst.c -g -DDEBUG=1
+ cc -o test test.c ./libtst.so -g
+ echo abc
+ rm -f new
+ env HEAPFILE=old HEAPOUT=new ./test
fakefini: heapsize=4
+ head -1000 old new
==> old <==
abc
==> new <==
bcd
Note that above links the program against the shared test library.
This is the easiest. But, it occurs to me, that you'd like the target program to be totally unaware of how heap
is initialized.
Although I didn't try it, it should be possible [with some slight modifications (e.g.) using ELF weak symbols/aliases] to change libtst.so
so that it force itself on the target programs using:
env LD_PRELOAD=./libtst.so ./test