diff --git a/Makefile b/Makefile
index 89ce355..351d60b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,63 +1,93 @@
.RECIPEPREFIX:=>
-#TODO [set as required] TODO
+
+#[set as required]
+INSTALL_DIR=/usr/local/lib
+INCLUDE_INSTALL_DIR=/usr/local/include
+LD_DIR=/etc/ld.so.conf.d
+
CC=gcc
-CFLAGS=-ggdb -Wall -fPIC
-INCLUDE=-lcmore
+CFLAGS=
+CFLAGS_TEST=-ggdb3 -O0
+WARN_OPTS=-Wall -Wextra
+LDFLAGS=-lcmore
-LIB_DIR="./src/lib"
-TEST_DIR="./src/test"
-TGT_DIR="./src/tgt"
-SRC_DIR=$(shell pwd)/src
-BUILD_DIR=$(shell pwd)/build
-DOC_DIR=$(shell pwd)/doc
+#[build constants]
+LIB_DIR=./src/lib
+TEST_DIR=./src/test
+BUILD_DIR=${shell pwd}/build
+PACKAGE_DIR=./package
-INSTALL_DIR=/usr/local
-LD_DIR=/etc/ld.so.conf.d
+
+#[installation constants]
+SHARED=libmcry.so
+STATIC=libmcry.a
+HEADER=memcry.h
#[set build options]
ifeq ($(build),debug)
- CFLAGS += -O0
+ CFLAGS += -O0 -ggdb3 -fsanitize=address -DDEBUG
+ CFLAGS_TEST += -DDEBUG
+ LDFLAGS += -static-libasan
else
- CFLAGS += -O2
+ CFLAGS += -O3 -flto
endif
-#[process targets]
-all: lib test tgt
+#[set static analysis options]
+ifeq ($(fanalyzer),true)
+ CFLAGS += -fanalyzer
+endif
-install:
-> cp ${BUILD_DIR}/lib/liblain.so ${INSTALL_DIR}/lib
-> mkdir -pv ${INSTALL_DIR}/include
-> cp ${SRC_DIR}/lib/liblain.h ${INSTALL_DIR}/include
-> mkdir -pv ${INSTALL_DIR}/share/man
-> cp -R ${DOC_DIR}/roff/* ${INSTALL_DIR}/share/man
-> echo "${INSTALL_DIR}/lib" > ${LD_DIR}/90lain.conf
-> ldconfig
-install_doc:
-> mkdir -pv ${INSTALL_DIR}/share/doc/liblain
-> cp ${DOC_DIR}/md/* ${INSTALL_DIR}/share/doc/liblain
+#[process targets]
+.PHONY prepare:
+> mkdir -p ${BUILD_DIR}/test ${BUILD_DIR}/lib ${PACKAGE_DIR}
-uninstall:
-> rm -vf ${INSTALL_DIR}/lib/liblain.so ${INSTALL_DIR}/include/liblain.h \
- ${INSTALL_DIR}/share/man/man3/liblain_* ${INSTALL_DIR}/share/doc/liblain/*
-> rmdir ${INSTALL_DIR}/share/doc/liblain
-> rm ${LD_DIR}/90lain.conf
-> ldconfig
+test: shared
+> $(MAKE) -C ${TEST_DIR} tests CC='${CC}' _CFLAGS='${CFLAGS_TEST}' \
+ _WARN_OPTS='${WARN_OPTS}' \
+ BUILD_DIR='${BUILD_DIR}/test' \
+ LIB_BIN_DIR='${BUILD_DIR}/lib'
-tgt:
-> $(MAKE) -C ${TGT_DIR} tgt CC='${CC}' BUILD_DIR='${BUILD_DIR}'
+all: shared static
-test: lib
-> $(MAKE) -C ${TEST_DIR} test CC='${CC}' BUILD_DIR='${BUILD_DIR}'
+shared:
+> $(MAKE) -C ${LIB_DIR} shared CC='${CC}' _CFLAGS='${CFLAGS} -fPIC' \
+ _WARN_OPTS='${WARN_OPTS}' \
+ _LDFLAGS='${LDFLAGS}' \
+ BUILD_DIR='${BUILD_DIR}/lib'
-lib:
-> $(MAKE) -C ${LIB_DIR} lib CC='${CC}' CFLAGS='${CFLAGS}' INCLUDE='${INCLUDE}' BUILD_DIR='${BUILD_DIR}'
+static:
+> $(MAKE) -C ${LIB_DIR} static CC='${CC}' _CFLAGS='${CFLAGS}' \
+ _WARN_OPTS='${WARN_OPTS}' \
+ _LDFLAGS='${LDFLAGS}' \
+ BUILD_DIR='${BUILD_DIR}/lib'
clean:
-> $(MAKE) -C ${TEST_DIR} clean_all CC='${CC}' BUILD_DIR='${BUILD_DIR}'
-> $(MAKE) -C ${LIB_DIR} clean_all CC='${CC}' CFLAGS='${CFLAGS}' BUILD_DIR='${BUILD_DIR}'
-> ${MAKE} -C ${TGT_DIR} clean_all CC='${CC}' BUILD_DIR='${BUILD_DIR}'
+> $(MAKE) -C ${TEST_DIR} clean BUILD_DIR='${BUILD_DIR}/test'
+> $(MAKE) -C ${LIB_DIR} clean BUILD_DIR='${BUILD_DIR}/lib'
+> -rm ${PACKAGE_DIR}/*
+
+install:
+> mkdir -pv ${INSTALL_DIR}
+> cp -v ${BUILD_DIR}/lib/${SHARED} ${INSTALL_DIR}
+> cp -v ${BUILD_DIR}/lib/${STATIC} ${INSTALL_DIR}
+> mkdir -pv ${INCLUDE_INSTALL_DIR}
+> cp -v ${LIB_DIR}/${HEADER} ${INCLUDE_INSTALL_DIR}
+> echo "${INSTALL_DIR}" > ${LD_DIR}/90memcry.conf
+> ldconfig
+
+uninstall:
+> -rm -v ${INSTALL_DIR}/{${SHARED},${STATIC}}
+> -rm -v ${INCLUDE_INSTALL_DIR}/${HEADER}
+> -rm ${LD_DIR}/90memcry.conf
+> ldconfig
+
+package: all
+> -cp ${BUILD_DIR}/lib/${SHARED} ${PACKAGE_DIR}
+> -cp ${BUILD_DIR}/lib/${STATIC} ${PACKAGE_DIR}
+> -cp ${LIB_DIR}/memcry.h ${PACKAGE_DIR}
+> -tar cvjf ${PACKAGE_DIR}/memcry.tar.bz2 ${PACKAGE_DIR}/*
diff --git a/README.md b/README.md
index 8ee0b63..44a07e4 100644
--- a/README.md
+++ b/README.md
@@ -1,88 +1,165 @@
-# liblain
+# MemCry
-
+
+
### ABOUT:
-The Lain library (liblain) provides a programmatic interface to the memory and memory maps of processes on Linux. Liblain can be used for:
+**The MemCry Library provides**:
-- Code injection
-- Memory analysis
-- Anti-cheat bypass (see [lain.ko](https://github.com/vykt/lain.ko))
+- Graph-like data structure (*map*) for representing the memory map of a target process.
+- The ability to update the *map* as the target's memory mappings change without invalidating pointers to said map.
+- Tracking of (assumed) ownership of unnamed *vm_area*s.
+- Support for **multiple interfaces** for acquiring the memory maps, reading and writing memory.
+- Multiple convenient utilities.
-liblain offers both a procfs and a [lain.ko](https://github.com/vykt/lain.ko) LKM backend. Both interfaces provide identical functionality and are interchangable.
+**Planned**:
-liblain stores both virtual memory areas and backing objects in nodes traversable as lists or trees. The use of nodes for storage means the internal memory map can be updated without invalidating any pointers. This makes development of complex tools much easier.
+- An interface for operating on coredumps instead of live processes.
-In addition to a memory interface liblain also provides several utilities including:
+
+
+
-- Same method for resolving PID as ps/top.
-- Fast address -> VM area search.
+See the example below. Feel free to contact me on discord: *@vykt* or email: *vykt[at]disroot[dot]org*
----
### DEPENDENCIES:
-liblain requires [libcmore](https://github.com/vykt/libcmore).
+If you're not using a packaged release, you'll need to install:
+- [CMore](https://github.com/vykt/cmore) - Data structures for C.
-### INSTALLATION:
-Fetch the repo:
-```
-$ git clone https://github.com/vykt/liblain
-```
+### EXAMPLE:
-Build:
-```
-$ make lib
-```
+```c
+#include
+#include
+#include
-Install:
-```
-# make install
-```
+#include
+#include
-Install additional markdown documentation:
-```
-# make install_doc
-```
-To uninstall:
-```
-# make uninstall
-```
+int main() {
----
+ int ret;
+
-### LINKING:
+ /*
+ * First, find the PID of the target based on the target's name. You can
+ * optionally pass a pointer to an uninitialised CMore vector if you want
+ * to find PIDs of multiple processes with the same name.
+ */
+ pid_t pid;
+ pid = mc_pid_by_name("target_name", NULL);
-Ensure your linker searches for liblain in the install directory (adjust as required):
-```
-# echo "/usr/local/lib" > /etc/ld.so.conf.d/liblain.conf
-```
-Include `` in your sources:
-```
-#include
-```
+ /*
+ * Open a session on your target. For the procfs interface, this will
+ * open file descriptors on /proc/pid/{mem,maps}
+ */
+ mc_session s;
+ ret = mc_open(&s, PROCFS, pid);
+ if (ret != 0) {
+ /* on error, a perror() function is provided */
+ mc_perror("[error]");
+ }
-Ask your compiler to link liblain:
-```
-$ gcc -o test test.c -llain
-```
----
+ /*
+ * Read the target's memory map for the first time.
+ */
+ mc_vm_map m;
+ ret = mc_update_map(&s, &m);
+
+
+ /*
+ * Find the "libfoo.so" object in the target.
+ */
+ cm_lst_node * libfoo_node = NULL;
+ libfoo_node = mc_get_obj_node_by_basename(&m, "libfoo.so");
+ if (libfoo_node == NULL) {/*...*/}
+
+
+ /*
+ * Print libfoo.so's starting address.
+ */
+ mc_vm_obj * libfoo_obj = MC_GET_NODE_OBJ(libfoo_node);
+ printf("libfoo.so start addr: 0x%lx, end addr: 0x%lx\n",
+ libfoo_obj->start_addr, libfoo_obj->end_addr);
+
-### DOCUMENTATION:
+ /*
+ * Print the full path of the object after libfoo.so.
+ */
+ cm_lst_node * next_node = libfoo_node->next;
+ mc_vm_obj * next_obj = MC_GET_NODE_OBJ(next_node);
+ printf("after libfoo.so: %s\n", next_obj->pathname);
-See `./doc/md` for markdown documentation. After installing liblain the following manpages are available:
-| Manpage | Description |
-| --------------- | --------------------------- |
-| `liblain_error` | Error handling |
-| `liblain_map` | Memory map data structure |
-| `liblain_iface` | Using interfaces |
-| `liblain_util` | Utilities |
+ /*
+ * Get the first area of libfoo.so. The object of libfoo (libfoo_obj)
+ * stores pointers to area nodes.
+ */
+ cm_lst_node * area_node_p = libfoo_obj->vm_area_node_ps.head;
+ cm_lst_node * area_node = MC_GET_NODE_PTR(area_node_p);
+ mc_vm_area * area = MC_GET_NODE_AREA(area_node);
+ printf("is first area writable?: %d\n, area->access & MC_ACCESS_WRITE);
+
+
+ /*
+ * Get the next area and print its address range.
+ */
+ mc_vm_area * next_area = MC_GET_NODE_AREA(area_node->next);
+ printf("next area start addr: 0x%lx, end addr: 0x%lx\n",
+ next_area->start_addr, next_area->end_addr);
+
+
+ /*
+ * The target's (OS-wide) memory map may have been updated; we should update
+ * our local map.
+ */
+ ret = mc_update_map(&s, &m);
+ if (ret != 0) {/*...*/}
+
+
+ /*
+ * Check if libfoo.so is still mapped. If not, fetch the next mapped
+ * object. Even if libfoo.so and its constituent areas have been unmapped,
+ * their nodes and object pointers will remain valid.
+ */
+ cm_lst_node * iter_node;
+ mc_vm_obj * iter_obj;
+ if (libfoo_obj->mapped == false) {
+ iter_node = libfoo_node->next;
+ while (iter_node != m.vm_objs.head) {
+ iter_obj = MC_GET_NODE_OBJ(iter_node);
+ if (iter_obj->mapped == true) break;
+ }
+ }
+
+
+ /*
+ * Clean up unmapped objects & areas. This will cause `libfoo_node` and
+ * `libfoo_obj` pointers to become invalid.
+ */
+ ret = mc_map_clean_unmapped(&m);
+ if (ret != 0) {/*...*/}
+
+
+ /*
+ * Clean up and exit.
+ */
+ ret = mc_close(&s);
+ if (ret != 0) {/*...*/}
+
+ ret = mc_del_vm_map(&m);
+ if (ret != 0) {/*...*/}
+
+ return 0;
+}
+```
diff --git a/TESTING b/TESTING
new file mode 100644
index 0000000..09cab92
--- /dev/null
+++ b/TESTING
@@ -0,0 +1,84 @@
+[Summary]:
+
+ TL;DR: Inspect failing unit tests with GDB.
+
+ Build the unit tests with `make test [build=debug]`. This produces a
+ `test` executable in `$PROJROOT/build/test`. To run tests for a given
+ component, pass one (or more) of these flags:
+
+ -m : Core map data structure.
+ -p : `procfs` interface.
+ -k : `krncry` interface.
+ -n : Map utilities.
+ -u : Generic utilities.
+
+ When debugging with GDB (see whole document), use the `debug.sh`
+ script in `$PROJROOT/build/test/debug.sh`.
+
+
+[Unit tests & ASAN]:
+
+ Unit tests use the 'Check' library:
+
+ https://libcheck.github.io/check/
+
+ Check forks off new processes for each unit test. This can be undesirable
+ because debug builds of MemCry compile with GCC's address sanitizer,
+ which will fail to report memory leaks if they occur in a child process.
+
+ Check will not fork new processes if the environment's `CK_FORK`
+ variable is set to `no`.
+
+
+[GDB scripts (essential)]:
+
+ MemCry's datastructures are very tedious to navigate with raw gdb.
+ Instead of writing 50 character long casts, make use these gdb scripts
+ defined in `$PROJROOT/build/test/init.gdb`:
+
+ pmapa
+
+ Pretty print all areas inside `m`.
+
+ pmapo
+
+ Pretty print all objects of a map `m` and their constituent
+ areas.
+
+ pmapua
+
+ Pretty print all unmapped areas inside `m`
+
+ pmapuo
+
+ Pretty print all unmapped objects of a map `m` and their
+ constituent areas.
+
+ parean * n>
+
+ Dump a `mc_vm_area` held by a list node `cm_lst_node`.
+
+ pxarean * n>
+
+ Dump (hex) a `mc_vm_area` held by a list node `cm_lst_node`
+
+ pobjn * n>
+
+ Dump a `mc_vm_obj` held by a list node `cm_lst_node`.
+
+ pxobjn * n>
+
+ Dump (hex) a `mc_vm_obj` held by a list node `cm_lst_node`.
+
+ pnode * n>
+
+ Dump a nested list node held by another list node.
+
+ pnodea *> * n>
+
+ `mc_vm_obj` stores a list of pointers to its constituent area
+ nodes. Use `pnodea` to easily print areas in this list.
+
+ pxnodea *> * n>
+
+ See above.
diff --git a/TODO b/TODO
index 55e7e1b..7d9955c 100644
--- a/TODO
+++ b/TODO
@@ -1 +1,2 @@
+[features]:
- add 3rd interface that uses process_vm_readv/process_vm_writev
diff --git a/build/test/debug.sh b/build/test/debug.sh
new file mode 100755
index 0000000..b60e60d
--- /dev/null
+++ b/build/test/debug.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+gdb -x memcry.gdb --args ./test -p "$@"
diff --git a/build/test/memcry.gdb b/build/test/memcry.gdb
new file mode 100644
index 0000000..0b52b1e
--- /dev/null
+++ b/build/test/memcry.gdb
@@ -0,0 +1,337 @@
+# ask libcheck to not fork each unit test when GDB is attached
+set environment CK_FORK=no
+
+# print vm_area at a node
+define parean
+ if $argc != 1
+ printf "Use: parean <*area_node>\n"
+ else
+ p *((mc_vm_area *) ($arg0->data))
+ end
+end
+
+# hex print vm_area at a node
+define pxarean
+ if $argc != 1
+ printf "Use: parean <*area_node>\n"
+ else
+ p/x *((mc_vm_area *) ($arg0->data))
+ end
+end
+
+# print vm_obj at a node
+define pobjn
+ if $argc != 1
+ printf "Use: pobjn <*obj_node>\n"
+ else
+ p *((mc_vm_obj *) ($arg0->data))
+ end
+end
+
+# hex print vm_obj at a node
+define pxobjn
+ if $argc != 1
+ printf "Use: pobjn <*obj_node>\n"
+ else
+ p/x *((mc_vm_obj *) ($arg0->data))
+ end
+end
+
+# print a node at the specified node
+define pnode
+ if $argc != 1
+ printf "Use: pnode <*obj_area_node>\n"
+ else
+ p *(*((cm_lst_node **) ($arg0->data)))
+ end
+end
+
+# print the area at a node at the specified node:
+define pnodea
+ if $argc != 1
+ printf "Use: pnodea <*obj_area_node>\n"
+ else
+ p *((mc_vm_area *) ((*((cm_lst_node **) ($arg0->data)))->data))
+ end
+end
+
+# hex print the area at a node at the specified node:
+define pxnodea
+ if $argc != 1
+ printf "Use: pnodea <*obj_area_node>\n"
+ else
+ p/x *((mc_vm_area *) ((*((cm_lst_node **) ($arg0->data)))->data))
+ end
+end
+
+# print the object at a node at the specified node:
+define pnodeo
+ if $argc != 1
+ printf "Use: pnodea <*obj_area_node>\n"
+ else
+ p *((mc_vm_obj *) ((*((cm_lst_node **) ($arg0->data)))->data))
+ end
+end
+
+# hex print the object at a node at the specified node:
+define pxnodeo
+ if $argc != 1
+ printf "Use: pnodea <*obj_area_node>\n"
+ else
+ p/x *((mc_vm_obj *) ((*((cm_lst_node **) ($arg0->data)))->data))
+ end
+end
+
+# print areas of a map
+define pmapa
+ if $argc != 1
+ printf "Use: pmapa <*map>\n"
+ else
+ # print header
+ printf " --- [AREAS] ---\n"
+
+ # bootstrap iteration
+ set $iter = 0
+ set $iter_node = $arg0->vm_areas.head
+
+ # for every area
+ while $iter != $arg0->vm_areas.len
+
+ # fetch & typecast next area
+ set $area = ((mc_vm_area *) ($iter_node->data))
+
+ # fetch relevant entries for this area
+ set $id = $area->id
+ set $basename = $area->basename
+ set $start_addr = $area->start_addr
+ set $end_addr = $area->end_addr
+
+ # fetch area's object id if one is present
+ if $area->obj_node_p != 0
+ set $obj_id = ((mc_vm_obj *) ($area->obj_node_p->data))->id
+ else
+ set $obj_id = -1337
+ end
+
+ # fetch area's last object i if one is present
+ if $area->last_obj_node_p != 0
+ set $last_obj_id = ((mc_vm_obj *) ($area->last_obj_node_p->data))->id
+ else
+ set $last_obj_id = -1337
+ end
+
+ # print relevant entries of this area
+ printf "%-3d: 0x%-12lx - 0x%-12lx | obj: %-5d - last_obj: %-5d | \"%s\"\n", $id, $start_addr, $end_addr, $obj_id, $last_obj_id, $basename
+
+ # advance iteration
+ set $iter = $iter + 1
+ set $iter_node = $iter_node->next
+ end
+ end
+end
+
+# print unmapped areas of a map
+define pmapua
+ if $argc != 1
+ printf "Use: pmapua <*map>\n"
+ else
+ # print header
+ printf " --- [UNMAPPED AREAS] ---\n"
+
+ # bootstrap iteration
+ set $iter = 0
+ set $iter_node = $arg0->vm_areas_unmapped.head
+
+ # for every area
+ while $iter != $arg0->vm_areas_unmapped.len
+
+ # fetch & typecast next area
+ set $area = ((mc_vm_area *) ((*((cm_lst_node **) ($iter_node->data)))->data))
+
+ # fetch relevant entries for this area
+ set $id = $area->id
+ set $basename = $area->basename
+ set $start_addr = $area->start_addr
+ set $end_addr = $area->end_addr
+
+ # fetch area's object id if one is present
+ if $area->obj_node_p != 0
+ set $obj_id = ((mc_vm_obj *) ($area->obj_node_p->data))->id
+ else
+ set $obj_id = -1337
+ end
+
+ # fetch area's last object i if one is present
+ if $area->last_obj_node_p != 0
+ set $last_obj_id = ((mc_vm_obj *) ($area->last_obj_node_p->data))->id
+ else
+ set $last_obj_id = -1337
+ end
+
+ # print relevant entries of this area
+ printf "%-3d: 0x%-12lx - 0x%-12lx | obj: %-5d - last_obj: %-5d | \"%s\"\n", $id, $start_addr, $end_addr, $obj_id, $last_obj_id, $basename
+
+ # advance iteration
+ set $iter = $iter + 1
+ set $iter_node = $iter_node->next
+ end
+ end
+end
+
+# print objs of a map
+define pmapo
+ if $argc != 1
+ printf "Use: pmapo <*map>\n"
+ else
+ # print header
+ printf " --- [OBJS] ---\n"
+
+ # bootstrap iteration over objects
+ set $iter = 0
+ set $iter_node = $arg0->vm_objs.head
+
+ # for every object
+ while $iter != $arg0->vm_objs.len
+
+ # fetch & typecast next object
+ set $obj = ((mc_vm_obj *) ($iter_node->data))
+
+ # fetch relevant entries of this object
+ set $id = $obj->id
+ set $basename = $obj->basename
+ set $start_addr = $obj->start_addr
+ set $end_addr = $obj->end_addr
+
+ # print relevant entries of this object
+ printf "<%-3d: 0x%lx - 0x%lx | \"%s\">\n", $id, $start_addr, $end_addr, $basename
+
+ # setup iteration over areas belonging to this object
+ set $inner_iter = 0
+ set $inner_iter_node = ((mc_vm_obj *) ($iter_node->data))->vm_area_node_ps.head
+
+ # for every area that is part of this object
+ printf " [areas]:\n"
+ while $inner_iter != ((mc_vm_obj *) ($iter_node->data))->vm_area_node_ps.len
+
+ # fetch and typecast next area
+ set $inner_area = ((mc_vm_area *) ((*((cm_lst_node **) ($inner_iter_node->data)))->data))
+
+ # fetch relevant entries of this area
+ set $id = $inner_area->id
+ set $basename = $inner_area->basename
+ set $start_addr = $inner_area->start_addr
+ set $end_addr = $inner_area->end_addr
+
+ #fetch area's object id if one is present
+ if $inner_area->obj_node_p != 0
+ set $inner_obj_id = ((mc_vm_obj *) ($inner_area->obj_node_p->data))->id
+ else
+ set $inner_obj_id = -1337
+ end
+
+ #fetch area's last object i if one is present
+ if $inner_area->last_obj_node_p != 0
+ set $inner_last_obj_id = ((mc_vm_obj *) ($inner_area->last_obj_node_p->data))->id
+ else
+ set $inner_last_obj_id = -1337
+ end
+
+ # print relevant entries of this area
+ printf " %-3d: 0x%-12lx - 0x%-12lx | obj: %-5d - last_obj: %-5d | \"%s\"\n", $id, $start_addr, $end_addr, $inner_obj_id, $inner_last_obj_id, $basename
+
+ # advance iteration over areas
+ set $inner_iter = $inner_iter + 1
+ set $inner_iter_node = $inner_iter_node->next
+ end
+
+ # setup iteration over last areas belonging to this object
+ set $inner_iter = 0
+ set $inner_iter_node = ((mc_vm_obj *) ($iter_node->data))->last_vm_area_node_ps.head
+
+ # for every area that is part of this object
+ printf " [last areas]:\n"
+ while $inner_iter != ((mc_vm_obj *) ($iter_node->data))->last_vm_area_node_ps.len
+
+ # fetch and typecast next area
+ set $inner_area = ((mc_vm_area *) ((*((cm_lst_node **) ($inner_iter_node->data)))->data))
+
+ # fetch relevant entries of this area
+ set $id = $inner_area->id
+ set $basename = $inner_area->basename
+ set $start_addr = $inner_area->start_addr
+ set $end_addr = $inner_area->end_addr
+
+ #fetch area's object id if one is present
+ if $inner_area->obj_node_p != 0
+ set $inner_obj_id = ((mc_vm_obj *) ($inner_area->obj_node_p->data))->id
+ else
+ set $inner_obj_id = -1337
+ end
+
+ #fetch last area's object i if one is present
+ if $inner_area->last_obj_node_p != 0
+ set $inner_last_obj_id = ((mc_vm_obj *) ($inner_area->last_obj_node_p->data))->id
+ else
+ set $inner_last_obj_id = -1337
+ end
+
+ # print relevant entries of this area
+ printf " %-3d: 0x%-12lx - 0x%-12lx | obj: %-5d - last_obj: %-5d | \"%s\"\n", $id, $start_addr, $end_addr, $inner_obj_id, $inner_last_obj_id, $basename
+
+ # advance iteration over areas
+ set $inner_iter = $inner_iter + 1
+ set $inner_iter_node = $inner_iter_node->next
+ end
+
+ # advance iteration over objects
+ set $iter = $iter + 1
+ set $iter_node = $iter_node->next
+ end
+ end
+end
+
+# print unmapped objs of a map
+define pmapuo
+ if $argc != 1
+ printf "Use: pmapuo <*map>\n"
+ else
+ # print header
+ printf " --- [UNMAPPED OBJS] ---\n"
+
+ # bootstrap iteration over objects
+ set $iter = 0
+ set $iter_node = $arg0->vm_objs_unmapped.head
+
+ # for every object
+ while $iter != $arg0->vm_objs_unmapped.len
+
+ # fetch & typecast next object
+ set $obj = ((mc_vm_obj *) ((*((cm_lst_node **) ($iter_node->data)))->data))
+
+ # fetch relevant entries of this object
+ set $id = $obj->id
+ set $basename = $obj->basename
+ set $start_addr = $obj->start_addr
+ set $end_addr = $obj->end_addr
+ set $num_area = $obj->vm_area_node_ps.len
+ set $num_last_area = $obj->last_vm_area_node_ps.len
+
+ # print relevant entries with conversion to MC_UNDEF_ADDR
+ if ($start_addr == -1) && ($end_addr == -1)
+ printf "<%-3d: MC_UNDEF_ADDR - MC_UNDEF_ADDR | areas: %d - last areas: %d | \"%s\">\n", $id, $num_area, $num_last_area, $basename
+ else
+ printf "<%-3d: 0x%-12lx - 0x%-12lx | areas: %d - last areas: %d | \"%s\">\n", $id, $start_addr, $end_addr, $num_area, $num_last_area, $basename
+ end
+
+ # advance iteration over objects
+ set $iter = $iter + 1
+ set $iter_node = $iter_node->next
+ end
+ end
+end
+
+
+# session dependent (modify from here onwards)
+tb main
+run
+layout src
diff --git a/doc/convert_md_to_roff.sh b/doc/convert_md_to_roff.sh
deleted file mode 100755
index c5554b4..0000000
--- a/doc/convert_md_to_roff.sh
+++ /dev/null
@@ -1,62 +0,0 @@
-#!/bin/bash
-
-PROJECT="liblain"
-VERSION="v1.0.2"
-DATE="Oct 2024"
-
-SRC_DIR="md"
-DST_DIR="roff/man3"
-
-#check that pandoc is installed
-if ! command -v pandoc &> /dev/null; then
- echo "[error]: Pandoc is not installed." >&2
- exit 1
-fi
-
-#create the dst dir if it doesn't exist
-mkdir -p ${DST_DIR}
-
-#clean previous conversions
-rm ${DST_DIR}/*
-
-#convert all markdown files to roff
-for file in "$SRC_DIR"/*.md; do
- if [ -f "$file" ]; then
- filename=$(basename "$file" .md)
- dst_file="$DST_DIR/$filename.3"
-
- echo "Converting $file to $dst_file..."
- pandoc -s -t man "$file" -o "$dst_file"
- if [ $? -ne 0 ]; then
- echo "[error]: Pandoc failed to convert $file to roff format." >&2
- fi
- fi
-done
-
-#remove incorrect formatting
-for file in "$DST_DIR"/*; do
- if [[ -f "$file" ]]; then
- sed -i '/.TH "" "" "" ""/d' "$file"
- fi
-done
-
-#add title and name to each file
-for file in "$DST_DIR"/*; do
-
- basefile=$(basename "$file")
- namefile=${basefile%.*}
- filename=$(echo $namefile | tr [A-Z] [a-z])
- FILENAME=$(echo $namefile | tr [a-z] [A-Z])
-
- # add the filename to the beginning of the file in all uppercase
- echo ".TH ${FILENAME} 3 \"${DATE}\" \"${PROJECT} ${VERSION}\" \"${filename}\"" | cat - $file > temp && mv temp $file
-
- # add the filename to the beginning of the file in all lowercase
- echo ".IX Title \"${FILENAME} 3" | cat - $file > temp && mv temp $file
-
- new_filename="${PROJECT}_$basefile"
- mv "$DST_DIR"/"$basefile" "$DST_DIR"/"$new_filename"
-
-done
-
-echo "Conversion complete."
diff --git a/doc/md/error.md b/doc/md/error.md
deleted file mode 100644
index 251d4ba..0000000
--- a/doc/md/error.md
+++ /dev/null
@@ -1,39 +0,0 @@
-### LIBRARY
-Lain memory manipulation library (liblain, -llain)
-
-
-### SYNOPSIS
-```c
-_Thread_local int ln_errno;
-
-void ln_perror();
-const char * ln_strerror(const int ln_errnum);
-```
-
-
-### STRUCTURE
-When a **liblain** function return a value indicating an error (typically -1 or NULL), the integer *ln_errno* is set to hold a unique error number that describes the error that occurred. Each error number has a corresponding textual description.
-
-There are 3 classes of error numbers:
-
-- *21XX* : Errors caused by the user of the library.
-- *22XX* : Errors caused by an internal bug in the library.
-- *23XX* : Errors caused by the environment (such as a failure to allocate memory).
-
-The *cm_errno* value is stored in thread-local storage. Setting it in one thread does not affect its value in any other thread.
-
-
-### FUNCTIONS
-The **ln_perror()** function outputs the textual description of the last error to occur, stored in *ln_errno*, to standard error.
-
-The **ln_strerror()** takes a error number and returns a pointer to the textual description for the said error number.
-
-The *ln_errno* value is stored in thread-local storage. Setting it in one thread does not affect its value in any other thread.
-
-
-### EXAMPLES
-None provided. See **perror**(3) and **strerror**(3) for identical functionality.
-
-
-### SEE ALSO
-**liblain_iface**(3), **liblain_map**(3), **liblain_util**(3)
diff --git a/doc/md/iface.md b/doc/md/iface.md
deleted file mode 100644
index 43beef5..0000000
--- a/doc/md/iface.md
+++ /dev/null
@@ -1,70 +0,0 @@
-### LIBRARY
-Lain memory manipulation library (liblain, -llain)
-
-
-### SYNOPSIS
-```c
-#define LN_IFACE_LAINKO 0
-#define LN_IFACE_PROCFS 1
-
-
-struct _ln_session {
-
- union {
- struct {
- int fd_mem;
- int pid;
- }; //procfs_data
- struct {
- char major;
- int fd_dev_memu;
- }; //lainko_data
- };
-
- ln_iface iface;
-
-};
-typedef struct _ln_session ln_session;
-
-int ln_open(ln_session * session, const int iface, const pid_t pid);
-int ln_close(ln_session * session);
-int ln_update_map(const ln_session * session, ln_vm_map * vm_map);
-int ln_read(const ln_session * session, const uintptr_t addr,
- cm_byte * buf, const size_t buf_sz);
-int ln_write(const ln_session * session, const uintptr_t addr,
- const cm_byte * buf, const size_t buf_sz);
-```
-
-
-### STRUCTURE
-**liblain** provides 2 interfaces for operating on targets: *procfs* and *lainko*. The *procfs* interface uses Linux's inbuilt */proc* pseudo-filesystem for accessing the target. The *lainko* interface uses the **lain.ko kernel module** provided separately from this library. Both interfaces provide identical functionality. If your target does not employ any countermeasures, it is easier to stick with the *procfs* interface.
-
-To operate on a target it must first be opened. The *ln_session* structure stores data relevant to a single open target. If you desire to operate on multiple targets at the same time, you must open multiple sessions. You can have multiple sessions utilising the same interface, and multiple sessions utilising different interfaces.
-
-A session does not include a memory map. You are free to maintain multiple memory maps associated with a single session.
-
-
-### FUNCTIONS
-The **ln_open()** function opens a *session* on a target with the specified *pid*. The interface to use should be specified with the *iface* argument and should take the value of *LN_IFACE_LAINKO* or *LN_IFACE_PROCFS*.
-
-The **ln_close()** function closes an opened *session*.
-
-The **ln_update_map()** function updates the passed memory map *vm_map*. This function can be called both to populate a map for the first time, and to update it.
-
-The **ln_read()** function reads *buf_sz* bytes at address *addr* into a buffer pointed to by *buf*,
-
-The **ln_write()** function writes *buf_sz* bytes at address *addr* from a buffer pointed to by *buf*.
-
-
-### RETURN VALUES
-**ln_open()**, **ln_close()**, **ln_update_map()**, **ln_read()**, and **ln_write()** functions return 0 on success and -1 on error.
-
-On error, *ln_errno* is set. See **liblain_error**(3).
-
-
-### EXAMPLES
-See *src/test/iface.c* for examples.
-
-
-### SEE ALSO
-**liblain_error**(3), **liblain_map**(3), **liblain_util**(3)
diff --git a/doc/md/map.md b/doc/md/map.md
deleted file mode 100644
index 90abbb7..0000000
--- a/doc/md/map.md
+++ /dev/null
@@ -1,154 +0,0 @@
-### LIBRARY
-Lain memory manipulation library (liblain, -llain)
-
-
-### SYNOPSIS
-```c
-//these macros take a cm_list_node pointer
-#define LN_GET_NODE_AREA(node) ((ln_vm_area *) (node->data))
-#define LN_GET_NODE_OBJ(node) ((ln_vm_obj *) (node->data))
-#define LN_GET_NODE_PTR(node) *((cm_list_node **) (node->data))
-
-//ln_vm_area.access bitmasks
-#define LN_ACCESS_READ 0x01
-#define LN_ACCESS_WRITE 0x02
-#define LN_ACCESS_EXEC 0x04
-#define LN_ACCESS_SHARED 0x08
-
-
-struct _ln_vm_obj;
-
-// --- [memory area]
-typedef struct {
-
- char * pathname;
- char * basename;
-
- uintptr_t start_addr;
- uintptr_t end_addr;
-
- cm_byte access;
-
- cm_list_node * obj_node_ptr; //STORES: own vm_obj *
- cm_list_node * last_obj_node_ptr; //STORES: last encountered vm_obj *
-
- int id;
- bool mapped; //can be set to false with map update
-
-} ln_vm_area;
-
-
-// --- ['backing' object]
-struct _ln_vm_obj {
-
- char pathname[PATH_MAX];
- char basename[NAME_MAX];
-
- uintptr_t start_addr;
- uintptr_t end_addr;
-
- cm_list vm_area_node_ptrs; //STORES: cm_list_node * of ln_vm_area
-
- int id;
- bool mapped; //can be set to false with map update
-};
-typedef struct _ln_vm_obj ln_vm_obj;
-
-
-// --- [memory map]
-typedef struct {
-
- //up to date entries
- cm_list vm_areas; //STORES: ln_vm_area
- cm_list vm_objs; //STORES: ln_vm_obj
-
- //unmapped entries (storage for future deallocation)
- cm_list vm_areas_unmapped; //STORES: cm_list_node * of ln_vm_area
- cm_list vm_objs_unmapped; //STORES: cm_list_node * of ln_vm_obj
-
- // [internal]
- int next_id_area;
- int next_id_obj;
-
-} ln_vm_map;
-
-
-void ln_new_vm_map(ln_vm_map * vm_map);
-int ln_del_vm_map(ln_vm_map * vm_map);
-int ln_map_clean_unmapped(ln_vm_map * vm_map);
-
-off_t ln_get_area_offset(const cm_list_node * area_node, const uintptr_t addr);
-off_t ln_get_obj_offset(const cm_list_node * obj_node, const uintptr_t addr);
-cm_list_node * ln_get_vm_area_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, const off_t * offset);
-
-cm_list_node * ln_get_vm_obj_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, off_t * offset);
-cm_list_node * ln_get_vm_obj_by_pathname(const ln_vm_map * vm_map,
- const char * pathname);
-cm_list_node * ln_get_vm_obj_by_basename(const ln_vm_map * vm_map,
- const char * basename);
-```
-
-
-### STRUCTURE
-**liblain** heavily relies on the linked list implementation provided by **libcmore**. Have a look at **libcmore_list**(3) to understand their interface.
-
-A memory map represents the virtual memory area address mappings of a process. The memory map of a target is represented by a *ln_vm_map* structure. This structure consists of 2 main linked lists: *vm_areas* and *vm_objs*. The *vm_areas* list stores the virtual memory areas of a process. The *vm_objs* list stores the backing objects of a process.
-
-The *ln_vm_area* structure represents a single virtual memory area of a process (kernel: struct *vm_area_struct*). Access permissions of an area can be checked by applying the *LN_ACCESS_READ*, *LN_ACCESS_WRITE*, *LN_ACCESS_EXEC*, and *LN_ACCESS_SHARED* bitmasks to the *access* member.
-
-The *ln_vm_obj* structure represents a single 'backing file' of a set of virtual memory areas (kernel *vm_area_struct.vm_file).
-
-Traversal between an area and its object can be done in O(1). Each area stores a pointer to its corresponding object list node, *obj_node_ptr*, if one is present. Each area without a corresponding object stores a pointer to the last encountered object list node instead, *last_obj_node_ptr*. Each object contains a list of pointers to its corresponding area list nodes, *vm_area_node_ptrs*.
-
-The macros *LN_GET_NODE_AREA()*, *LN_GET_NODE_OBJ()*, and *LN_GET_NODE_PTR()* have been provided to easily fetch the data held by a linked list node.
-
-A memory map is populated and updated by calling **ln_update_map()** on an open session. See **liblain_iface**(3).
-
-Following an update to a map, some areas and objects may become unmapped. To prevent pointer invalidation, their list nodes will be moved to the *vm_areas_unmapped* and *vm_objs_unmapped* linked lists. The *mapped* values of their *ln_vm_area* and *ln_vm_obj* structures will be set to false, and the *next* and *prev* pointers of their nodes will be set to NULL. When ready, all unmapped nodes can be deallocated with **ln_map_clean_unmapped()**.
-
-
-### FUNCTIONS
-The **ln_new_vm_map()** function initialises a new map *vm_map*.
-
-The **ln_del_vm_map()** function deallocates all contents of a map *vm_map*.
-
-The **ln_map_clean_unmapped()** function deallocates all unmapped areas and objects of a map *vm_map*.
-
-The **ln_get_area_offset()** function returns the offset of *addr* from the start of the area *area_node*.
-
-The **ln_get_obj_offset()** function returns the offset of *addr* from the start of the obj *obj_node*.
-
-The **ln_get_area_offset_bnd()** function returns the offset of *addr* from the start of the area *area_node*, or -1 if the address is not in the area.
-
-The **ln_get_obj_offset_bnd()** function returns the offset of *addr* from the start of the obj *obj_node*, or -1 if the address is not in the area.
-
-The **ln_get_vm_area_by_addr()** functions returns a pointer to the area node that *addr* falls into. If *offset* is not NULL, it is set to the offset of *addr* from the beginning of the area.
-
-The **ln_get_vm_obj_by_addr()** function returns a pointer to the object node that *addr* falls into. If *offset* is not NULL, it is set to the offset of *addr* from the beginning of the object.
-
-The **ln_get_vm_obj_by_pathname()** function returns a pointer to the first object who's path matches *pathname*.
-
-The **ln_get_vm_obj_by_basename()** function returns a pointer to the first object who's name matches *basename*.
-
-
-
-### RETURN VALUES
-**ln_new_vm_map()**, **ln_del_vm_map()**, and **ln_map_clean_unmapped()** functions return 0 on success and -1 on error.
-
-**ln_get_area_offset()**, and **ln_get_obj_offset()** return an offset on success, and -1 if *addr* does not belong in the area/object.
-
-**ln_get_vm_area_by_addr()** return a *cm_list_node \** holding a *ln_vm_area* on success, and NULL on error.
-
-**ln_get_vm_obj_by_addr()**, **ln_get_vm_obj_by_pathname()**, and **ln_get_vm_obj_by_basename()** return a *cm_list_node \** on success, and NULL on error.
-
-On error, *ln_errno* is set. See **liblain_error**(3).
-
-
-### EXAMPLES
-See *src/test/map.c* for examples.
-
-
-### SEE ALSO
-**liblain_error**(3), **liblain_iface**(3), **liblain_util**(3)
diff --git a/doc/md/util.md b/doc/md/util.md
deleted file mode 100644
index dd826f2..0000000
--- a/doc/md/util.md
+++ /dev/null
@@ -1,43 +0,0 @@
-### LIBRARY
-Lain memory manipulation library (liblain, -llain)
-
-
-### SYNOPSIS
-```c
-const char * ln_pathname_to_basename(const char * pathname);
-pid_t ln_pid_by_name(const char * basename, cm_vector * pid_vector);
-int ln_name_by_pid(const pid_t pid, char * name_buf);
-void ln_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out);
-```
-
-
-### STRUCTURE
-**liblain** provides some utility functions.
-
-
-### FUNCTIONS
-The **ln_pathname_to_basename()** function returns a pointer to the basename component of the provided *pathname*.
-
-The **ln_pid_by_name()** function returns an allocated vector *pid_vector* of process IDs with a name matching *basename*. On success, *pid_vector* must be manually deallocated with **cm_del_vector()**. See **libcmore_vector**(3).
-
-The **ln_name_by_pid()** function stores the basename (comm) inside *name_buf*. The name is fetched from */proc//status* to mimic behaviour of utilities like *top* and *ps*.
-
-The **ln_bytes_to_hex()** function converts a binary buffer *inp* into its hexadecimal string representation, prefixed by '0x' and stored in the *out* buffer. Note that the length of *out* must be *inp_len* \* 2 + 2.
-
-
-### RETURN VALUES
-**ln_pathname_to_basename()** returns a pointer to the basename on success, and NULL on error.
-
-**ln_pid_by_name()** returns the first pid in *pid_vector* on success, and -1 on error.
-
-**ln_name_by_pid()** returns 0 on success, and -1 on error.
-
-On error, *ln_errno* is set. See **liblain_error**(3).
-
-
-### EXAMPLES
-See *src/test/util.c* for examples.
-
-
-### SEE ALSO
-**liblain_error**(3), **liblain_iface**(3), **liblain_map**(3)
diff --git a/doc/roff/man3/liblain_error.3 b/doc/roff/man3/liblain_error.3
deleted file mode 100644
index d2d89c7..0000000
--- a/doc/roff/man3/liblain_error.3
+++ /dev/null
@@ -1,70 +0,0 @@
-.IX Title "ERROR 3
-.TH ERROR 3 "Oct 2024" "liblain v1.0.2" "error"
-.\" Automatically generated by Pandoc 3.1.2
-.\"
-.\" Define V font for inline verbatim, using C font in formats
-.\" that render this, and otherwise B font.
-.ie "\f[CB]x\f[]"x" \{\
-. ftr V B
-. ftr VI BI
-. ftr VB B
-. ftr VBI BI
-.\}
-.el \{\
-. ftr V CR
-. ftr VI CI
-. ftr VB CB
-. ftr VBI CBI
-.\}
-.hy
-.SS LIBRARY
-.PP
-Lain memory manipulation library (liblain, -llain)
-.SS SYNOPSIS
-.IP
-.nf
-\f[C]
-_Thread_local int ln_errno;
-
-void ln_perror();
-const char * ln_strerror(const int ln_errnum);
-\f[R]
-.fi
-.SS STRUCTURE
-.PP
-When a \f[B]liblain\f[R] function return a value indicating an error
-(typically -1 or NULL), the integer \f[I]ln_errno\f[R] is set to hold a
-unique error number that describes the error that occurred.
-Each error number has a corresponding textual description.
-.PP
-There are 3 classes of error numbers:
-.IP \[bu] 2
-\f[I]21XX\f[R] : Errors caused by the user of the library.
-.IP \[bu] 2
-\f[I]22XX\f[R] : Errors caused by an internal bug in the library.
-.IP \[bu] 2
-\f[I]23XX\f[R] : Errors caused by the environment (such as a failure to
-allocate memory).
-.PP
-The \f[I]cm_errno\f[R] value is stored in thread-local storage.
-Setting it in one thread does not affect its value in any other thread.
-.SS FUNCTIONS
-.PP
-The \f[B]ln_perror()\f[R] function outputs the textual description of
-the last error to occur, stored in \f[I]ln_errno\f[R], to standard
-error.
-.PP
-The \f[B]ln_strerror()\f[R] takes a error number and returns a pointer
-to the textual description for the said error number.
-.PP
-The \f[I]ln_errno\f[R] value is stored in thread-local storage.
-Setting it in one thread does not affect its value in any other thread.
-.SS EXAMPLES
-.PP
-None provided.
-See \f[B]perror\f[R](3) and \f[B]strerror\f[R](3) for identical
-functionality.
-.SS SEE ALSO
-.PP
-\f[B]liblain_iface\f[R](3), \f[B]liblain_map\f[R](3),
-\f[B]liblain_util\f[R](3)
diff --git a/doc/roff/man3/liblain_iface.3 b/doc/roff/man3/liblain_iface.3
deleted file mode 100644
index 4e5ec23..0000000
--- a/doc/roff/man3/liblain_iface.3
+++ /dev/null
@@ -1,115 +0,0 @@
-.IX Title "IFACE 3
-.TH IFACE 3 "Oct 2024" "liblain v1.0.2" "iface"
-.\" Automatically generated by Pandoc 3.1.2
-.\"
-.\" Define V font for inline verbatim, using C font in formats
-.\" that render this, and otherwise B font.
-.ie "\f[CB]x\f[]"x" \{\
-. ftr V B
-. ftr VI BI
-. ftr VB B
-. ftr VBI BI
-.\}
-.el \{\
-. ftr V CR
-. ftr VI CI
-. ftr VB CB
-. ftr VBI CBI
-.\}
-.hy
-.SS LIBRARY
-.PP
-Lain memory manipulation library (liblain, -llain)
-.SS SYNOPSIS
-.IP
-.nf
-\f[C]
-#define LN_IFACE_LAINKO 0
-#define LN_IFACE_PROCFS 1
-
-
-struct _ln_session {
-
- union {
- struct {
- int fd_mem;
- int pid;
- }; //procfs_data
- struct {
- char major;
- int fd_dev_memu;
- }; //lainko_data
- };
-
- ln_iface iface;
-
-};
-typedef struct _ln_session ln_session;
-
-int ln_open(ln_session * session, const int iface, const pid_t pid);
-int ln_close(ln_session * session);
-int ln_update_map(const ln_session * session, ln_vm_map * vm_map);
-int ln_read(const ln_session * session, const uintptr_t addr,
- cm_byte * buf, const size_t buf_sz);
-int ln_write(const ln_session * session, const uintptr_t addr,
- const cm_byte * buf, const size_t buf_sz);
-\f[R]
-.fi
-.SS STRUCTURE
-.PP
-\f[B]liblain\f[R] provides 2 interfaces for operating on targets:
-\f[I]procfs\f[R] and \f[I]lainko\f[R].
-The \f[I]procfs\f[R] interface uses Linux\[cq]s inbuilt \f[I]/proc\f[R]
-pseudo-filesystem for accessing the target.
-The \f[I]lainko\f[R] interface uses the \f[B]lain.ko kernel module\f[R]
-provided separately from this library.
-Both interfaces provide identical functionality.
-If your target does not employ any countermeasures, it is easier to
-stick with the \f[I]procfs\f[R] interface.
-.PP
-To operate on a target it must first be opened.
-The \f[I]ln_session\f[R] structure stores data relevant to a single open
-target.
-If you desire to operate on multiple targets at the same time, you must
-open multiple sessions.
-You can have multiple sessions utilising the same interface, and
-multiple sessions utilising different interfaces.
-.PP
-A session does not include a memory map.
-You are free to maintain multiple memory maps associated with a single
-session.
-.SS FUNCTIONS
-.PP
-The \f[B]ln_open()\f[R] function opens a \f[I]session\f[R] on a target
-with the specified \f[I]pid\f[R].
-The interface to use should be specified with the \f[I]iface\f[R]
-argument and should take the value of \f[I]LN_IFACE_LAINKO\f[R] or
-\f[I]LN_IFACE_PROCFS\f[R].
-.PP
-The \f[B]ln_close()\f[R] function closes an opened \f[I]session\f[R].
-.PP
-The \f[B]ln_update_map()\f[R] function updates the passed memory map
-\f[I]vm_map\f[R].
-This function can be called both to populate a map for the first time,
-and to update it.
-.PP
-The \f[B]ln_read()\f[R] function reads \f[I]buf_sz\f[R] bytes at address
-\f[I]addr\f[R] into a buffer pointed to by \f[I]buf\f[R],
-.PP
-The \f[B]ln_write()\f[R] function writes \f[I]buf_sz\f[R] bytes at
-address \f[I]addr\f[R] from a buffer pointed to by \f[I]buf\f[R].
-.SS RETURN VALUES
-.PP
-\f[B]ln_open()\f[R], \f[B]ln_close()\f[R], \f[B]ln_update_map()\f[R],
-\f[B]ln_read()\f[R], and \f[B]ln_write()\f[R] functions return 0 on
-success and -1 on error.
-.PP
-On error, \f[I]ln_errno\f[R] is set.
-See \f[B]liblain_error\f[R](3).
-.SS EXAMPLES
-.PP
-See \f[I]src/test/iface.c\f[R] for examples.
-.SS SEE ALSO
-.PP
-\f[B]liblain_error\f[R](3), \f[B]liblain_map\f[R](3),
-\f[B]liblain_util\f[R](3)
diff --git a/doc/roff/man3/liblain_map.3 b/doc/roff/man3/liblain_map.3
deleted file mode 100644
index 7c9c605..0000000
--- a/doc/roff/man3/liblain_map.3
+++ /dev/null
@@ -1,231 +0,0 @@
-.IX Title "MAP 3
-.TH MAP 3 "Oct 2024" "liblain v1.0.2" "map"
-.\" Automatically generated by Pandoc 3.1.2
-.\"
-.\" Define V font for inline verbatim, using C font in formats
-.\" that render this, and otherwise B font.
-.ie "\f[CB]x\f[]"x" \{\
-. ftr V B
-. ftr VI BI
-. ftr VB B
-. ftr VBI BI
-.\}
-.el \{\
-. ftr V CR
-. ftr VI CI
-. ftr VB CB
-. ftr VBI CBI
-.\}
-.hy
-.SS LIBRARY
-.PP
-Lain memory manipulation library (liblain, -llain)
-.SS SYNOPSIS
-.IP
-.nf
-\f[C]
-//these macros take a cm_list_node pointer
-#define LN_GET_NODE_AREA(node) ((ln_vm_area *) (node->data))
-#define LN_GET_NODE_OBJ(node) ((ln_vm_obj *) (node->data))
-#define LN_GET_NODE_PTR(node) *((cm_list_node **) (node->data))
-
-//ln_vm_area.access bitmasks
-#define LN_ACCESS_READ 0x01
-#define LN_ACCESS_WRITE 0x02
-#define LN_ACCESS_EXEC 0x04
-#define LN_ACCESS_SHARED 0x08
-
-
-struct _ln_vm_obj;
-
-// --- [memory area]
-typedef struct {
-
- char * pathname;
- char * basename;
-
- uintptr_t start_addr;
- uintptr_t end_addr;
-
- cm_byte access;
-
- cm_list_node * obj_node_ptr; //STORES: own vm_obj *
- cm_list_node * last_obj_node_ptr; //STORES: last encountered vm_obj *
-
- int id;
- bool mapped; //can be set to false with map update
-
-} ln_vm_area;
-
-
-// --- [\[aq]backing\[aq] object]
-struct _ln_vm_obj {
-
- char pathname[PATH_MAX];
- char basename[NAME_MAX];
-
- uintptr_t start_addr;
- uintptr_t end_addr;
-
- cm_list vm_area_node_ptrs; //STORES: cm_list_node * of ln_vm_area
-
- int id;
- bool mapped; //can be set to false with map update
-};
-typedef struct _ln_vm_obj ln_vm_obj;
-
-
-// --- [memory map]
-typedef struct {
-
- //up to date entries
- cm_list vm_areas; //STORES: ln_vm_area
- cm_list vm_objs; //STORES: ln_vm_obj
-
- //unmapped entries (storage for future deallocation)
- cm_list vm_areas_unmapped; //STORES: cm_list_node * of ln_vm_area
- cm_list vm_objs_unmapped; //STORES: cm_list_node * of ln_vm_obj
-
- // [internal]
- int next_id_area;
- int next_id_obj;
-
-} ln_vm_map;
-
-
-void ln_new_vm_map(ln_vm_map * vm_map);
-int ln_del_vm_map(ln_vm_map * vm_map);
-int ln_map_clean_unmapped(ln_vm_map * vm_map);
-
-off_t ln_get_area_offset(const cm_list_node * area_node, const uintptr_t addr);
-off_t ln_get_obj_offset(const cm_list_node * obj_node, const uintptr_t addr);
-cm_list_node * ln_get_vm_area_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, const off_t * offset);
-
-cm_list_node * ln_get_vm_obj_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, off_t * offset);
-cm_list_node * ln_get_vm_obj_by_pathname(const ln_vm_map * vm_map,
- const char * pathname);
-cm_list_node * ln_get_vm_obj_by_basename(const ln_vm_map * vm_map,
- const char * basename);
-\f[R]
-.fi
-.SS STRUCTURE
-.PP
-\f[B]liblain\f[R] heavily relies on the linked list implementation
-provided by \f[B]libcmore\f[R].
-Have a look at \f[B]libcmore_list\f[R](3) to understand their interface.
-.PP
-A memory map represents the virtual memory area address mappings of a
-process.
-The memory map of a target is represented by a \f[I]ln_vm_map\f[R]
-structure.
-This structure consists of 2 main linked lists: \f[I]vm_areas\f[R] and
-\f[I]vm_objs\f[R].
-The \f[I]vm_areas\f[R] list stores the virtual memory areas of a
-process.
-The \f[I]vm_objs\f[R] list stores the backing objects of a process.
-.PP
-The \f[I]ln_vm_area\f[R] structure represents a single virtual memory
-area of a process (kernel: struct \f[I]vm_area_struct\f[R]).
-Access permissions of an area can be checked by applying the
-\f[I]LN_ACCESS_READ\f[R], \f[I]LN_ACCESS_WRITE\f[R],
-\f[I]LN_ACCESS_EXEC\f[R], and \f[I]LN_ACCESS_SHARED\f[R] bitmasks to the
-\f[I]access\f[R] member.
-.PP
-The \f[I]ln_vm_obj\f[R] structure represents a single `backing file' of
-a set of virtual memory areas (kernel *vm_area_struct.vm_file).
-.PP
-Traversal between an area and its object can be done in O(1).
-Each area stores a pointer to its corresponding object list node,
-\f[I]obj_node_ptr\f[R], if one is present.
-Each area without a corresponding object stores a pointer to the last
-encountered object list node instead, \f[I]last_obj_node_ptr\f[R].
-Each object contains a list of pointers to its corresponding area list
-nodes, \f[I]vm_area_node_ptrs\f[R].
-.PP
-The macros \f[I]LN_GET_NODE_AREA()\f[R], \f[I]LN_GET_NODE_OBJ()\f[R],
-and \f[I]LN_GET_NODE_PTR()\f[R] have been provided to easily fetch the
-data held by a linked list node.
-.PP
-A memory map is populated and updated by calling
-\f[B]ln_update_map()\f[R] on an open session.
-See \f[B]liblain_iface\f[R](3).
-.PP
-Following an update to a map, some areas and objects may become
-unmapped.
-To prevent pointer invalidation, their list nodes will be moved to the
-\f[I]vm_areas_unmapped\f[R] and \f[I]vm_objs_unmapped\f[R] linked lists.
-The \f[I]mapped\f[R] values of their \f[I]ln_vm_area\f[R] and
-\f[I]ln_vm_obj\f[R] structures will be set to false, and the
-\f[I]next\f[R] and \f[I]prev\f[R] pointers of their nodes will be set to
-NULL.
-When ready, all unmapped nodes can be deallocated with
-\f[B]ln_map_clean_unmapped()\f[R].
-.SS FUNCTIONS
-.PP
-The \f[B]ln_new_vm_map()\f[R] function initialises a new map
-\f[I]vm_map\f[R].
-.PP
-The \f[B]ln_del_vm_map()\f[R] function deallocates all contents of a map
-\f[I]vm_map\f[R].
-.PP
-The \f[B]ln_map_clean_unmapped()\f[R] function deallocates all unmapped
-areas and objects of a map \f[I]vm_map\f[R].
-.PP
-The \f[B]ln_get_area_offset()\f[R] function returns the offset of
-\f[I]addr\f[R] from the start of the area \f[I]area_node\f[R].
-.PP
-The \f[B]ln_get_obj_offset()\f[R] function returns the offset of
-\f[I]addr\f[R] from the start of the obj \f[I]obj_node\f[R].
-.PP
-The \f[B]ln_get_area_offset_bnd()\f[R] function returns the offset of
-\f[I]addr\f[R] from the start of the area \f[I]area_node\f[R], or -1 if
-the address is not in the area.
-.PP
-The \f[B]ln_get_obj_offset_bnd()\f[R] function returns the offset of
-\f[I]addr\f[R] from the start of the obj \f[I]obj_node\f[R], or -1 if
-the address is not in the area.
-.PP
-The \f[B]ln_get_vm_area_by_addr()\f[R] functions returns a pointer to
-the area node that \f[I]addr\f[R] falls into.
-If \f[I]offset\f[R] is not NULL, it is set to the offset of
-\f[I]addr\f[R] from the beginning of the area.
-.PP
-The \f[B]ln_get_vm_obj_by_addr()\f[R] function returns a pointer to the
-object node that \f[I]addr\f[R] falls into.
-If \f[I]offset\f[R] is not NULL, it is set to the offset of
-\f[I]addr\f[R] from the beginning of the object.
-.PP
-The \f[B]ln_get_vm_obj_by_pathname()\f[R] function returns a pointer to
-the first object who\[cq]s path matches \f[I]pathname\f[R].
-.PP
-The \f[B]ln_get_vm_obj_by_basename()\f[R] function returns a pointer to
-the first object who\[cq]s name matches \f[I]basename\f[R].
-.SS RETURN VALUES
-.PP
-\f[B]ln_new_vm_map()\f[R], \f[B]ln_del_vm_map()\f[R], and
-\f[B]ln_map_clean_unmapped()\f[R] functions return 0 on success and -1
-on error.
-.PP
-\f[B]ln_get_area_offset()\f[R], and \f[B]ln_get_obj_offset()\f[R] return
-an offset on success, and -1 if \f[I]addr\f[R] does not belong in the
-area/object.
-.PP
-\f[B]ln_get_vm_area_by_addr()\f[R] return a \f[I]cm_list_node *\f[R]
-holding a \f[I]ln_vm_area\f[R] on success, and NULL on error.
-.PP
-\f[B]ln_get_vm_obj_by_addr()\f[R],
-\f[B]ln_get_vm_obj_by_pathname()\f[R], and
-\f[B]ln_get_vm_obj_by_basename()\f[R] return a \f[I]cm_list_node *\f[R]
-on success, and NULL on error.
-.PP
-On error, \f[I]ln_errno\f[R] is set.
-See \f[B]liblain_error\f[R](3).
-.SS EXAMPLES
-.PP
-See \f[I]src/test/map.c\f[R] for examples.
-.SS SEE ALSO
-.PP
-\f[B]liblain_error\f[R](3), \f[B]liblain_iface\f[R](3),
-\f[B]liblain_util\f[R](3)
diff --git a/doc/roff/man3/liblain_util.3 b/doc/roff/man3/liblain_util.3
deleted file mode 100644
index 470be67..0000000
--- a/doc/roff/man3/liblain_util.3
+++ /dev/null
@@ -1,75 +0,0 @@
-.IX Title "UTIL 3
-.TH UTIL 3 "Oct 2024" "liblain v1.0.2" "util"
-.\" Automatically generated by Pandoc 3.1.2
-.\"
-.\" Define V font for inline verbatim, using C font in formats
-.\" that render this, and otherwise B font.
-.ie "\f[CB]x\f[]"x" \{\
-. ftr V B
-. ftr VI BI
-. ftr VB B
-. ftr VBI BI
-.\}
-.el \{\
-. ftr V CR
-. ftr VI CI
-. ftr VB CB
-. ftr VBI CBI
-.\}
-.hy
-.SS LIBRARY
-.PP
-Lain memory manipulation library (liblain, -llain)
-.SS SYNOPSIS
-.IP
-.nf
-\f[C]
-const char * ln_pathname_to_basename(const char * pathname);
-pid_t ln_pid_by_name(const char * basename, cm_vector * pid_vector);
-int ln_name_by_pid(const pid_t pid, char * name_buf);
-void ln_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out);
-\f[R]
-.fi
-.SS STRUCTURE
-.PP
-\f[B]liblain\f[R] provides some utility functions.
-.SS FUNCTIONS
-.PP
-The \f[B]ln_pathname_to_basename()\f[R] function returns a pointer to
-the basename component of the provided \f[I]pathname\f[R].
-.PP
-The \f[B]ln_pid_by_name()\f[R] function returns an allocated vector
-\f[I]pid_vector\f[R] of process IDs with a name matching
-\f[I]basename\f[R].
-On success, \f[I]pid_vector\f[R] must be manually deallocated with
-\f[B]cm_del_vector()\f[R].
-See \f[B]libcmore_vector\f[R](3).
-.PP
-The \f[B]ln_name_by_pid()\f[R] function stores the basename (comm)
-inside \f[I]name_buf\f[R].
-The name is fetched from \f[I]/proc//status\f[R] to mimic behaviour of
-utilities like \f[I]top\f[R] and \f[I]ps\f[R].
-.PP
-The \f[B]ln_bytes_to_hex()\f[R] function converts a binary buffer
-\f[I]inp\f[R] into its hexadecimal string representation, prefixed by
-`0x' and stored in the \f[I]out\f[R] buffer.
-Note that the length of \f[I]out\f[R] must be \f[I]inp_len\f[R] * 2 + 2.
-.SS RETURN VALUES
-.PP
-\f[B]ln_pathname_to_basename()\f[R] returns a pointer to the basename on
-success, and NULL on error.
-.PP
-\f[B]ln_pid_by_name()\f[R] returns the first pid in \f[I]pid_vector\f[R]
-on success, and -1 on error.
-.PP
-\f[B]ln_name_by_pid()\f[R] returns 0 on success, and -1 on error.
-.PP
-On error, \f[I]ln_errno\f[R] is set.
-See \f[B]liblain_error\f[R](3).
-.SS EXAMPLES
-.PP
-See \f[I]src/test/util.c\f[R] for examples.
-.SS SEE ALSO
-.PP
-\f[B]liblain_error\f[R](3), \f[B]liblain_iface\f[R](3),
-\f[B]liblain_map\f[R](3)
diff --git a/liblain.png b/media/memcry.png
similarity index 100%
rename from liblain.png
rename to media/memcry.png
diff --git a/media/overview.png b/media/overview.png
new file mode 100644
index 0000000..2327094
Binary files /dev/null and b/media/overview.png differ
diff --git a/media/resources/COLOURS b/media/resources/COLOURS
new file mode 100644
index 0000000..9e2ce43
--- /dev/null
+++ b/media/resources/COLOURS
@@ -0,0 +1,4 @@
+#6e2a2c - red (border)
+#342025 - grox red (background)
+#a29d88 - sand (symbol)
+
diff --git a/media/resources/memcry.xcf b/media/resources/memcry.xcf
new file mode 100644
index 0000000..98271f1
Binary files /dev/null and b/media/resources/memcry.xcf differ
diff --git a/media/resources/overview.drawio b/media/resources/overview.drawio
new file mode 100644
index 0000000..157fa02
--- /dev/null
+++ b/media/resources/overview.drawio
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/media/resources/phage.png b/media/resources/phage.png
new file mode 100644
index 0000000..e319d15
Binary files /dev/null and b/media/resources/phage.png differ
diff --git a/src/lib/Makefile b/src/lib/Makefile
index cd56e16..c8caef6 100644
--- a/src/lib/Makefile
+++ b/src/lib/Makefile
@@ -2,34 +2,44 @@
# This makefile takes the following variables:
#
-# CC - Compiler.
-# CFLAGS - Compiler flags.
-# INCLUDE - Shared objects to link.
-# BUILD_DIR - Base build directory.
+# CC - Compiler.
+# BUILD_DIR - Library build directory.
+#
+# _CFLAGS - Compiler flags.
+# _WARN_OPTS - Compiler warnings.
+# _LDFLAGS - Linker flags.
+
-SOURCES_LIB=error.c iface.c lainko_iface.c map.c map_util.c procfs_iface.c util.c
-HEADERS_LIB=error.h iface.h lainko.h lainko_iface.h liblain.h map.h map_util.h procfs_iface.h util.h
-OBJECTS_LIB=${SOURCES_LIB:.c=.o}
+CFLAGS=${_CFLAGS}
+WARN_OPTS=${_WARN_OPTS} -Wno-unused-parameter
+LDFLAGS=${_LDFLAGS}
-LIB=liblain.so
+SOURCES_LIB=error.c iface.c krncry_iface.c \
+ map.c map_util.c procfs_iface.c util.c
+OBJECTS_LIB=${SOURCES_LIB:%.c=${BUILD_DIR}/%.o}
+SHARED=libmcry.so
+STATIC=libmcry.a
-WARN_OPTS := -Wno-unused-but-set-variable -Wno-stringop-truncation -Wno-maybe-uninitialized
-lib: ${LIB}
-> mkdir -p ${BUILD_DIR}/lib
-> mv ${LIB} ${BUILD_DIR}/lib
+shared: ${SHARED}
+> mkdir -p ${BUILD_DIR}
+> mv ${SHARED} ${BUILD_DIR}
-${LIB}: ${OBJECTS_LIB}
-> ${CC} ${CFLAGS} -shared -o ${LIB} ${OBJECTS_LIB} ${HEADERS_LIB} ${WARN_OPTS} ${INCLUDE}
+static: ${STATIC}
+> mkdir -p ${BUILD_DIR}
+> mv ${STATIC} ${BUILD_DIR}
-${OBJECTS_LIB}: ${SOURCES_LIB} ${HEADERS_LIB}
-> ${CC} ${CFLAGS} -c ${SOURCES_LIB} ${WARN_OPTS} ${INCLUDE}
+${SHARED}: ${OBJECTS_LIB}
+> ${CC} ${CFLAGS} -shared -o $@ $^ ${LDFLAGS}
-clean_all: clean_src clean_build
+${STATIC}: ${OBJECTS_LIB}
+> ar rcs $@ $^
-clean_src:
-> -rm -f *.o
+${BUILD_DIR}/%.o: %.c
+> ${CC} ${CFLAGS} ${WARN_OPTS} -c $< -o $@
-clean_build:
-> -rm ${BUILD_DIR}/lib/${LIB}
+clean:
+> -rm -v ${BUILD_DIR}/${SHARED}
+> -rm -v ${BUILD_DIR}/${STATIC}
+> -rm -v ${OBJECTS_LIB}
diff --git a/src/lib/debug.h b/src/lib/debug.h
new file mode 100644
index 0000000..7d1e2c1
--- /dev/null
+++ b/src/lib/debug.h
@@ -0,0 +1,18 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+
+/*
+ * Do not define internal functions as static in debug builds
+ */
+
+#ifdef DEBUG
+#define DBG_STATIC
+#define DBG_INLINE
+#else
+#define DBG_STATIC static
+#define DBG_INLINE inline
+#endif
+
+
+#endif
diff --git a/src/lib/error.c b/src/lib/error.c
index b235824..2a16086 100644
--- a/src/lib/error.c
+++ b/src/lib/error.c
@@ -1,82 +1,91 @@
+//standard library
#include
-#include "liblain.h"
+//local headers
+#include "memcry.h"
#include "error.h"
-__thread int ln_errno;
+__thread int mc_errno;
-void ln_perror() {
+/*
+ * TODO: Find a better way to do this.
+ */
- switch(ln_errno) {
+void mc_perror(const char * prefix) {
+
+ switch(mc_errno) {
// 1XX - user errors
- case LN_ERR_PROC_MEM:
- fprintf(stderr, LN_ERR_PROC_MEM_MSG);
+ case MC_ERR_PROC_MEM:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_PROC_MEM_MSG);
break;
- case LN_ERR_PROC_MAP:
- fprintf(stderr, LN_ERR_PROC_MAP_MSG);
+ case MC_ERR_PROC_MAP:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_PROC_MAP_MSG);
break;
- case LN_ERR_SEEK_ADDR:
- fprintf(stderr, LN_ERR_SEEK_ADDR_MSG);
+ case MC_ERR_SEEK_ADDR:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_SEEK_ADDR_MSG);
break;
// 2XX - internal errors
- case LN_ERR_INTERNAL_INDEX:
- fprintf(stderr, LN_ERR_INTERNAL_INDEX_MSG);
+ case MC_ERR_INTERNAL_INDEX:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_INTERNAL_INDEX_MSG);
+ break;
+
+ case MC_ERR_AREA_IN_OBJ:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_AREA_IN_OBJ_MSG);
break;
- case LN_ERR_UNEXPECTED_NULL:
- fprintf(stderr, LN_ERR_UNEXPECTED_NULL_MSG);
+ case MC_ERR_UNEXPECTED_NULL:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_UNEXPECTED_NULL_MSG);
break;
- case LN_ERR_LIBCMORE:
- fprintf(stderr, LN_ERR_LIBCMORE_MSG);
+ case MC_ERR_CMORE:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_CMORE_MSG);
break;
- case LN_ERR_READ_WRITE:
- fprintf(stderr, LN_ERR_READ_WRITE_MSG);
+ case MC_ERR_READ_WRITE:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_READ_WRITE_MSG);
break;
- case LN_ERR_MEMU_TARGET:
- fprintf(stderr, LN_ERR_MEMU_TARGET_MSG);
+ case MC_ERR_MEMU_TARGET:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_MEMU_TARGET_MSG);
break;
- case LN_ERR_MEMU_MAP_SZ:
- fprintf(stderr, LN_ERR_MEMU_MAP_SZ_MSG);
+ case MC_ERR_MEMU_MAP_SZ:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_MEMU_MAP_SZ_MSG);
break;
- case LN_ERR_MEMU_MAP_GET:
- fprintf(stderr, LN_ERR_MEMU_MAP_GET_MSG);
+ case MC_ERR_MEMU_MAP_GET:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_MEMU_MAP_GET_MSG);
break;
- case LN_ERR_PROC_STATUS:
- fprintf(stderr, LN_ERR_PROC_STATUS_MSG);
+ case MC_ERR_PROC_STATUS:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_PROC_STATUS_MSG);
break;
- case LN_ERR_PROC_NAV:
- fprintf(stderr, LN_ERR_PROC_NAV_MSG);
+ case MC_ERR_PROC_NAV:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_PROC_NAV_MSG);
break;
-
// 3XX - environmental errors
- case LN_ERR_MEM:
- fprintf(stderr, LN_ERR_MEM_MSG);
+ case MC_ERR_MEM:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_MEM_MSG);
break;
- case LN_ERR_PAGESIZE:
- fprintf(stderr, LN_ERR_PAGESIZE_MSG);
+ case MC_ERR_PAGESIZE:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_PAGESIZE_MSG);
break;
- case LN_ERR_LAINKO_MAJOR:
- fprintf(stderr, LN_ERR_LAINKO_MAJOR_MSG);
+ case MC_ERR_KRNCRY_MAJOR:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_KRNCRY_MAJOR_MSG);
break;
- case LN_ERR_MEMU_OPEN:
- fprintf(stderr, LN_ERR_MEMU_OPEN_MSG);
+ case MC_ERR_MEMU_OPEN:
+ fprintf(stderr, "%s: %s", prefix, MC_ERR_MEMU_OPEN_MSG);
break;
default:
@@ -88,60 +97,62 @@ void ln_perror() {
}
-const char * ln_strerror(const int ln_errnum) {
-
- switch (ln_errnum) {
+const char * mc_strerror(const int mc_errnum) {
+ switch (mc_errnum) {
// 1XX - user errors
- case LN_ERR_PROC_MEM:
- return LN_ERR_PROC_MEM_MSG;
+ case MC_ERR_PROC_MEM:
+ return MC_ERR_PROC_MEM_MSG;
- case LN_ERR_PROC_MAP:
- return LN_ERR_PROC_MAP_MSG;
+ case MC_ERR_PROC_MAP:
+ return MC_ERR_PROC_MAP_MSG;
- case LN_ERR_SEEK_ADDR:
- return LN_ERR_SEEK_ADDR_MSG;
+ case MC_ERR_SEEK_ADDR:
+ return MC_ERR_SEEK_ADDR_MSG;
// 2xx - internal errors
- case LN_ERR_INTERNAL_INDEX:
- return LN_ERR_INTERNAL_INDEX_MSG;
+ case MC_ERR_INTERNAL_INDEX:
+ return MC_ERR_INTERNAL_INDEX_MSG;
+
+ case MC_ERR_AREA_IN_OBJ:
+ return MC_ERR_AREA_IN_OBJ_MSG;
- case LN_ERR_UNEXPECTED_NULL:
- return LN_ERR_UNEXPECTED_NULL_MSG;
+ case MC_ERR_UNEXPECTED_NULL:
+ return MC_ERR_UNEXPECTED_NULL_MSG;
- case LN_ERR_LIBCMORE:
- return LN_ERR_LIBCMORE_MSG;
+ case MC_ERR_CMORE:
+ return MC_ERR_CMORE_MSG;
- case LN_ERR_READ_WRITE:
- return LN_ERR_READ_WRITE_MSG;
+ case MC_ERR_READ_WRITE:
+ return MC_ERR_READ_WRITE_MSG;
- case LN_ERR_MEMU_TARGET:
- return LN_ERR_MEMU_TARGET_MSG;
+ case MC_ERR_MEMU_TARGET:
+ return MC_ERR_MEMU_TARGET_MSG;
- case LN_ERR_MEMU_MAP_SZ:
- return LN_ERR_MEMU_MAP_SZ_MSG;
+ case MC_ERR_MEMU_MAP_SZ:
+ return MC_ERR_MEMU_MAP_SZ_MSG;
- case LN_ERR_MEMU_MAP_GET:
- return LN_ERR_MEMU_MAP_GET_MSG;
+ case MC_ERR_MEMU_MAP_GET:
+ return MC_ERR_MEMU_MAP_GET_MSG;
- case LN_ERR_PROC_STATUS:
- return LN_ERR_PROC_STATUS_MSG;
+ case MC_ERR_PROC_STATUS:
+ return MC_ERR_PROC_STATUS_MSG;
- case LN_ERR_PROC_NAV:
- return LN_ERR_PROC_NAV_MSG;
+ case MC_ERR_PROC_NAV:
+ return MC_ERR_PROC_NAV_MSG;
// 3XX - environmental errors
- case LN_ERR_MEM:
- return LN_ERR_MEM_MSG;
+ case MC_ERR_MEM:
+ return MC_ERR_MEM_MSG;
- case LN_ERR_PAGESIZE:
- return LN_ERR_PAGESIZE_MSG;
+ case MC_ERR_PAGESIZE:
+ return MC_ERR_PAGESIZE_MSG;
- case LN_ERR_LAINKO_MAJOR:
- return LN_ERR_LAINKO_MAJOR_MSG;
+ case MC_ERR_KRNCRY_MAJOR:
+ return MC_ERR_KRNCRY_MAJOR_MSG;
- case LN_ERR_MEMU_OPEN:
- return LN_ERR_MEMU_OPEN_MSG;
+ case MC_ERR_MEMU_OPEN:
+ return MC_ERR_MEMU_OPEN_MSG;
default:
return "Undefined error code.\n";
diff --git a/src/lib/error.h b/src/lib/error.h
index 0c41a8c..b1a9aa2 100644
--- a/src/lib/error.h
+++ b/src/lib/error.h
@@ -3,8 +3,7 @@
//external
-void ln_perror();
-const char * ln_strerror(const int ln_errnum);
-
+void mc_perror(const char * prefix);
+const char * mc_strerror(const int mc_errnum);
#endif
diff --git a/src/lib/iface.c b/src/lib/iface.c
index d3148cc..23d833d 100644
--- a/src/lib/iface.c
+++ b/src/lib/iface.c
@@ -1,49 +1,65 @@
+//standard library
#include
#include
-#include
+//external libraries
+#include
+//local headers
#include "iface.h"
-#include "liblain.h"
-
+#include "memcry.h"
#include "procfs_iface.h"
-#include "lainko_iface.h"
+#include "krncry_iface.h"
+#include "debug.h"
+
-static inline void set_procfs_session(ln_session * session) {
+/*
+ * --- [INTERNAL] ---
+ */
- session->iface.open = _procfs_open;
- session->iface.close = _procfs_close;
- session->iface.update_map = _procfs_update_map;
- session->iface.read = _procfs_read;
- session->iface.write = _procfs_write;
+DBG_STATIC DBG_INLINE
+void _set_procfs_session(mc_session * session) {
+
+ session->iface.open = procfs_open;
+ session->iface.close = procfs_close;
+ session->iface.update_map = procfs_update_map;
+ session->iface.read = procfs_read;
+ session->iface.write = procfs_write;
return;
}
-static inline void set_lainko_session(ln_session * session) {
+DBG_STATIC DBG_INLINE
+void _set_krncry_session(mc_session * session) {
- session->iface.open = _lainko_open;
- session->iface.close = _lainko_close;
- session->iface.update_map = _lainko_update_map;
- session->iface.read = _lainko_read;
- session->iface.write = _lainko_write;
+ session->iface.open = krncry_open;
+ session->iface.close = krncry_close;
+ session->iface.update_map = krncry_update_map;
+ session->iface.read = krncry_read;
+ session->iface.write = krncry_write;
return;
}
-//open session
-int ln_open(ln_session * session, const int iface, const pid_t pid) {
+
+/*
+ * --- [EXTERNAL] ---
+ */
+
+int mc_open(mc_session * session,
+ const enum mc_iface_type iface, const pid_t pid) {
+
int ret;
//if requesting procfs interface
- if (iface == LN_IFACE_PROCFS) {
- set_procfs_session(session);
+ if (iface == PROCFS) {
+ _set_procfs_session(session);
} else {
- set_lainko_session(session);
+ _set_krncry_session(session);
}
ret = session->iface.open(session, pid);
@@ -53,8 +69,7 @@ int ln_open(ln_session * session, const int iface, const pid_t pid) {
}
-//close session
-int ln_close(ln_session * session) {
+int mc_close(mc_session * session) {
int ret;
@@ -65,8 +80,7 @@ int ln_close(ln_session * session) {
}
-//update a map
-int ln_update_map(const ln_session * session, ln_vm_map * vm_map) {
+int mc_update_map(const mc_session * session, mc_vm_map * vm_map) {
int ret;
@@ -77,27 +91,25 @@ int ln_update_map(const ln_session * session, ln_vm_map * vm_map) {
}
-//read memory
-int ln_read(const ln_session * session, const uintptr_t addr,
+int mc_read(const mc_session * session, const uintptr_t addr,
cm_byte * buf, const size_t buf_sz) {
int ret;
ret = session->iface.read(session, addr, buf, buf_sz);
- if (ret) return -1;
+ if (ret == -1) return -1;
return 0;
}
-//write memory
-int ln_write(const ln_session * session, uintptr_t addr,
+int mc_write(const mc_session * session, const uintptr_t addr,
const cm_byte * buf, const size_t buf_sz) {
int ret;
ret = session->iface.write(session, addr, buf, buf_sz);
- if (ret) return -1;
+ if (ret == -1) return -1;
return 0;
}
diff --git a/src/lib/iface.h b/src/lib/iface.h
index 76d5ccd..5d7da07 100644
--- a/src/lib/iface.h
+++ b/src/lib/iface.h
@@ -1,19 +1,31 @@
#ifndef IFACE_H
#define IFACE_H
+//standard library
#include
-#include "liblain.h"
+//system headers
+#include
+
+//local headers
+#include "memcry.h"
+
+
+#ifdef DEBUG
+//internal
+void _set_procfs_session(mc_session * session);
+void _set_krncry_session(mc_session * session);
+#endif
//external
-int ln_open(ln_session * session, const int iface, const pid_t pid);
-int ln_close(ln_session * session);
-int ln_update_map(const ln_session * session, ln_vm_map * vm_map);
-int ln_read(const ln_session * session, const uintptr_t addr,
+int mc_open(mc_session * session,
+ const enum mc_iface_type iface, const pid_t pid);
+int mc_close(mc_session * session);
+int mc_update_map(const mc_session * session, mc_vm_map * vm_map);
+int mc_read(const mc_session * session, const uintptr_t addr,
cm_byte * buf, const size_t buf_sz);
-int ln_write(const ln_session * session, const uintptr_t addr,
+int mc_write(const mc_session * session, const uintptr_t addr,
const cm_byte * buf, const size_t buf_sz);
-
#endif
diff --git a/src/lib/krncry.h b/src/lib/krncry.h
new file mode 100644
index 0000000..95e1c34
--- /dev/null
+++ b/src/lib/krncry.h
@@ -0,0 +1,76 @@
+#ifndef KRNCRY_H
+#define KRNCRY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//system headers
+#include
+
+
+/*
+ * This header comes from the krncry project.
+ */
+
+//krncry ioctl call numbers
+#define KRNCRY_IOCTL_OPEN_TGT 0
+#define KRNCRY_IOCTL_RELEASE_TGT 1
+#define KRNCRY_IOCTL_GET_MAP 2
+#define KRNCRY_IOCTL_GET_MAP_SZ 3
+
+//templates for ioctl calls
+#define KRNCRY_TEMPLATE_OPEN_TGT 0x40080000
+#define KRNCRY_TEMPLATE_RELEASE_TGT 0x00000001
+#define KRNCRY_TEMPLATE_GET_MAP 0xc0080002
+#define KRNCRY_TEMPLATE_GET_MAP_SZ 0x40080003
+
+//template macro
+#define KRNCRY_APPLY_TEMPLATE(major, krncry_template) (((major << 8) & 0x0000ff00) | krncry_template)
+
+
+//vma protection - taken from linux/pgtable_types.h
+typedef unsigned long krncry_pgprot_t;
+
+//permission bitmask
+#define VM_PROT_MASK 0x0000000F
+
+//specific permission bitmasks
+#define VM_READ 0x00000001
+#define VM_WRITE 0x00000002
+#define VM_EXEC 0x00000004
+#define VM_SHARED 0x00000008
+
+
+/*
+ * --- [DATA TYPES] ---
+ */
+
+// [byte]
+typedef unsigned char kc_byte;
+
+
+// [ioctl argument]
+struct ioctl_arg {
+ kc_byte * u_buf;
+ int target_pid;
+};
+
+
+// [map entry]
+struct vm_entry {
+
+ unsigned long vm_start;
+ unsigned long vm_end;
+
+ unsigned long file_off;
+ krncry_pgprot_t prot;
+ char file_path[PATH_MAX];
+};
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/krncry_iface.c b/src/lib/krncry_iface.c
new file mode 100644
index 0000000..0179188
--- /dev/null
+++ b/src/lib/krncry_iface.c
@@ -0,0 +1,278 @@
+//standard library
+#include
+#include
+#include
+
+//system headers
+#include
+#include
+
+#include
+#include
+
+#include
+
+//external libraries
+#include
+
+//local headers
+#include "krncry_iface.h"
+#include "memcry.h"
+#include "krncry.h"
+#include "map.h"
+#include "debug.h"
+
+
+
+/*
+ * --- [INTERNAL] ---
+ */
+
+//read krncry's major number
+DBG_STATIC DBG_INLINE
+char _krncry_iface_get_major() {
+
+ int fd;
+
+ ssize_t read_bytes;
+ char major, read_buf[8];
+
+
+ //open major attribute
+ fd = open(KRNCRY_MAJOR_PATH, O_RDONLY);
+ if (fd == -1) {
+ mc_errno = MC_ERR_KRNCRY_MAJOR;
+ return -1;
+ }
+
+ //read string representation into buffer
+ read_bytes = read(fd, &read_buf, 8);
+ if (read_bytes == -1) {
+ mc_errno = MC_ERR_KRNCRY_MAJOR;
+ return -1;
+ }
+
+ //convert back to binary representation
+ major = (char) strtol(read_buf, NULL, 10);
+
+ close(fd);
+ return major;
+}
+
+
+
+/*
+ * --- [INTERFACE] ---
+ */
+
+int krncry_open(mc_session * session, const pid_t pid) {
+
+ int ret;
+ char device_path[PATH_MAX];
+ uint32_t ioctl_call;
+ struct ioctl_arg arg;
+
+
+ //get page size to determine maximum read/write size
+ session->page_size = sysconf(_SC_PAGESIZE);
+ if (session->page_size < 0) {
+ mc_errno = MC_ERR_PAGESIZE;
+ return -1;
+ }
+
+ //get major of the module, returns -1 if unloaded
+ session->major = _krncry_iface_get_major();
+ if (session->major == -1) return -1;
+
+ //build krncry device path
+ snprintf(device_path, PATH_MAX, "/dev/char/%d:%d",
+ (unsigned char) session->major, KRNCRY_MINOR);
+
+ //open the krncry device
+ session->fd_dev_krncry = open(device_path, O_RDWR);
+ if (session->fd_dev_krncry == -1) {
+ mc_errno = MC_ERR_MEMU_OPEN;
+ return -1;
+ }
+
+ //call ioctl to set the target process
+ arg.target_pid = pid;
+ ioctl_call = KRNCRY_APPLY_TEMPLATE((char) session->major,
+ KRNCRY_TEMPLATE_OPEN_TGT);
+ ret = ioctl(session->fd_dev_krncry, ioctl_call, &arg);
+ if (ret) {
+ close(session->fd_dev_krncry);
+ mc_errno = MC_ERR_MEMU_TARGET;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int krncry_close(mc_session * session) {
+
+ int ret;
+ uint32_t ioctl_call;
+ struct ioctl_arg arg; //not used
+
+
+ //call ioctl to release target
+ ioctl_call = KRNCRY_APPLY_TEMPLATE((char) session->major,
+ KRNCRY_TEMPLATE_RELEASE_TGT);
+ ret = ioctl(session->fd_dev_krncry, ioctl_call, &arg);
+ if (ret == -1) {
+
+ return -1;
+ } //FIXME check this is correct
+
+ //close device
+ close(session->fd_dev_krncry);
+
+ return 0;
+}
+
+
+int krncry_update_map(const mc_session * session, mc_vm_map * vm_map) {
+
+ int ret, count;
+ uint32_t ioctl_call;
+ size_t u_buf_sz;
+
+ struct ioctl_arg arg;
+ _traverse_state state;
+
+
+ //call ioctl to get the map size
+ ioctl_call = KRNCRY_APPLY_TEMPLATE((char) session->major,
+ KRNCRY_TEMPLATE_GET_MAP_SZ);
+ count = ioctl(session->fd_dev_krncry, ioctl_call, &arg);
+ if (count <= 0) {
+ mc_errno = MC_ERR_MEMU_MAP_SZ;
+ return -1;
+ }
+
+ //allocate buffer to hold the map
+ u_buf_sz = count * sizeof(struct vm_entry);
+ arg.u_buf = mmap(NULL, u_buf_sz,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (arg.u_buf == MAP_FAILED) {
+ mc_errno = MC_ERR_MEM;
+ return -1;
+ }
+
+ //get the map
+ ioctl_call = KRNCRY_APPLY_TEMPLATE((char) session->major,
+ KRNCRY_TEMPLATE_GET_MAP);
+
+ count = ioctl(session->fd_dev_krncry, ioctl_call, &arg);
+ if (count <= 0) {
+ munmap(arg.u_buf, u_buf_sz);
+ mc_errno = MC_ERR_MEMU_MAP_GET;
+ return -1;
+ }
+
+
+ //update the map with each received segment
+ map_init_traverse_state(&state, vm_map);
+
+ for (int i = 0; i < count; ++i) {
+
+ ret = map_send_entry((struct vm_entry *)
+ (arg.u_buf + (i * sizeof(struct vm_entry))),
+ &state, vm_map);
+ if (ret) {
+ munmap(arg.u_buf, u_buf_sz);
+ return -1;
+ }
+ } //end for
+
+ //unmap map buffer
+ munmap(arg.u_buf, u_buf_sz);
+
+ return 0;
+}
+
+
+int krncry_read(const mc_session * session, const uintptr_t addr,
+ cm_byte * buf, const size_t buf_sz) {
+
+ off_t off_ret;
+ ssize_t read_bytes, read_done, read_left;
+
+
+ //initialise read state
+ read_done = read_left = 0;
+
+ //seek to address
+ off_ret = lseek(session->fd_dev_krncry, (off_t) addr, SEEK_SET);
+ if (off_ret == -1) {
+ mc_errno = MC_ERR_SEEK_ADDR;
+ return -1;
+ }
+
+ //read page_size bytes repeatedly until done
+ do {
+
+ //calc how many bytes left to read
+ read_left = buf_sz - read_done;
+
+ //read into buffer
+ read_bytes = read(session->fd_dev_krncry, buf + read_done,
+ read_left > session->page_size
+ ? session->page_size : read_left);
+ //if error or EOF before reading len bytes
+ if (read_bytes == -1 || (read_bytes == 0
+ && read_done < (ssize_t) buf_sz)) {
+
+ mc_errno = MC_ERR_READ_WRITE;
+ return -1;
+ }
+ read_done += read_bytes;
+
+ } while (read_done < (ssize_t) buf_sz);
+
+ return 0;
+}
+
+
+int krncry_write(const mc_session * session, const uintptr_t addr,
+ const cm_byte * buf, const size_t buf_sz) {
+
+ off_t off_ret;
+ ssize_t write_bytes, write_done, write_left;
+
+
+ //initialise write state
+ write_done = write_left = 0;
+
+ //seek to address
+ off_ret = lseek(session->fd_dev_krncry, (off_t) addr, SEEK_SET);
+ if (off_ret == -1) {
+ mc_errno = MC_ERR_SEEK_ADDR;
+ return -1;
+ }
+
+ //write page_size bytes repeatedly until done
+ do {
+
+ //calc how many bytes left to write
+ write_left = buf_sz - write_done;
+
+ //write into buffer
+ write_bytes = write(session->fd_dev_krncry, buf + write_done,
+ write_left > session->page_size
+ ? session->page_size : write_left);
+ //if error or EOF before writing len bytes
+ if (write_bytes == -1 || (write_bytes == 0
+ && write_done < (ssize_t) buf_sz)) {
+
+ mc_errno = MC_ERR_READ_WRITE;
+ return -1;
+ }
+ write_done += write_bytes;
+
+ } while (write_done < (ssize_t) buf_sz);
+
+ return 0;
+}
diff --git a/src/lib/krncry_iface.h b/src/lib/krncry_iface.h
new file mode 100644
index 0000000..9e92590
--- /dev/null
+++ b/src/lib/krncry_iface.h
@@ -0,0 +1,31 @@
+#ifndef KRNCRY_IFACE_H
+#define KRNCRY_IFACE_H
+
+//external libraries
+#include
+
+//local headers
+#include "memcry.h"
+#include "debug.h"
+
+
+#define KRNCRY_MAJOR_PATH "/sys/class/krncry/krncry_major"
+#define KRNCRY_MINOR 0
+
+
+#ifdef DEBUG
+//internal
+char _krncry_iface_get_major();
+#endif
+
+
+//interface
+int krncry_open(mc_session * session, const pid_t pid);
+int krncry_close(mc_session * session);
+int krncry_update_map(const mc_session * session, mc_vm_map * vm_map);
+int krncry_read(const mc_session * session, const uintptr_t addr,
+ cm_byte * buf, const size_t buf_sz);
+int krncry_write(const mc_session * session, const uintptr_t addr,
+ const cm_byte * buf, const size_t buf_sz);
+
+#endif
diff --git a/src/lib/lainko.h b/src/lib/lainko.h
deleted file mode 100644
index e05ad85..0000000
--- a/src/lib/lainko.h
+++ /dev/null
@@ -1,71 +0,0 @@
-#ifndef LAINKO_H
-#define LAINKO_H
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-
-#include
-
-
-/*
- * This header is shared with userspace.
- */
-
-//lainmemu ioctl call numbers
-#define LAINMEMU_IOCTL_OPEN_TGT 0
-#define LAINMEMU_IOCTL_RELEASE_TGT 1
-#define LAINMEMU_IOCTL_GET_MAP 2
-#define LAINMEMU_IOCTL_GET_MAP_SZ 3
-
-
-//templates for ioctl calls
-#define LAINMEMU_TEMPLATE_OPEN_TGT 0x40080000
-#define LAINMEMU_TEMPLATE_RELEASE_TGT 0x00000001
-#define LAINMEMU_TEMPLATE_GET_MAP 0xc0080002
-#define LAINMEMU_TEMPLATE_GET_MAP_SZ 0x40080003
-
-//template macro
-#define LAINKO_APPLY_TEMPLATE(major, lainmemu_template) (((major << 8) & 0x0000ff00) | lainmemu_template)
-
-
-//vma protection - taken from linux/pgtable_types.h
-typedef unsigned long lainko_pgprot_t;
-
-#define VM_READ 0x00000001
-#define VM_WRITE 0x00000002
-#define VM_EXEC 0x00000004
-#define VM_SHARED 0x00000008
-
-#define VM_PROT_MASK 0x0000000F
-
-
-typedef char lainko_byte;
-typedef unsigned char lainko_ubyte;
-
-
-
-//ioctl argument
-struct ioctl_arg {
- lainko_byte * u_buf;
- int target_pid;
-};
-
-
-//map entry
-struct vm_entry {
-
- unsigned long vm_start;
- unsigned long vm_end;
-
- unsigned long file_off;
- lainko_pgprot_t prot;
- char file_path[PATH_MAX];
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src/lib/lainko_iface.c b/src/lib/lainko_iface.c
deleted file mode 100644
index 846c609..0000000
--- a/src/lib/lainko_iface.c
+++ /dev/null
@@ -1,258 +0,0 @@
-#include
-#include
-#include
-
-#include
-#include
-
-#include
-#include
-
-#include
-
-#include
-
-#include "lainko_iface.h"
-#include "liblain.h"
-#include "lainko.h"
-#include "map.h"
-
-
-// --- LAINKO INTERFACE INTERNALS
-
-//read lainko's major number
-static inline char _get_major() {
-
- int fd;
-
- size_t read_bytes;
- char read_buf[8];
-
- char major;
-
- //open major attribute
- fd = open(LAINKO_MAJOR_PATH, O_RDONLY);
- if (fd == -1) {
- ln_errno = LN_ERR_LAINKO_MAJOR;
- return -1;
- }
-
- //read string representation into buffer
- read_bytes = read(fd, &read_buf, 8);
- if (read_bytes == -1) {
- ln_errno = LN_ERR_LAINKO_MAJOR;
- return -1;
- }
-
- //convert back to binary representation
- major = (char) strtol(read_buf, NULL, 10);
-
- close(fd);
- return major;
-}
-
-
-// --- CALLED BY VIRTUAL INTERFACE
-
-//locate and open the lainmemu device, and then open the target
-int _lainko_open(ln_session * session, const pid_t pid) {
-
- int ret;
- char memu_path[PATH_MAX];
- uint32_t ioctl_call;
- struct ioctl_arg arg;
-
- //get page size
- session->page_size = sysconf(_SC_PAGESIZE);
- if (session->page_size < 0) {
- ln_errno = LN_ERR_PAGESIZE;
- return -1;
- }
-
- //get major of the module, -1 if unloaded
- session->major = _get_major();
- if (session->major == -1) return -1;
-
- //build lainmemu device path
- snprintf(memu_path, PATH_MAX, "/dev/char/%d:%d",
- (unsigned char) session->major, LAINMEMU_MINOR);
-
- //open the lainmemu device
- session->fd_dev_memu = open(memu_path, O_RDWR);
- if (session->fd_dev_memu == -1) {
- ln_errno = LN_ERR_MEMU_OPEN;
- return -1;
- }
-
- //call ioctl to set target
- arg.target_pid = pid;
- ioctl_call = LAINKO_APPLY_TEMPLATE((char) session->major,
- LAINMEMU_TEMPLATE_OPEN_TGT);
- ret = ioctl(session->fd_dev_memu, ioctl_call, &arg);
- if (ret) {
- close(session->fd_dev_memu);
- ln_errno = LN_ERR_MEMU_TARGET;
- return -1;
- }
-
- return 0;
-}
-
-
-//close the target
-int _lainko_close(ln_session * session) {
-
- int ret;
- uint32_t ioctl_call;
- struct ioctl_arg arg; //not used
-
- //call ioctl to release target
- ioctl_call = LAINKO_APPLY_TEMPLATE((char) session->major,
- LAINMEMU_TEMPLATE_RELEASE_TGT);
- ret = ioctl(session->fd_dev_memu, ioctl_call, &arg);
-
- //close device
- close(session->fd_dev_memu);
-
- return 0;
-}
-
-
-//update the map
-int _lainko_update_map(const ln_session * session, ln_vm_map * vm_map) {
-
- int ret, count;
- uint32_t ioctl_call;
- size_t u_buf_sz;
-
- struct ioctl_arg arg;
- _traverse_state state;
-
-
- //call ioctl to get the map size
- ioctl_call = LAINKO_APPLY_TEMPLATE((char) session->major,
- LAINMEMU_TEMPLATE_GET_MAP_SZ);
- count = ioctl(session->fd_dev_memu, ioctl_call, &arg);
- if (count <= 0) {
- ln_errno = LN_ERR_MEMU_MAP_SZ;
- return -1;
- }
-
- //allocate buffer to hold the map
- u_buf_sz = count * sizeof(struct vm_entry);
- arg.u_buf = mmap(NULL, u_buf_sz,
- PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (arg.u_buf == MAP_FAILED) {
- ln_errno = LN_ERR_MEM;
- return -1;
- }
-
- //get the map
- ioctl_call = LAINKO_APPLY_TEMPLATE((char) session->major,
- LAINMEMU_TEMPLATE_GET_MAP);
-
- count = ioctl(session->fd_dev_memu, ioctl_call, &arg);
- if (count <= 0) {
- munmap(arg.u_buf, u_buf_sz);
- ln_errno = LN_ERR_MEMU_MAP_GET;
- return -1;
- }
-
-
- //update the map with each received segment
- _map_init_traverse_state(vm_map, &state);
-
- for (int i = 0; i < count; ++i) {
-
- ret = _map_send_entry(vm_map, &state,
- (struct vm_entry *)
- (arg.u_buf + (i * sizeof(struct vm_entry))));
- if (ret) {
- munmap(arg.u_buf, u_buf_sz);
- return -1;
- }
- } //end for
-
- //unmap map buffer
- munmap(arg.u_buf, u_buf_sz);
-
- return 0;
-}
-
-
-//read memory
-int _lainko_read(const ln_session * session, const uintptr_t addr,
- cm_byte * buf, const size_t buf_sz) {
-
- off_t off_ret;
- ssize_t read_bytes, read_done, read_left;
-
- read_done = read_left = 0;
-
- //seek to address
- off_ret = lseek(session->fd_dev_memu, (off_t) addr, SEEK_SET);
- if (off_ret == -1) {
- ln_errno = LN_ERR_SEEK_ADDR;
- return -1;
- }
-
- //read page_size bytes repeatedly until done
- do {
-
- //calc how many bytes left to read
- read_left = buf_sz - read_done;
-
- //read into buffer
- read_bytes = read(session->fd_dev_memu, buf + read_done,
- read_left > session->page_size
- ? session->page_size : read_left);
- //if error or EOF before reading len bytes
- if (read_bytes == -1 || (read_bytes == 0 && read_done < buf_sz)) {
- ln_errno = LN_ERR_READ_WRITE;
- return -1;
- }
- read_done += read_bytes;
-
- } while (read_done < buf_sz);
-
- return 0;
-}
-
-
-//write memory
-int _lainko_write(const ln_session * session, const uintptr_t addr,
- const cm_byte * buf, const size_t buf_sz) {
-
- off_t off_ret;
- ssize_t write_bytes, write_done, write_left;
-
- write_done = write_left = 0;
-
- //seek to address
- off_ret = lseek(session->fd_dev_memu, (off_t) addr, SEEK_SET);
- if (off_ret == -1) {
- ln_errno = LN_ERR_SEEK_ADDR;
- return -1;
- }
-
- //write page_size bytes repeatedly until done
- do {
-
- //calc how many bytes left to write
- write_left = buf_sz - write_done;
-
- //write into buffer
- write_bytes = write(session->fd_dev_memu, buf + write_done,
- write_left > session->page_size
- ? session->page_size : write_left);
- //if error or EOF before writing len bytes
- if (write_bytes == -1 || (write_bytes == 0 && write_done < buf_sz)) {
- ln_errno = LN_ERR_READ_WRITE;
- return -1;
- }
- write_done += write_bytes;
-
- } while (write_done < buf_sz);
-
- return 0;
-}
diff --git a/src/lib/lainko_iface.h b/src/lib/lainko_iface.h
deleted file mode 100644
index 386b34e..0000000
--- a/src/lib/lainko_iface.h
+++ /dev/null
@@ -1,21 +0,0 @@
-#ifndef LAINKO_IFACE_H
-#define LAINKO_IFACE_H
-
-#include
-
-#include "liblain.h"
-
-
-#define LAINKO_MAJOR_PATH "/sys/class/lainko/lainmemu_major"
-#define LAINMEMU_MINOR 0
-
-//internal
-int _lainko_open(ln_session * session, const pid_t pid);
-int _lainko_close(ln_session * session);
-int _lainko_update_map(const ln_session * session, ln_vm_map * vm_map);
-int _lainko_read(const ln_session * session, const uintptr_t addr,
- cm_byte * buf, const size_t buf_sz);
-int _lainko_write(const ln_session * session, const uintptr_t addr,
- const cm_byte * buf, const size_t buf_sz);
-
-#endif
diff --git a/src/lib/liblain.h b/src/lib/liblain.h
deleted file mode 100644
index 56afdd7..0000000
--- a/src/lib/liblain.h
+++ /dev/null
@@ -1,258 +0,0 @@
-#ifndef LIBMOD_H
-#define LIBMOD_H
-
-#ifdef __cplusplus
-extern "C"{
-#endif
-
-#include
-#include
-#include
-
-#include
-
-#include
-
-#include
-
-
-//these macros take a cm_list_node pointer
-#define LN_GET_NODE_AREA(node) ((ln_vm_area *) (node->data))
-#define LN_GET_NODE_OBJ(node) ((ln_vm_obj *) (node->data))
-#define LN_GET_NODE_PTR(node) *((cm_list_node **) (node->data))
-
-
-//ln_vm_area.access bitmasks
-#define LN_ACCESS_READ 0x01
-#define LN_ACCESS_WRITE 0x02
-#define LN_ACCESS_EXEC 0x04
-#define LN_ACCESS_SHARED 0x08
-
-
-//interface types
-#define LN_IFACE_LAINKO 0
-#define LN_IFACE_PROCFS 1
-
-
-//
-#define ZERO_OBJ_ID -1
-
-
-/*
- * --- [DATA TYPES] ---
- */
-
-
-struct _ln_vm_obj;
-
-// --- [memory area]
-typedef struct {
-
- char * pathname;
- char * basename;
-
- uintptr_t start_addr;
- uintptr_t end_addr;
-
- cm_byte access;
-
- cm_list_node * obj_node_ptr; //STORES: own vm_obj *
- cm_list_node * last_obj_node_ptr; //STORES: last encountered vm_obj *
-
- int id;
- bool mapped; //can be set to false with map update
-
-} ln_vm_area;
-
-
-// --- ['backing' object]
-struct _ln_vm_obj {
-
- char pathname[PATH_MAX];
- char basename[NAME_MAX];
-
- uintptr_t start_addr;
- uintptr_t end_addr;
-
- cm_list vm_area_node_ptrs; //STORES: cm_list_node * of ln_vm_area
-
- int id;
- bool mapped; //can be set to false with map update
-};
-typedef struct _ln_vm_obj ln_vm_obj;
-
-
-// --- [memory map]
-typedef struct {
-
- //up to date entries
- cm_list vm_areas; //STORES: ln_vm_area
- cm_list vm_objs; //STORES: ln_vm_obj
-
- //unmapped entries (storage for future deallocation)
- cm_list vm_areas_unmapped; //STORES: cm_list_node * of ln_vm_area
- cm_list vm_objs_unmapped; //STORES: cm_list_node * of ln_vm_obj
-
- // [internal]
- int next_id_area;
- int next_id_obj;
-
-} ln_vm_map;
-
-
-// --- [interface / session]
-struct _ln_session;
-
-typedef struct {
-
- int (*open)(struct _ln_session *, int);
- int (*close)(struct _ln_session *);
- int (*update_map)(const struct _ln_session *, ln_vm_map *);
- int (*read)(const struct _ln_session *, const uintptr_t,
- cm_byte *, const size_t);
- int (*write)(const struct _ln_session *, const uintptr_t,
- const cm_byte *, const size_t);
-
-} ln_iface;
-
-
-struct _ln_session {
-
- union {
- struct {
- int fd_mem;
- pid_t pid;
- }; //procfs_data
- struct {
- char major;
- int fd_dev_memu;
- }; //lainko_data
- };
-
- long page_size;
- ln_iface iface;
-
-};
-typedef struct _ln_session ln_session;
-
-
-/*
- * --- [FUNCTIONS] ---
- */
-
-// --- [utils]
-//return: basename = success, NULL = fail/error
-extern const char * ln_pathname_to_basename(const char * pathname);
-//must destroy 'pid_vector' manually on success | pid = success, -1 = fail/error
-extern pid_t ln_pid_by_name(const char * comm, cm_vector * pid_vector);
-//return: 0 = success, -1 = fail/error
-extern int ln_name_by_pid(const pid_t pid, char * name_buf);
-//'out' must have space for double the length of 'inp' + 1
-extern void ln_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out);
-
-
-// --- [virtual interface (session)]
-//all return 0 = success, -1 = fail/error
-extern int ln_open(ln_session * session, const int iface, const pid_t pid);
-extern int ln_close(ln_session * session);
-extern int ln_update_map(const ln_session * session, ln_vm_map * vm_map);
-extern int ln_read(const ln_session * session, const uintptr_t addr,
- cm_byte * buf, const size_t buf_sz);
-extern int ln_write(const ln_session * session, const uintptr_t addr,
- const cm_byte * buf, const size_t buf_sz);
-
-// --- [map operations]
-//all return 0 = success, -1 = fail/error
-extern void ln_new_vm_map(ln_vm_map * vm_map);
-extern int ln_del_vm_map(ln_vm_map * vm_map);
-extern int ln_map_clean_unmapped(ln_vm_map * vm_map);
-
-
-// --- [map utils]
-//return: offset from start of area/object
-extern off_t ln_get_area_offset(const cm_list_node * area_node, const uintptr_t addr);
-extern off_t ln_get_obj_offset(const cm_list_node * obj_node, const uintptr_t addr);
-//return: offset from start of area/object = success, -1 = not in area/object
-extern off_t ln_get_area_offset_bnd(const cm_list_node * area_node,
- const uintptr_t addr);
-extern off_t ln_get_obj_offset_bnd(const cm_list_node * obj_node,
- const uintptr_t addr);
-//return area node * = success, NULL = fail/error
-extern cm_list_node * ln_get_vm_area_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, off_t * offset);
-//return obj node * = success, NULL = fail/error
-extern cm_list_node * ln_get_vm_obj_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, off_t * offset);
-extern cm_list_node * ln_get_vm_obj_by_pathname(const ln_vm_map * vm_map,
- const char * pathname);
-extern cm_list_node * ln_get_vm_obj_by_basename(const ln_vm_map * vm_map,
- const char * basename);
-
-
-// --- [error handling]
-//void return
-extern void ln_perror();
-extern const char * ln_strerror(const int ln_errnum);
-
-
-/*
- * --- [ERROR HANDLING] ---
- */
-
-extern __thread int ln_errno;
-
-// [error codes]
-
-// 1XX - user errors
-#define LN_ERR_PROC_MEM 2100
-#define LN_ERR_PROC_MAP 2101
-#define LN_ERR_SEEK_ADDR 2102
-
-// 2XX - internal errors
-#define LN_ERR_INTERNAL_INDEX 2200
-#define LN_ERR_UNEXPECTED_NULL 2201
-#define LN_ERR_LIBCMORE 2202
-#define LN_ERR_READ_WRITE 2203
-#define LN_ERR_MEMU_TARGET 2204
-#define LN_ERR_MEMU_MAP_SZ 2205
-#define LN_ERR_MEMU_MAP_GET 2206
-#define LN_ERR_PROC_STATUS 2207
-#define LN_ERR_PROC_NAV 2208
-
-// 3XX - environmental errors
-#define LN_ERR_MEM 2300
-#define LN_ERR_PAGESIZE 2301
-#define LN_ERR_LAINKO_MAJOR 2302
-#define LN_ERR_MEMU_OPEN 2303
-
-
-// [error code messages]
-
-// 1XX - user errors
-#define LN_ERR_PROC_MEM_MSG "Could not open /proc//mem for specified pid.\n"
-#define LN_ERR_PROC_MAP_MSG "Could not open /proc//maps for specified pid.\n"
-#define LN_ERR_SEEK_ADDR_MSG "Could not seek to specified address.\n"
-
-// 2XX - internal errors
-#define LN_ERR_INTERNAL_INDEX_MSG "Internal: Indexing error.\n"
-#define LN_ERR_UNEXPECTED_NULL_MSG "Internal: Unexpected NULL pointer.\n"
-#define LN_ERR_LIBCMORE_MSG "Internal: Libcmore error. See cm_perror().\n"
-#define LN_ERR_READ_WRITE_MSG "Internal: Read/write failed.\n"
-#define LN_ERR_MEMU_TARGET_MSG "Internal: Lainko target open failed.\n"
-#define LN_ERR_MEMU_MAP_SZ_MSG "Internal: Lainko map size fetch failed..\n"
-#define LN_ERR_MEMU_MAP_GET_MSG "Internal: Lainko map transfer failed.\n"
-#define LN_ERR_PROC_STATUS_MSG "Internal: Failed to use /proc//status.\n"
-#define LN_ERR_PROC_NAV_MSG "Internal: Failed to navigate /proc directories.\n"
-
-// 3XX - environmental errors
-#define LN_ERR_MEM_MSG "Failed to acquire the necessary memory.\n"
-#define LN_ERR_PAGESIZE_MSG "Unable to fetch pagesize through sysconf().\n"
-#define LN_ERR_LAINKO_MAJOR_MSG "Could not fetch lainko's major number.\n"
-#define LN_ERR_MEMU_OPEN_MSG "Could not open lainmemu device.\n"
-
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif
diff --git a/src/lib/map.c b/src/lib/map.c
index 1a1bae7..c06c521 100644
--- a/src/lib/map.c
+++ b/src/lib/map.c
@@ -1,275 +1,603 @@
+//standard library
#include
#include
#include
+//system headers
#include
-#include
+//external libraries
+#include
+#include
-#include "liblain.h"
+//local headers
+#include "memcry.h"
#include "map.h"
#include "util.h"
+#include "debug.h"
-// --- INTERNAL CONSTRUCTORS & DESTRUCTORS
+/*
+ * --- [INTERNAL] ---
+ */
+
+/*
+ * Note that while the vm_area initialiser function does take a vm_obj node
+ * as a parameter, the state of this vm_obj is not changed.
+ */
+
+DBG_STATIC
+void _map_init_vm_area(mc_vm_area * area, const struct vm_entry * entry,
+ const cm_lst_node * obj_node,
+ const cm_lst_node * last_obj_node, mc_vm_map * map) {
-//ln_vm_area constructor
-static void _new_vm_area(ln_vm_area * vm_area, ln_vm_map * vm_map,
- const cm_list_node * obj_node,
- const cm_list_node * last_obj_node,
- const struct vm_entry * entry) {
+ mc_vm_obj * obj;
- ln_vm_obj * vm_obj;
+
+ //zero the area
+ memset(area, 0, sizeof(*area));
//set pathname for area if applicable
if (obj_node != NULL) {
- vm_obj = LN_GET_NODE_OBJ(obj_node);
+ obj = MC_GET_NODE_OBJ(obj_node);
- vm_area->pathname = vm_obj->pathname;
- vm_area->basename = vm_obj->basename;
+ area->pathname = obj->pathname;
+ area->basename = obj->basename;
} else {
- vm_area->pathname = NULL;
- vm_area->basename = NULL;
+ area->pathname = NULL;
+ area->basename = NULL;
} //end if
- vm_area->obj_node_ptr = (cm_list_node *) obj_node;
- vm_area->last_obj_node_ptr = (cm_list_node *) last_obj_node;
+ area->obj_node_p = (cm_lst_node *) obj_node;
+ area->last_obj_node_p = (cm_lst_node *) last_obj_node;
- vm_area->start_addr = entry->vm_start;
- vm_area->end_addr = entry->vm_end;
+ area->start_addr = entry->vm_start;
+ area->end_addr = entry->vm_end;
- vm_area->access = (cm_byte) (entry->prot & 0x0000000F);
+ area->access = (cm_byte) (entry->prot & 0x0000000F);
- vm_area->mapped = true;
+ area->mapped = true;
//set id & increment map's next id
- vm_area->id = vm_map->next_id_area;
- ++vm_map->next_id_area;
+ area->id = map->next_id_area;
+ ++map->next_id_area;
return;
}
-//ln_vm_obj constructor
-static void _new_vm_obj(ln_vm_obj * vm_obj, ln_vm_map * vm_map,
- const char * pathname) {
+/*
+ * Note that while the vm_obj constructor does take a vm_map pointer as a
+ * parameter, the vm_obj is not added to the vm_map in the constructor. Only
+ * the `next_id_obj` counter is incremented.
+ */
+
+DBG_STATIC
+void _map_new_vm_obj(mc_vm_obj * obj,
+ mc_vm_map * map, const char * pathname) {
+
+ size_t len;
- const char * basename = ln_pathname_to_basename(pathname);
- strncpy(vm_obj->pathname, pathname, PATH_MAX);
- strncpy(vm_obj->basename, basename, NAME_MAX);
+ //zero the obj
+ memset(obj, 0, sizeof(*obj));
- vm_obj->start_addr = 0x0;
- vm_obj->end_addr = 0x0;
+ //allocate space for the pathname
+ len = strnlen(pathname, PATH_MAX);
+ obj->pathname = calloc(sizeof(char), len + 1);
+
+ //construct pathname
+ strncpy(obj->pathname, pathname, len);
+ obj->basename = mc_pathname_to_basename(obj->pathname);
+
+ obj->start_addr = MC_UNDEF_ADDR;
+ obj->end_addr = MC_UNDEF_ADDR;
//initialise area list
- cm_new_list(&vm_obj->vm_area_node_ptrs, sizeof(cm_list_node *));
+ cm_new_lst(&obj->vm_area_node_ps, sizeof(cm_lst_node *));
+ cm_new_lst(&obj->last_vm_area_node_ps, sizeof(cm_lst_node *));
- vm_obj->mapped = true;
+ obj->mapped = true;
//set id & increment map's next id
- vm_obj->id = vm_map->next_id_obj;
- ++vm_map->next_id_obj;
+ obj->id = map->next_id_obj;
+ ++map->next_id_obj;
return;
}
-//ln_vm_obj destructor
-static void _del_vm_obj(ln_vm_obj * vm_obj) {
+DBG_STATIC
+void _map_del_vm_obj(mc_vm_obj * obj) {
+
+ free(obj->pathname);
+ cm_del_lst(&obj->vm_area_node_ps);
+ cm_del_lst(&obj->last_vm_area_node_ps);
+
+ return;
+}
+
- cm_del_list(&vm_obj->vm_area_node_ptrs);
+DBG_STATIC DBG_INLINE
+void _map_make_zero_obj(mc_vm_obj * obj) {
+ obj->id = MC_ZERO_OBJ_ID;
+ obj->start_addr = obj->end_addr = 0x0;
+
return;
}
+DBG_STATIC
+int _map_obj_add_area_insert(cm_lst * obj_area_lst,
+ const cm_lst_node * area_node) {
-// --- MAP GENERATION INTERNALS
+ cm_lst_node * ret_node, * iter_node, * inner_node;
+ mc_vm_area * area = MC_GET_NODE_AREA(area_node);
-//modify object to act as pseudo object
-static inline void _make_zero_obj(ln_vm_obj * vm_obj) {
+ //if list is empty, append
+ if (obj_area_lst->len == 0) {
- vm_obj->id = ZERO_OBJ_ID;
+ cm_lst_apd(obj_area_lst, &area_node);
+
+ //list is not empty, find appropriate insert point
+ } else {
- return;
+ ret_node = NULL;
+
+ //setup iteration
+ iter_node = obj_area_lst->head;
+ for (int i = 0; i < obj_area_lst->len; ++i) {
+
+ //get area node that iter_node points to
+ inner_node = MC_GET_NODE_PTR(iter_node);
+
+ //new area ends at a lower address than some existing area
+ if (area->end_addr < MC_GET_NODE_AREA(inner_node)->end_addr) {
+
+ ret_node = cm_lst_ins_nb(obj_area_lst, iter_node, &area_node);
+ break;
+ }
+
+ //new area ends later than any existing area
+ if ((iter_node->next == NULL)
+ || (iter_node->next == obj_area_lst->head)) {
+
+ ret_node = cm_lst_ins_na(obj_area_lst, iter_node, &area_node);
+ break;
+ }
+
+ //increment node iteration
+ iter_node = iter_node->next;
+ }
+
+ //check the new area was inserted successfully
+ if (ret_node == NULL) {
+ mc_errno = MC_ERR_CMORE;
+ return -1;
+ }
+ }
+
+ return 0;
}
-//add an area to an obj (is not called
-static inline int _obj_add_area(ln_vm_obj * obj, const cm_list_node * area_node) {
+DBG_STATIC
+cm_lst_node * _map_obj_find_area_outer_node(cm_lst * obj_area_lst,
+ cm_lst_node * area_node) {
- cm_list_node * ret_node;
- ln_vm_area * area = LN_GET_NODE_AREA(area_node);
+ cm_lst_node * iter_node;
- //if this object has no areas yet
- if (obj->start_addr == -1 || obj->start_addr == 0x0) {
+ //list should never be empty
+ if (obj_area_lst->len == 0) {
+
+ mc_errno = MC_ERR_AREA_IN_OBJ;
+ return NULL;
+ }
+
+ //setup iteration
+ iter_node = obj_area_lst->head;
+ for (int i = 0; i < obj_area_lst->len; ++i) {
+
+ if (area_node == MC_GET_NODE_PTR(iter_node)) return iter_node;
+ iter_node = iter_node->next;
+ }
+
+ mc_errno = MC_ERR_AREA_IN_OBJ;
+ return NULL;
+}
+
+
+// This function only updates the vm_obj structure itself, not the area list
+DBG_STATIC DBG_INLINE
+int _map_obj_add_area(mc_vm_obj * obj,
+ const cm_lst_node * area_node) {
+
+ int ret;
+ mc_vm_area * area = MC_GET_NODE_AREA(area_node);
+
+
+ //if this object has no areas yet
+ if (obj->start_addr == MC_UNDEF_ADDR) {
//set new addr bounds
obj->start_addr = area->start_addr;
obj->end_addr = area->end_addr;
-
+
//if this object has areas, only update the start and end addr if necessary
} else {
-
//set new addr bounds if necessary
- if (area->start_addr < obj->start_addr) obj->start_addr = area->start_addr;
- if (area->end_addr > obj->end_addr) obj->end_addr = area->end_addr;
+ if (area->start_addr < obj->start_addr)
+ obj->start_addr = area->start_addr;
+ if (area->end_addr > obj->end_addr)
+ obj->end_addr = area->end_addr;
}
+
+ //insert new area & ensure the area list remains sorted
+ ret = _map_obj_add_area_insert(&obj->vm_area_node_ps, area_node);
+ if (ret) return -1;
+
+ return 0;
+}
+
+
+DBG_STATIC DBG_INLINE
+int _map_obj_add_last_area(mc_vm_obj * obj,
+ const cm_lst_node * last_area_node) {
+
+ int ret;
- //simply appending preserves chronological order; out of order vm_areas
- //are guaranteed(?) to be removed
- ret_node = cm_list_append(&obj->vm_area_node_ptrs, (const cm_byte *) &area_node);
- if (ret_node == NULL) {
- ln_errno = LN_ERR_LIBCMORE;
+ //insert new last area & ensure the last area list remains sorted
+ ret = _map_obj_add_area_insert(&obj->last_vm_area_node_ps, last_area_node);
+ if (ret) return -1;
+
+ return 0;
+}
+
+
+DBG_STATIC
+int _map_obj_rmv_area_fast(mc_vm_obj * obj, cm_lst_node * outer_area_node) {
+
+ int ret, index;
+ cm_lst_node * temp_node;
+
+
+ //remove area node from object
+ ret = cm_lst_rmv_n(&obj->vm_area_node_ps, outer_area_node);
+ if (ret != 0) {
+ mc_errno = MC_ERR_CMORE;
+ return -1;
+ }
+
+
+ //update object's address range
+
+ //if no area nodes left, address range is now undefined (unmapped)
+ if (obj->vm_area_node_ps.len == 0) {
+ obj->end_addr = obj->start_addr = MC_UNDEF_ADDR;
+ return 0;
+ }
+
+ //get start addr
+ ret = cm_lst_get(&obj->vm_area_node_ps, 0, &temp_node);
+ if (ret) {
+ mc_errno = MC_ERR_CMORE;
return -1;
}
+ obj->start_addr = MC_GET_NODE_AREA(temp_node)->start_addr;
+
+ //get end addr
+ index = obj->vm_area_node_ps.len == 1 ? 0 : -1;
+
+ if (index != 0) {
+ ret = cm_lst_get(&obj->vm_area_node_ps, index, &temp_node);
+ if (ret) {
+ mc_errno = MC_ERR_CMORE;
+ return -1;
+ }
+ }
+
+ obj->end_addr = MC_GET_NODE_AREA(temp_node)->end_addr;
+
return 0;
}
-//find if vm area's pathname belongs in object
-static bool _is_pathname_in_obj(const char * pathname, const ln_vm_obj * obj) {
+DBG_STATIC DBG_INLINE
+int _map_obj_rmv_area(mc_vm_obj * obj, cm_lst_node * inner_area_node) {
+
+ int ret;
+ cm_lst_node * outer_area_node;
+
+ //find index of area in object
+ outer_area_node = _map_obj_find_area_outer_node(&obj->vm_area_node_ps,
+ inner_area_node);
+ if (outer_area_node == NULL) {
+ mc_errno = MC_ERR_AREA_IN_OBJ;
+ return -1;
+ }
+
+ ret = _map_obj_rmv_area_fast(obj, outer_area_node);
+ if (ret != 0) return -1;
+
+ return 0;
+}
+
+
+DBG_STATIC
+int _map_obj_rmv_last_area_fast(mc_vm_obj * obj,
+ cm_lst_node * outer_area_node) {
+
+ int ret;
+
+ ret = cm_lst_rmv_n(&obj->last_vm_area_node_ps, outer_area_node);
+ if (ret != 0) {
+ mc_errno = MC_ERR_CMORE;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+DBG_STATIC DBG_INLINE
+int _map_obj_rmv_last_area(mc_vm_obj * obj,
+ cm_lst_node * inner_area_node) {
+
+ int ret;
+ cm_lst_node * outer_area_node;
+
+
+ //remove area node from the object
+
+ //find index of area in object
+ outer_area_node = _map_obj_find_area_outer_node(&obj->last_vm_area_node_ps,
+ inner_area_node);
+ if (outer_area_node == NULL) {
+ mc_errno = MC_ERR_AREA_IN_OBJ;
+ return -1;
+ }
+
+ ret = _map_obj_rmv_last_area_fast(obj, outer_area_node);
+ if (ret != 0) return -1;
+
+ return 0;
+}
+
+
+DBG_STATIC
+bool _map_is_pathname_in_obj(const char * pathname, const mc_vm_obj * obj) {
if (obj == NULL) return false;
+ //if looking at pseudo object, '\0' pathname is a match
+ if (obj->id == MC_ZERO_OBJ_ID) {
+ if (*pathname == '\0') return true;
+ return false;
+ }
+
+ //else require a string match
bool ret = (strncmp(pathname, obj->pathname, PATH_MAX) ? false : true);
return ret;
}
-#define _MAP_OBJ_PREV 0
-#define _MAP_OBJ_NEW 1
-#define _MAP_OBJ_NEXT 2
-static inline int _find_obj_for_area(const _traverse_state * state,
- const struct vm_entry * entry) {
+DBG_STATIC DBG_INLINE
+int _map_find_obj_for_area(const struct vm_entry * entry,
+ const _traverse_state * state) {
- cm_list_node * prev_node, * next_node;
- ln_vm_obj * prev_obj, * next_obj;
+ cm_lst_node * prev_node, * next_node;
+ mc_vm_obj * prev_obj, * next_obj;
//check for null
- if (state->prev_obj == NULL) return _MAP_OBJ_NEW;
+ if (state->prev_obj_node == NULL) return _MAP_OBJ_NEW;
//get prev obj
- prev_node = state->prev_obj;
- prev_obj = LN_GET_NODE_OBJ(prev_node);
+ prev_node = state->prev_obj_node;
+ prev_obj = MC_GET_NODE_OBJ(prev_node);
if (prev_node->next == NULL) {
next_obj = NULL;
} else {
next_node = prev_node->next;
- next_obj = LN_GET_NODE_OBJ(next_node);
+ next_obj = MC_GET_NODE_OBJ(next_node);
}
//return appropriate index
- if (_is_pathname_in_obj(entry->file_path, prev_obj)) return _MAP_OBJ_PREV;
- if (_is_pathname_in_obj(entry->file_path, next_obj)) return _MAP_OBJ_NEXT;
+ if (_map_is_pathname_in_obj(entry->file_path, prev_obj))
+ return _MAP_OBJ_PREV;
+ if (_map_is_pathname_in_obj(entry->file_path, next_obj))
+ return _MAP_OBJ_NEXT;
return _MAP_OBJ_NEW;
}
-//find index of vm area in its corresponding object
-static inline int _get_area_index(const ln_vm_area * area, const ln_vm_obj * obj) {
+//called when deleting an object; moves `last_obj_node_p` back if needed
+DBG_STATIC DBG_INLINE
+int _map_backtrack_unmapped_obj_last_vm_areas(cm_lst_node * obj_node) {
int ret;
+ cm_lst_node * ret_p;
+
+ int iterations;
- cm_list_node * temp_node;
- ln_vm_area * temp_area;
+ mc_vm_area * last_area;
+ cm_lst_node * last_area_node, * temp_area_node;
- //for all vm areas in this object
- for (int i = 0; i < obj->vm_area_node_ptrs.len; ++i) {
+ mc_vm_obj * temp_obj, * temp_prev_obj;
- ret = cm_list_get_val(&obj->vm_area_node_ptrs, i, (cm_byte *) &temp_node);
- if (ret) return -1;
- temp_area = LN_GET_NODE_AREA(temp_node);
-
- if (temp_area->id == area->id) return i;
-
- } //end for
- return -1;
-}
+ //setup iteration
+ temp_obj = MC_GET_NODE_OBJ(obj_node);
+ temp_prev_obj = MC_GET_NODE_OBJ(obj_node->prev);
+ //get first node
+ last_area_node = temp_obj->last_vm_area_node_ps.head;
+ if (last_area_node == NULL) return 0;
-//find the new start and end addresses for an object
-static inline int _update_obj_addr_range(ln_vm_obj * obj) {
+ //get last area from first node
+ temp_area_node = MC_GET_NODE_PTR(last_area_node);
+ last_area = MC_GET_NODE_AREA(temp_area_node);
- int ret, end_index;
- cm_list_node * temp_node;
- ln_vm_area * temp_area;
+ //for every area, backtrack last object pointer
+ iterations = temp_obj->last_vm_area_node_ps.len;
+ for (int i = 0; i < iterations; ++i) {
- //get start addr
- ret = cm_list_get_val(&obj->vm_area_node_ptrs, 0, (cm_byte *) &temp_node);
- if (ret) {
- ln_errno = LN_ERR_LIBCMORE;
- return -1;
- }
+ //update pointer
+ last_area->last_obj_node_p = obj_node->prev;
+ ret_p = cm_lst_apd(&temp_prev_obj->last_vm_area_node_ps,
+ &temp_area_node);
+ if (ret_p == NULL) {
+ mc_errno = MC_ERR_CMORE;
+ return -1;
+ }
- temp_area = LN_GET_NODE_AREA(temp_node);
- obj->start_addr = temp_area->start_addr;
+ //advance iteration (part 1)
+ last_area_node = last_area_node->next;
+ if (last_area_node != NULL) {
+ temp_area_node = MC_GET_NODE_PTR(last_area_node);
+ }
- //get end addr
- if (obj->vm_area_node_ptrs.len == 1) {
- end_index = 0;
- } else {
- end_index = -1;
- }
-
- ret = cm_list_get_val(&obj->vm_area_node_ptrs, end_index, (cm_byte *) &temp_node);
- if (ret) {
- ln_errno = LN_ERR_LIBCMORE;
- return -1;
+ //remove this area from the object's last_vm_area_node_ps list
+ ret = cm_lst_rmv(&temp_obj->last_vm_area_node_ps, 0);
+ if (ret == -1) {
+ mc_errno = MC_ERR_CMORE;
+ return -1;
+ }
+
+ //advance iteration (part 2)
+ if (last_area_node != NULL) {
+ last_area = MC_GET_NODE_AREA(temp_area_node);
+ }
}
- temp_area = LN_GET_NODE_AREA(temp_node);
- obj->end_addr = temp_area->end_addr;
+ return 0;
+}
+
+
+//called when adding an object; moves `last_obj_node_p` forward if needed
+DBG_STATIC DBG_INLINE
+int _map_forward_unmapped_obj_last_vm_areas(cm_lst_node * obj_node) {
+
+ int ret;
+
+ cm_lst_node * last_area_node, * temp_area_node, * prev_obj_node;
+ mc_vm_area * last_area;
+ mc_vm_obj * obj, * prev_obj;
+
+
+ //setup objects
+ obj = MC_GET_NODE_OBJ(obj_node);
+ prev_obj_node = obj_node->prev;
+ prev_obj = MC_GET_NODE_OBJ(prev_obj_node);
+
+
+ //setup iteration
+ last_area_node = prev_obj->last_vm_area_node_ps.head;
+ if (last_area_node == NULL) return 0;
+
+ temp_area_node = MC_GET_NODE_PTR(last_area_node);
+ last_area = MC_GET_NODE_AREA(temp_area_node);
+
+ //for every area, move last object pointer forward if necessary
+ for (int i = 0; i < prev_obj->last_vm_area_node_ps.len; ++i) {
+
+ //if this area's address range comes completely after this object
+ if (last_area->start_addr >= obj->end_addr) {
+
+ //set this object as the new last object pointer
+ last_area->last_obj_node_p = obj_node;
+
+ //add this area to this object's last_vm_area_node_ps list
+ ret = _map_obj_add_last_area(obj, temp_area_node);
+ if (ret == -1) return -1;
+
+ //advance iteration (part 1)
+ temp_area_node = last_area_node->next;
+
+ //remove this area from the previous
+ //last object's last_vm_area_node_ps
+ ret =_map_obj_rmv_last_area_fast(prev_obj, last_area_node);
+ if (ret == -1) return -1;
+
+ //correct iteration index
+ i -= 1;
+
+ //otherwise just update iteration
+ } else {
+ temp_area_node = last_area_node->next;
+ }
+
+ //advance iteration
+ if (temp_area_node != NULL) {
+
+ last_area_node = temp_area_node;
+ temp_area_node = MC_GET_NODE_PTR(last_area_node);
+ last_area = MC_GET_NODE_AREA(temp_area_node);
+ }
+
+ } //end for
return 0;
}
-//correctly remove unmapped obj node
-static inline int _unlink_unmapped_obj(cm_list_node * node,
- const _traverse_state * state,
- ln_vm_map * vm_map) {
+DBG_STATIC DBG_INLINE
+int _map_unlink_unmapped_obj(cm_lst_node * obj_node,
+ _traverse_state * state, mc_vm_map * map) {
int ret;
- cm_list_node * ret_node;
- ln_vm_obj * temp_obj;
+ cm_lst_node * ret_node;
+
+ mc_vm_obj * obj;
+
+ //backtrack traverse state if it is currently on this object
+ if (obj_node == state->prev_obj_node) {
+ state->prev_obj_node = state->prev_obj_node->prev;
+ }
- //if this is the pseudo object, just reset it
- temp_obj = LN_GET_NODE_OBJ(node);
- if (temp_obj->id == ZERO_OBJ_ID) {
- temp_obj->start_addr = 0x0;
- temp_obj->end_addr = 0x0;
+ //fetch object
+ obj = MC_GET_NODE_OBJ(obj_node);
+
+ //if this is the pseydo object, just reset it
+ if (obj->id == MC_ZERO_OBJ_ID) {
+ obj->start_addr = 0x0;
+ obj->end_addr = 0x0;
return 0;
}
- //unlink this node from the list of mapped vm areas
- ret = cm_list_unlink(&vm_map->vm_objs, state->prev_obj_index + 1);
- if (ret) {
- ln_errno = LN_ERR_LIBCMORE;
+ //correct last_obj_node_p of every vm_area
+ //with this object as its last object
+ ret = _map_backtrack_unmapped_obj_last_vm_areas(obj_node);
+ if (ret == -1) return -1;
+
+ //unlink this node from the list of mapped vm objs
+ ret_node = cm_lst_uln_n(&map->vm_objs, obj_node);
+ if (ret_node != obj_node) {
+ mc_errno = MC_ERR_CMORE;
return -1;
}
- //set node's values to be unmapped
- (LN_GET_NODE_OBJ(node))->mapped = false;
- node->next = NULL;
- node->prev = NULL;
+ //disconnect object node's attributes
+ obj->mapped = false;
+ obj->start_addr = MC_UNDEF_ADDR;
+ obj->end_addr = MC_UNDEF_ADDR;
+ obj_node->next = NULL;
+ obj_node->prev = NULL;
- //add a pointer to this node to the list containing nodes to dealloc later
- ret_node = cm_list_append(&vm_map->vm_objs_unmapped, (cm_byte *) &node);
+ //move this object node to the unmapped list
+ ret_node = cm_lst_apd(&map->vm_objs_unmapped, &obj_node);
if (ret_node == NULL) {
- ln_errno = LN_ERR_LIBCMORE;
+ mc_errno = MC_ERR_CMORE;
return -1;
}
@@ -277,73 +605,71 @@ static inline int _unlink_unmapped_obj(cm_list_node * node,
}
-//correctly remove unmapped area node
-static inline int _unlink_unmapped_area(cm_list_node * node,
- const _traverse_state * state,
- ln_vm_map * vm_map) {
+DBG_STATIC DBG_INLINE
+int _map_unlink_unmapped_area(cm_lst_node * area_node,
+ _traverse_state * state, mc_vm_map * map) {
- int ret, index;
-
- cm_list_node * obj_node, * temp_node;
-
- ln_vm_area * temp_area;
- ln_vm_obj * temp_obj;
+ int ret;
+ cm_lst_node * ret_node;
+
+ mc_vm_area * area;
+
+ mc_vm_obj * obj;
+ cm_lst_node * obj_node;
//get vm area of node
- temp_area = LN_GET_NODE_AREA(node);
+ area = MC_GET_NODE_AREA(area_node);
-
//remove this area from its parent object if necessary
- if (temp_area->obj_node_ptr != NULL) {
+ if (area->obj_node_p != NULL) {
- obj_node = temp_area->obj_node_ptr;
- temp_obj = LN_GET_NODE_OBJ(obj_node);
-
- //find the index for this area in the obj
- index = _get_area_index(temp_area, temp_obj);
- if (index != -1) {
- ret = cm_list_remove(&temp_obj->vm_area_node_ptrs, index);
- if (ret) {
- ln_errno = LN_ERR_LIBCMORE;
- return -1;
- }
- } else {
- ln_errno = LN_ERR_INTERNAL_INDEX;
- return -1;
- }
+ obj_node = area->obj_node_p;
+ obj = MC_GET_NODE_OBJ(obj_node);
+
+ //remove this area from the object
+ ret = _map_obj_rmv_area(obj, area_node);
+ if (ret == -1) return -1;
- //if the object now has no areas, set it to be unmapped
- if (temp_obj->vm_area_node_ptrs.len == 0) {
+ //if this area's object now has no areas, unmap it
+ if (obj->vm_area_node_ps.len == 0) {
- ret = _unlink_unmapped_obj(obj_node, state, vm_map);
- if (ret) return -1;
-
- //else set the new start and end addr for this object
- } else {
- ret = _update_obj_addr_range(temp_obj);
+ ret = _map_unlink_unmapped_obj(obj_node, state, map);
if (ret) return -1;
}
+ }
- } //end if
+ //remove this area from its last parent object if necessary
+ if (area->last_obj_node_p != NULL) {
+
+ obj_node = area->last_obj_node_p;
+ obj = MC_GET_NODE_OBJ(obj_node);
+
+ //remove this area from the last object
+ ret = _map_obj_rmv_last_area(obj, area_node);
+ if (ret == -1) return -1;
+ }
+
//unlink this node from the list of mapped vm areas
- ret = cm_list_unlink(&vm_map->vm_areas, state->next_area_index);
- if (ret) {
- ln_errno = LN_ERR_LIBCMORE;
+ ret_node = cm_lst_uln_n(&map->vm_areas, area_node);
+ if (ret_node != area_node) {
+ mc_errno = MC_ERR_CMORE;
return -1;
}
- //now safe to set node's values to be unmapped
- temp_area->mapped = false;
- node->next = NULL;
- node->prev = NULL;
+ //disconnect area node's attributes
+ area->mapped = false;
+ area->obj_node_p = NULL;
+ area->last_obj_node_p = NULL;
+ area_node->next = NULL;
+ area_node->prev = NULL;
- //add a pointer to this node to the list containing nodes to dealloc later
- temp_node = cm_list_append(&vm_map->vm_areas_unmapped, (cm_byte *) &node);
- if (temp_node == NULL) {
- ln_errno = LN_ERR_LIBCMORE;
+ //move this area node to the unmapped list
+ ret_node = cm_lst_apd(&map->vm_areas_unmapped, &area_node);
+ if (ret_node == NULL) {
+ mc_errno = MC_ERR_CMORE;
return -1;
}
@@ -351,11 +677,11 @@ static inline int _unlink_unmapped_area(cm_list_node * node,
}
-//check if the new vm_area is the same as the old vm_area
-static inline int _check_area_eql(const struct vm_entry * entry,
- const cm_list_node * area_node) {
+DBG_STATIC DBG_INLINE
+int _map_check_area_eql(const struct vm_entry * entry,
+ const cm_lst_node * area_node) {
- ln_vm_area * area = LN_GET_NODE_AREA(area_node);
+ mc_vm_area * area = MC_GET_NODE_AREA(area_node);
//check misc
if ((entry->vm_start != area->start_addr)
@@ -372,98 +698,52 @@ static inline int _check_area_eql(const struct vm_entry * entry,
}
-//remove old entries and get in sync again
-static inline int _resync_area(ln_vm_map * vm_map,
- _traverse_state * state, const struct vm_entry * entry) {
-
- int ret;
-
- ln_vm_area * iter_area;
- cm_list_node * iter_node;
-
- iter_node = state->next_area;
- iter_area = LN_GET_NODE_AREA(iter_node);
-
-
- //while there are vm areas left to discard
- while (entry->vm_end > iter_area->start_addr) {
-
- //make state point to the next node, and NULL if there is no next node
- if (state->next_area->next == NULL) {
- state->next_area = NULL;
- } else {
- state->next_area = state->next_area->next;
- }
-
- //correctly handle removing this area node
- ret = _unlink_unmapped_area(iter_node, state, vm_map);
- if (ret) return -1;
-
- //update iterator nodes
- if (state->next_area == NULL) break;
- iter_node = state->next_area;
- iter_area = LN_GET_NODE_AREA(iter_node);
-
- } //end while
-
- return 0;
-}
-
-
-#define _STATE_AREA_NODE_KEEP 0
-#define _STATE_AREA_NODE_ADVANCE 1
-#define _STATE_AREA_NODE_REASSIGN 2
-//move area state forward
-static void _state_inc_area(ln_vm_map * vm_map, _traverse_state * state,
- const cm_list_node * assign_node, const int inc_type) {
+DBG_STATIC
+void _map_state_inc_area(_traverse_state * state, const int inc_type,
+ const cm_lst_node * assign_node, mc_vm_map * map) {
switch (inc_type) {
case _STATE_AREA_NODE_KEEP:
+
break;
case _STATE_AREA_NODE_ADVANCE:
- //advance next area if we haven't reached end & dont circle back to start
- if (state->next_area != NULL
- && state->next_area_index < vm_map->vm_areas.len) {
-
- state->next_area = state->next_area->next;
+
+ //advance next area if we haven't reached the end
+ //& dont circle back to the start
+ if (state->next_area_node != NULL
+ && state->next_area_node->next != map->vm_areas.head) {
+ state->next_area_node = state->next_area_node->next;
} else {
- state->next_area = NULL;
- }
+ state->next_area_node = NULL;
+ }
break;
case _STATE_AREA_NODE_REASSIGN:
- state->next_area = (cm_list_node *) assign_node;
+
+ state->next_area_node = (cm_lst_node *) assign_node;
break;
} //end switch
- //always increment state index
- ++state->next_area_index;
-
return;
}
-//move obj state forward
-static void _state_inc_obj(ln_vm_map * vm_map, _traverse_state * state) {
+DBG_STATIC
+void _map_state_inc_obj(_traverse_state * state, mc_vm_map * map) {
//if there is no prev obj, initialise it
- if (state->prev_obj == NULL) {
-
- state->prev_obj = vm_map->vm_objs.head;
- state->prev_obj_index = 0;
+ if (state->prev_obj_node == NULL) {
+ state->prev_obj_node = map->vm_objs.head;
//if there is a prev obj
} else {
-
//only advance next object if we won't circle back to start
- if (state->prev_obj_index < vm_map->vm_objs.len) {;
-
- state->prev_obj = state->prev_obj->next;
- ++state->prev_obj_index;
+ if (state->prev_obj_node->next != map->vm_objs.head) {
+ state->prev_obj_node = state->prev_obj_node->next;
}
}
@@ -471,145 +751,232 @@ static void _state_inc_obj(ln_vm_map * vm_map, _traverse_state * state) {
}
-//add a new obj to the map
-static cm_list_node * _map_add_obj(ln_vm_map * vm_map, _traverse_state * state,
- const struct vm_entry * entry) {
+//return: 0: removed all areas up to entry->end_addr
+// 1: stopped iterating when matching area found
+DBG_STATIC DBG_INLINE
+int _map_resync_area(const struct vm_entry * entry,
+ _traverse_state * state, mc_vm_map * map) {
- int index;
+ int ret;
- ln_vm_obj vm_obj;
- cm_list_node * obj_node;
+ mc_vm_area * area;
+ cm_lst_node * area_node;
+
+
+ //setup iteration
+ area_node = state->next_area_node;
+ area = MC_GET_NODE_AREA(area_node);
+
+ //while there are vm areas left to discard
+ while (entry->vm_end > area->start_addr
+ && _map_check_area_eql(entry, area_node) == -1) {
+
+ //advance state
+ _map_state_inc_area(state, _STATE_AREA_NODE_ADVANCE, NULL, map);
+
+ //remove this area node
+ ret = _map_unlink_unmapped_area(area_node, state, map);
+ if (ret) return -1;
+
+ //update iterator nodes
+ if (state->next_area_node == NULL) break;
+ area_node = state->next_area_node;
+ area = MC_GET_NODE_AREA(area_node);
+
+ } //end while
+
+ if (entry->vm_end > area->start_addr) return 1;
+ return 0;
+}
+
+
+DBG_STATIC
+cm_lst_node * _map_add_obj(const struct vm_entry * entry,
+ _traverse_state * state, mc_vm_map * map) {
+
+ mc_vm_obj vm_obj;
+ cm_lst_node * obj_node;
- //create new object
- _new_vm_obj(&vm_obj, vm_map, entry->file_path);
- //figure out which insertion index to use
- index = (vm_map->vm_objs.len == 0) ? 0 : state->prev_obj_index + 1;
+ //create new object
+ _map_new_vm_obj(&vm_obj, map, entry->file_path);
- //insert obj into map at state's index
- obj_node = cm_list_insert(&vm_map->vm_objs,
- index, (cm_byte *) &vm_obj);
+ //insert obj into map
+ obj_node = cm_lst_ins_na(&map->vm_objs, state->prev_obj_node, &vm_obj);
if (obj_node == NULL) {
- ln_errno = LN_ERR_LIBCMORE;
+ mc_errno = MC_ERR_CMORE;
return NULL;
}
//advance state
- _state_inc_obj(vm_map, state);
+ _map_state_inc_obj(state, map);
return obj_node;
}
-//add a new area to the map
-static int _map_add_area(ln_vm_map * vm_map, _traverse_state * state,
- const struct vm_entry * entry, const int inc_type) {
+DBG_STATIC
+int _map_add_area(const struct vm_entry * entry,
+ _traverse_state * state, mc_vm_map * map) {
int ret;
- bool use_obj = false;
+ bool use_obj;
+ bool forward_obj = false;
+
+ mc_vm_area area;
+ cm_lst_node * area_node;
+
+ mc_vm_obj * obj;
+ cm_lst_node * obj_node;
- ln_vm_area vm_area;
- ln_vm_obj * vm_obj;
- cm_list_node * area_node;
- cm_list_node * obj_node;
+ //determine if this area belongs to a backing object
+ use_obj = (entry->file_path[0] == '\0') ? false : true;
- //if no obj for this area
- if (entry->file_path[0] != '\0') use_obj = true;
+ //if this area does not belong to a backing object, create a new area
if (!use_obj) {
/*
- * It should never be possible for prev_obj to point at/ahead of this vm_area
+ * It should never be possible for prev_obj
+ * to point at/ahead of this vm_area.
*/
- _new_vm_area(&vm_area, vm_map, NULL, state->prev_obj, entry);
+ _map_init_vm_area(&area, entry, NULL, state->prev_obj_node, map);
- //else there is an obj for this area
+
+ //else there is a backing object for this area
} else {
- ret = _find_obj_for_area(state, entry);
-
+ //determine which of the adjascent backing objects this area belongs to
+ ret = _map_find_obj_for_area(entry, state);
+
+ //dispatch case
switch (ret) {
- //area belongs to previous obj
+ //area belongs to the previous object
case _MAP_OBJ_PREV:
- _new_vm_area(&vm_area, vm_map, state->prev_obj, NULL, entry);
break;
- //area is part of a new object
+ //area is the start of a new object
case _MAP_OBJ_NEW:
- obj_node = _map_add_obj(vm_map, state, entry);
+ obj_node = _map_add_obj(entry, state, map);
if (obj_node == NULL) return -1;
- _new_vm_area(&vm_area, vm_map, state->prev_obj, NULL, entry);
+ forward_obj = true;
break;
- //area belongs to the next obj
+ //area belongs to the next object
case _MAP_OBJ_NEXT:
- _state_inc_obj(vm_map, state);
- _new_vm_area(&vm_area, vm_map, state->prev_obj, NULL, entry);
+ _map_state_inc_obj(state, map);
break;
} //end switch
- }
+
+ //initialise the area
+ _map_init_vm_area(&area, entry, state->prev_obj_node, NULL, map);
+
+ } //end if-else
+
//add area to the map list
- area_node = cm_list_insert(&vm_map->vm_areas,
- state->next_area_index, (cm_byte *) &vm_area);
+ if (state->next_area_node == NULL) {
+
+ //if map is empty, insert at start
+ if (map->vm_areas.head == NULL) {
+ area_node = cm_lst_ins(&map->vm_areas, 0, &area);
+ } else {
+ area_node = cm_lst_ins(&map->vm_areas, -1, &area);
+ }
+
+ } else {
+ area_node = cm_lst_ins_nb(&map->vm_areas,
+ state->next_area_node, &area);
+ }
if (area_node == NULL) {
- ln_errno = LN_ERR_LIBCMORE;
+ mc_errno = MC_ERR_CMORE;
return -1;
}
- //add area to the obj ptr list
+ //add area to the object pointer list
+ obj = MC_GET_NODE_OBJ(state->prev_obj_node);
if (use_obj) {
- vm_obj = LN_GET_NODE_OBJ(state->prev_obj);
- ret = _obj_add_area(vm_obj, area_node);
+ ret = _map_obj_add_area(obj, area_node);
+ if (ret == -1) return -1;
+
+ } else {
+ ret = _map_obj_add_last_area(obj, area_node);
+ if (ret == -1) return -1;
+ }
+
+ /*
+ * Only now that the new object has an area associated with it (and hence
+ * a valid address range) can areas be forwarded to it.
+ */
+ if (forward_obj) {
+ ret = _map_forward_unmapped_obj_last_vm_areas(state->prev_obj_node);
if (ret == -1) return -1;
}
- //increment area state
- _state_inc_area(vm_map, state, area_node, inc_type);
+ /*
+ * Increment area state if this new area is higher than the state's area.
+ * It should be impossible for areas to have address overlap due to
+ * _map_resync_area() eliminating old areas that overlap with new areas.
+ * Because of this, comparing just end addresses should suffice.
+ */
+ if ((state->next_area_node != NULL)
+ && (entry->vm_end
+ >= MC_GET_NODE_AREA(state->next_area_node)->end_addr)) {
+ _map_state_inc_area(state, _STATE_AREA_NODE_ADVANCE, NULL, map);
+ }
return 0;
}
-// --- CALLED BY MEMORY INTERFACES
+/*
+ * --- [INTERFACE] ---
+ */
-//send information about a vm area from an interface to a map
-int _map_send_entry(ln_vm_map * vm_map,
- _traverse_state * state, const struct vm_entry * entry) {
+int map_send_entry(const struct vm_entry * entry,
+ _traverse_state * state, mc_vm_map * map) {
int ret;
- //if at end of old map
- if (state->next_area == NULL) {
+ //if reached the end of the old map
+ if (state->next_area_node == NULL) {
- ret = _map_add_area(vm_map, state, entry, false);
+ ret = _map_add_area(entry, state, map);
if (ret == -1) return -1;
- //if not at end of old map
+ //if not reached the end end of the old map
} else {
//if entry doesn't match next area (a change in the map)
- if (_check_area_eql(entry, state->next_area)) {
+ if (_map_check_area_eql(entry, state->next_area_node) == -1) {
- ret = _resync_area(vm_map, state, entry);
- if (ret) return -1;
-
- ret = _map_add_area(vm_map, state, entry, false);
+ ret = _map_resync_area(entry, state, map);
if (ret == -1) return -1;
+ //replace area
+ if (ret == 0) {
+ ret = _map_add_area(entry, state, map);
+ if (ret == -1) return -1;
+ //area match found, advance state
+ } else {
+ _map_state_inc_area(state,
+ _STATE_AREA_NODE_ADVANCE, NULL, map);
+ }
+
// else entry matches next area
} else {
- _state_inc_area(vm_map, state, NULL, _STATE_AREA_NODE_ADVANCE);
+ _map_state_inc_area(state,
+ _STATE_AREA_NODE_ADVANCE, NULL, map);
//check if area belongs to the next obj
- if (_find_obj_for_area(state, entry) == _MAP_OBJ_NEXT) {
- _state_inc_obj(vm_map, state);
+ if (_map_find_obj_for_area(entry, state) == _MAP_OBJ_NEXT) {
+ _map_state_inc_obj(state, map);
}
} //end if match
@@ -620,146 +987,148 @@ int _map_send_entry(ln_vm_map * vm_map,
}
-//initialise traverse state for a map
-void _map_init_traverse_state(const ln_vm_map * vm_map, _traverse_state * state) {
+void map_init_traverse_state(_traverse_state * state, const mc_vm_map * map) {
- state->next_area_index = 0;
- state->prev_obj_index = 0;
-
- //set up next area node
- state->next_area = vm_map->vm_areas.head;
- state->prev_obj = vm_map->vm_objs.head;
+ state->next_area_node = map->vm_areas.len == 0 ? NULL : map->vm_areas.head;
+ state->prev_obj_node = map->vm_objs.len == 0 ? NULL : map->vm_objs.head;
return;
}
-// --- USER INTERFACE
+/*
+ * --- [EXTERNAL] ---
+ */
-//ln_vm_map constructor
-void ln_new_vm_map(ln_vm_map * vm_map) {
+void mc_new_vm_map(mc_vm_map * map) {
//pseudo object, will adopt leading parentless vm_areas
- ln_vm_obj zero_obj;
+ mc_vm_obj zero_obj;
+
+ //zero the map
+ memset(map, 0, sizeof(*map));
//initialise lists
- cm_new_list(&vm_map->vm_areas, sizeof(ln_vm_area));
- cm_new_list(&vm_map->vm_objs, sizeof(ln_vm_obj));
+ cm_new_lst(&map->vm_areas, sizeof(mc_vm_area));
+ cm_new_lst(&map->vm_objs, sizeof(mc_vm_obj));
- cm_new_list(&vm_map->vm_areas_unmapped, sizeof(cm_list_node *));
- cm_new_list(&vm_map->vm_objs_unmapped, sizeof(cm_list_node *));
+ cm_new_lst(&map->vm_areas_unmapped, sizeof(cm_lst_node *));
+ cm_new_lst(&map->vm_objs_unmapped, sizeof(cm_lst_node *));
//setup pseudo object at start of map
- _new_vm_obj(&zero_obj, vm_map, "0x0");
- _make_zero_obj(&zero_obj);
+ _map_new_vm_obj(&zero_obj, map, "0x0");
+ _map_make_zero_obj(&zero_obj);
- cm_list_append(&vm_map->vm_objs, (cm_byte *) &zero_obj);
+ cm_lst_apd(&map->vm_objs, &zero_obj);
- //reset id
- vm_map->next_id_area = vm_map->next_id_obj = 0;
+ //set next IDs to 0
+ map->next_id_area = map->next_id_obj = 0;
return;
}
-//ln_vm_map destructor
-int ln_del_vm_map(ln_vm_map * vm_map) {
+int mc_del_vm_map(mc_vm_map * map) {
- int ret;
+ int ret, len_obj;
- //unallocate all unmapped nodes
- ret = ln_map_clean_unmapped(vm_map);
- if (ret) return -1;
+ cm_lst_node * obj_node;
+ mc_vm_obj * obj;
- cm_list_node * iter_node;
- ln_vm_obj * iter_obj;
+ //unallocate all unmapped nodes
+ ret = mc_map_clean_unmapped(map);
+ if (ret) return -1;
- int len_obj = vm_map->vm_objs.len;
+ //setup iteration
+ len_obj = map->vm_objs.len;
+ obj_node = map->vm_objs.head;
- //manually free all unmapped obj nodes
+ //manually free all object lists
for (int i = 0; i < len_obj; ++i) {
- iter_node = cm_list_get_node(&vm_map->vm_objs_unmapped, i);
- if (iter_node == NULL) {
- ln_errno = LN_ERR_LIBCMORE;
- return -1;
- }
-
- iter_obj = LN_GET_NODE_OBJ(iter_node);
- if (iter_obj == NULL) {
- ln_errno = LN_ERR_UNEXPECTED_NULL;
- return -1;
- }
+ //fetch the object
+ obj = MC_GET_NODE_OBJ(obj_node);
- _del_vm_obj(iter_obj);
+ //destroy the object's list
+ _map_del_vm_obj(obj);
+ //cm_del_lst(&obj->vm_area_node_ps);
+ //cm_del_lst(&obj->last_vm_area_node_ps);
+
+ //advance iteration
+ obj_node = obj_node->next;
} //end for
-
- cm_del_list(&vm_map->vm_areas);
- cm_del_list(&vm_map->vm_objs);
+
+
+ //destroy all lists
+ cm_del_lst(&map->vm_areas);
+ cm_del_lst(&map->vm_objs);
+ cm_del_lst(&map->vm_areas_unmapped);
+ cm_del_lst(&map->vm_objs_unmapped);
return 0;
}
+
+int mc_map_clean_unmapped(mc_vm_map * map) {
-/*
- * The nodes of 'vm_map->_unmapped' lists hold pointers to other nodes.
- * This means to thoroughly
- */
-int ln_map_clean_unmapped(ln_vm_map * vm_map) {
+ int ret, len;
- int ret;
+ mc_vm_obj * obj;
+ cm_lst_node * node, * del_node;
- cm_list_node * iter_node;
- cm_list_node * del_node;
- int len_area = vm_map->vm_areas_unmapped.len;
- int len_obj = vm_map->vm_objs_unmapped.len;
+ //setup unmapped area iteration
+ len = map->vm_areas_unmapped.len;
+ node = map->vm_areas_unmapped.head;
//manually free all unmapped area nodes
- for (int i = 0; i < len_area; ++i) {
+ for (int i = 0; i < len; ++i) {
- iter_node = cm_list_get_node(&vm_map->vm_areas_unmapped, i);
- if (iter_node == NULL) {
- ln_errno = LN_ERR_LIBCMORE;
- return -1;
- }
+ //delete the unmapped area node
+ del_node = MC_GET_NODE_PTR(node);
+ cm_del_lst_node(del_node);
- del_node = *((cm_list_node **) iter_node->data);
- free(del_node->data);
- free(del_node);
+ //advance iteration
+ node = node->next;
} //end for
+
+
+ //setup unmapped object iteration
+ len = map->vm_objs_unmapped.len;
+ node = map->vm_objs_unmapped.head;
//manually free all unmapped obj nodes
- for (int i = 0; i < len_obj; ++i) {
+ for (int i = 0; i < len; ++i) {
- iter_node = cm_list_get_node(&vm_map->vm_objs_unmapped, i);
- if (iter_node == NULL) {
- ln_errno = LN_ERR_LIBCMORE;
- return -1;
- }
+ //delete the unmapped object and its node
+ del_node = MC_GET_NODE_PTR(node);
+ obj = MC_GET_NODE_OBJ(del_node);
- del_node = *((cm_list_node **) iter_node->data);
- free(del_node->data);
- free(del_node);
+ _map_del_vm_obj(obj);
+ cm_del_lst_node(del_node);
+
+ //advance iteration
+ node = node->next;
} //end for
+
//empty out both unmapped lists
- ret = cm_list_empty(&vm_map->vm_areas_unmapped);
+ ret = cm_lst_emp(&map->vm_areas_unmapped);
if (ret) {
- ln_errno = LN_ERR_LIBCMORE;
+ mc_errno = MC_ERR_CMORE;
return -1;
}
- ret = cm_list_empty(&vm_map->vm_objs_unmapped);
+ ret = cm_lst_emp(&map->vm_objs_unmapped);
if (ret) {
- ln_errno = LN_ERR_LIBCMORE;
+ mc_errno = MC_ERR_CMORE;
return -1;
}
diff --git a/src/lib/map.h b/src/lib/map.h
index 1f8881b..1ac4bb5 100644
--- a/src/lib/map.h
+++ b/src/lib/map.h
@@ -1,38 +1,97 @@
#ifndef MAP_H
#define MAP_H
-#include "liblain.h"
-#include "lainko.h"
+//local headers
+#include "memcry.h"
+#include "krncry.h"
+
+
+#define _MAP_OBJ_PREV 0
+#define _MAP_OBJ_NEW 1
+#define _MAP_OBJ_NEXT 2
+
+#define _STATE_AREA_NODE_KEEP 0
+#define _STATE_AREA_NODE_ADVANCE 1
+#define _STATE_AREA_NODE_REASSIGN 2
/*
- * initialise this struct manually on the first call to
+ * Initialise _traverse_state manually on the first call to
* send_map_node() for a map generated by a memory interface.
*
* send_map_node() will automatically update it for use by
* all subsequent calls.
*/
-typedef struct {
- cm_list_node * next_area; //ln_vm_area
- int next_area_index;
+typedef struct {
- cm_list_node * prev_obj; //ln_vm_obj
- int prev_obj_index;
+ cm_lst_node * next_area_node; //mc_vm_area
+ cm_lst_node * prev_obj_node; //mc_vm_obj
} _traverse_state;
+#ifdef DEBUG
//internal
-int _map_send_entry(ln_vm_map * vm_map,
- _traverse_state * state, const struct vm_entry * entry);
-void _map_init_traverse_state(const ln_vm_map * vm_map, _traverse_state * state);
+void _map_init_vm_area(mc_vm_area * area, const struct vm_entry * entry,
+ const cm_lst_node * obj_node,
+ const cm_lst_node * last_obj_node, mc_vm_map * map);
+void _map_new_vm_obj(mc_vm_obj * obj,
+ mc_vm_map * map, const char * pathname);
+void _map_del_vm_obj(mc_vm_obj * obj);
+void _map_make_zero_obj(mc_vm_obj * obj);
-//external
-void ln_new_vm_map(ln_vm_map * vm_map);
-int ln_del_vm_map(ln_vm_map * vm_map);
-int ln_map_clean_unmapped(ln_vm_map * vm_map);
+int _map_obj_add_area_insert(cm_lst * obj_area_lst,
+ const cm_lst_node * area_node);
+cm_lst_node * _map_obj_find_area_outer_node(cm_lst * obj_area_lst,
+ cm_lst_node * area_node);
+
+int _map_obj_add_area(mc_vm_obj * obj, const cm_lst_node * area_node);
+int _map_obj_add_last_area(mc_vm_obj * obj, const cm_lst_node * last_area_node);
+
+int _map_obj_rmv_area_fast(mc_vm_obj * obj, cm_lst_node * outer_area_node);
+int _map_obj_rmv_area(mc_vm_obj * obj, cm_lst_node * inner_area_node);
+int _map_obj_rmv_last_area_fast(mc_vm_obj * obj, cm_lst_node * outer_area_node);
+int _map_obj_rmv_last_area(mc_vm_obj * obj, cm_lst_node * inner_area_node);
+
+bool _map_is_pathname_in_obj(const char * pathname, const mc_vm_obj * obj);
+int _map_find_obj_for_area(const struct vm_entry * entry,
+ const _traverse_state * state);
+
+int _map_backtrack_unmapped_obj_last_vm_areas(cm_lst_node * obj_node);
+int _map_forward_unmapped_obj_last_vm_areas(cm_lst_node * obj_node);
+int _map_unlink_unmapped_obj(cm_lst_node * obj_node,
+ _traverse_state * state, mc_vm_map * map);
+int _map_unlink_unmapped_area(cm_lst_node * area_node,
+ _traverse_state * state, mc_vm_map * map);
+
+int _map_check_area_eql(const struct vm_entry * entry,
+ const cm_lst_node * area_node);
+
+void _map_state_inc_area(_traverse_state * state, const int inc_type,
+ const cm_lst_node * assign_node, mc_vm_map * map);
+void _map_state_inc_obj(_traverse_state * state, mc_vm_map * map);
+int _map_resync_area(const struct vm_entry * entry,
+ _traverse_state * state, mc_vm_map * map);
+
+cm_lst_node * _map_add_obj(const struct vm_entry * entry,
+ _traverse_state * state, mc_vm_map * map);
+int _map_add_area(const struct vm_entry * entry,
+ _traverse_state * state, mc_vm_map * map);
+#endif
+
+
+//interface
+int map_send_entry(const struct vm_entry * entry,
+ _traverse_state * state, mc_vm_map * map);
+void map_init_traverse_state(_traverse_state * state, const mc_vm_map * map);
+
+
+//external
+void mc_new_vm_map(mc_vm_map * map);
+int mc_del_vm_map(mc_vm_map * map);
+int mc_map_clean_unmapped(mc_vm_map * map);
#endif
diff --git a/src/lib/map_util.c b/src/lib/map_util.c
index 50ebf5b..0544cc7 100644
--- a/src/lib/map_util.c
+++ b/src/lib/map_util.c
@@ -1,22 +1,29 @@
+//standard library
#include
-#include
+//external libraries
+#include
-#include "liblain.h"
+//local headers
+#include "memcry.h"
#include "map_util.h"
+#include "debug.h"
-#define MAP_UTIL_GET_AREA 0
-#define MAP_UTIL_GET_OBJ 1
+#define _MAP_UTIL_GET_AREA 0
+#define _MAP_UTIL_GET_OBJ 1
-#define MAP_UTIL_USE_PATHNAME 0
-#define MAP_UTIL_USE_BASENAME 1
+#define _MAP_UTIL_USE_PATHNAME 0
+#define _MAP_UTIL_USE_BASENAME 1
-// --- INTERNAL
-//check map is initialised
-static inline bool _is_map_empty(const ln_vm_map * vm_map) {
+/*
+ * --- [INTERNAL] ---
+ */
+
+DBG_STATIC DBG_INLINE
+bool _is_map_empty(const mc_vm_map * vm_map) {
if (vm_map->vm_areas.len == 0) return true;
if (vm_map->vm_objs.len == 0) return true;
@@ -25,17 +32,22 @@ static inline bool _is_map_empty(const ln_vm_map * vm_map) {
}
-//skip pseudo object if it is empty
-static inline cm_list_node * _get_starting_obj(const ln_vm_map * vm_map) {
+/*
+ * Determines starting object for iteration.
+ * Will skip the pseudo-object if it is empty.
+ */
+
+DBG_STATIC DBG_INLINE
+cm_lst_node * _get_starting_obj(const mc_vm_map * vm_map) {
- cm_list_node * obj_node;
- ln_vm_obj * vm_obj;
+ cm_lst_node * obj_node;
+ mc_vm_obj * vm_obj;
obj_node = vm_map->vm_objs.head;
- vm_obj = LN_GET_NODE_OBJ(obj_node);
+ vm_obj = MC_GET_NODE_OBJ(obj_node);
//if pseudo object has no areas
- if (vm_obj->start_addr == -1 || vm_obj->start_addr == 0x0) {
+ if (vm_obj->start_addr == MC_UNDEF_ADDR || vm_obj->start_addr == 0x0) {
obj_node = obj_node->next;
}
@@ -43,35 +55,35 @@ static inline cm_list_node * _get_starting_obj(const ln_vm_map * vm_map) {
}
-//get object's last area
-static inline cm_list_node * _get_obj_last_area(const ln_vm_obj * vm_obj) {
+DBG_STATIC DBG_INLINE
+cm_lst_node * _get_obj_last_area(const mc_vm_obj * vm_obj) {
- cm_list_node * last_node;
+ cm_lst_node * last_node;
//if this object has multiple areas
- if (vm_obj->vm_area_node_ptrs.len > 1) {
- last_node = LN_GET_NODE_PTR(vm_obj->vm_area_node_ptrs.head->prev);
+ if (vm_obj->vm_area_node_ps.len > 1) {
+ last_node = MC_GET_NODE_PTR(vm_obj->vm_area_node_ps.head->prev);
//else this object only has one area
} else {
- last_node = LN_GET_NODE_PTR(vm_obj->vm_area_node_ptrs.head);
+ last_node = MC_GET_NODE_PTR(vm_obj->vm_area_node_ps.head);
}
return last_node;
}
-//iterate through objects, then areas for a fast search
-static cm_list_node * _fast_addr_find(const ln_vm_map * vm_map,
- const uintptr_t addr, const int mode) {
+DBG_STATIC
+cm_lst_node * _fast_addr_find(const mc_vm_map * vm_map,
+ const uintptr_t addr, const int mode) {
- cm_list_node * iter_obj_node;
- cm_list_node * iter_area_node;
+ cm_lst_node * iter_obj_node;
+ cm_lst_node * iter_area_node;
- ln_vm_obj * iter_vm_obj;
- ln_vm_area * iter_vm_area;
+ mc_vm_obj * iter_vm_obj;
+ mc_vm_area * iter_vm_area;
- ln_vm_area * prev_area;
+ mc_vm_area * prev_area;
//check map is not empty
@@ -79,10 +91,10 @@ static cm_list_node * _fast_addr_find(const ln_vm_map * vm_map,
//init object iteration
iter_obj_node = _get_starting_obj(vm_map);
- iter_vm_obj = LN_GET_NODE_OBJ(iter_obj_node);
+ iter_vm_obj = MC_GET_NODE_OBJ(iter_obj_node);
- iter_area_node = LN_GET_NODE_PTR(iter_vm_obj->vm_area_node_ptrs.head);
- iter_vm_area = LN_GET_NODE_AREA(iter_area_node);
+ iter_area_node = MC_GET_NODE_PTR(iter_vm_obj->vm_area_node_ps.head);
+ iter_vm_area = MC_GET_NODE_AREA(iter_area_node);
//if the address is in the very first object
@@ -95,17 +107,17 @@ static cm_list_node * _fast_addr_find(const ln_vm_map * vm_map,
if (iter_vm_obj->end_addr > addr) break;
iter_area_node = _get_obj_last_area(iter_vm_obj);
- iter_vm_area = LN_GET_NODE_AREA(iter_area_node);
+ iter_vm_area = MC_GET_NODE_AREA(iter_area_node);
iter_obj_node = iter_obj_node->next;
- iter_vm_obj = LN_GET_NODE_OBJ(iter_obj_node);
+ iter_vm_obj = MC_GET_NODE_OBJ(iter_obj_node);
} //end object search
}//end if
//if an obj was requested
- if (mode == MAP_UTIL_GET_OBJ) {
+ if (mode == _MAP_UTIL_GET_OBJ) {
//if the address is in range of the obj
if (iter_vm_obj->start_addr <= addr && iter_vm_obj->end_addr > addr) {
@@ -116,18 +128,19 @@ static cm_list_node * _fast_addr_find(const ln_vm_map * vm_map,
} //end if an obj was requested
- //switch to area search and continue the search from last object's final area
+ //switch to area search and continue the search from last object's end area
while (1) {
//if found a matching area
- if (iter_vm_area->start_addr <= addr && iter_vm_area->end_addr > addr) {
+ if (iter_vm_area->start_addr <= addr
+ && iter_vm_area->end_addr > addr) {
return iter_area_node;
}
//move to next object
prev_area = iter_vm_area;
iter_area_node = iter_area_node->next;
- iter_vm_area = LN_GET_NODE_AREA(iter_area_node);
+ iter_vm_area = MC_GET_NODE_AREA(iter_area_node);
//check if back to start of dl-list, and backtrack if true
if (prev_area->start_addr > iter_vm_area->end_addr) {
@@ -142,14 +155,14 @@ static cm_list_node * _fast_addr_find(const ln_vm_map * vm_map,
}
-//find object with a given basename/pathname
-static cm_list_node * _obj_name_find(const ln_vm_map * vm_map,
- const char * name, const int mode) {
+DBG_STATIC
+cm_lst_node * _obj_name_find(const mc_vm_map * vm_map,
+ const char * name, const int mode) {
int ret;
- cm_list_node * iter_obj_node;
- ln_vm_obj * iter_vm_obj;
+ cm_lst_node * iter_obj_node;
+ mc_vm_obj * iter_vm_obj;
//check map is not empty
if (_is_map_empty(vm_map)) return NULL;
@@ -157,13 +170,13 @@ static cm_list_node * _obj_name_find(const ln_vm_map * vm_map,
//init search
iter_obj_node = vm_map->vm_objs.head;
- iter_vm_obj = LN_GET_NODE_OBJ(iter_obj_node);
+ iter_vm_obj = MC_GET_NODE_OBJ(iter_obj_node);
//while can still iterate through objects
for (int i = 0; i < vm_map->vm_objs.len; ++i) {
//carry out comparison
- if (mode == MAP_UTIL_USE_PATHNAME) {
+ if (mode == _MAP_UTIL_USE_PATHNAME) {
ret = strncmp(name, iter_vm_obj->pathname, PATH_MAX);
} else {
ret = strncmp(name, iter_vm_obj->basename, NAME_MAX);
@@ -175,7 +188,7 @@ static cm_list_node * _obj_name_find(const ln_vm_map * vm_map,
}
iter_obj_node = iter_obj_node->next;
- iter_vm_obj = LN_GET_NODE_OBJ(iter_obj_node);
+ iter_vm_obj = MC_GET_NODE_OBJ(iter_obj_node);
} //end object search
@@ -184,98 +197,115 @@ static cm_list_node * _obj_name_find(const ln_vm_map * vm_map,
-// --- EXTERNAL
+/*
+ * --- [EXTERNAL] ---
+ */
-//get area offset
-off_t ln_get_area_offset(const cm_list_node * area_node, const uintptr_t addr) {
+off_t mc_get_area_off(const cm_lst_node * area_node,
+ const uintptr_t addr) {
- ln_vm_area * vm_area = LN_GET_NODE_AREA(area_node);
+ mc_vm_area * vm_area = MC_GET_NODE_AREA(area_node);
return (addr - vm_area->start_addr);
}
-//get obj offset
-off_t ln_get_obj_offset(const cm_list_node * obj_node, const uintptr_t addr) {
+off_t mc_get_obj_off(const cm_lst_node * obj_node,
+ const uintptr_t addr) {
- ln_vm_obj * vm_obj = LN_GET_NODE_OBJ(obj_node);
+ mc_vm_obj * vm_obj = MC_GET_NODE_OBJ(obj_node);
return (addr - vm_obj->start_addr);
}
-//get area offset
-off_t ln_get_area_offset_bnd(const cm_list_node * area_node, const uintptr_t addr) {
+off_t mc_get_area_off_bnd(const cm_lst_node * area_node,
+ const uintptr_t addr) {
- ln_vm_area * vm_area = LN_GET_NODE_AREA(area_node);
+ mc_vm_area * vm_area = MC_GET_NODE_AREA(area_node);
if ((addr >= vm_area->end_addr) || (addr < vm_area->start_addr)) return -1;
return (addr - vm_area->start_addr);
}
-//get obj offset
-off_t ln_get_obj_offset_bnd(const cm_list_node * obj_node, const uintptr_t addr) {
+off_t mc_get_obj_off_bnd(const cm_lst_node * obj_node,
+ const uintptr_t addr) {
- ln_vm_obj * vm_obj = LN_GET_NODE_OBJ(obj_node);
+ mc_vm_obj * vm_obj = MC_GET_NODE_OBJ(obj_node);
if ((addr >= vm_obj->end_addr) || (addr < vm_obj->start_addr)) return -1;
return (addr - vm_obj->start_addr);
}
+cm_lst_node * mc_get_area_by_addr(const mc_vm_map * vm_map,
+ const uintptr_t addr, off_t * offset) {
-//return the area node at a given address, optionally include offset into the area
-cm_list_node * ln_get_vm_area_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, off_t * offset) {
-
- cm_list_node * area_node;
+ cm_lst_node * area_node;
- area_node = _fast_addr_find(vm_map, addr, MAP_UTIL_GET_AREA);
+ area_node = _fast_addr_find(vm_map, addr, _MAP_UTIL_GET_AREA);
if (!area_node) return NULL;
- if (offset != NULL) *offset = ln_get_area_offset(area_node, addr);
+ if (offset != NULL) *offset = mc_get_area_off(area_node, addr);
return area_node;
}
-//return the obj node at a given address, optionally include offset into the obj
-cm_list_node * ln_get_vm_obj_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, off_t * offset) {
+cm_lst_node * mc_get_obj_by_addr(const mc_vm_map * vm_map,
+ const uintptr_t addr, off_t * offset) {
- cm_list_node * obj_node;
+ cm_lst_node * obj_node;
- obj_node = _fast_addr_find(vm_map, addr, MAP_UTIL_GET_OBJ);
+ obj_node = _fast_addr_find(vm_map, addr, _MAP_UTIL_GET_OBJ);
if (!obj_node) return NULL;
- if (offset != NULL) *offset = ln_get_obj_offset(obj_node, addr);
+ if (offset != NULL) *offset = mc_get_obj_off(obj_node, addr);
return obj_node;
}
-//return object matching pathname
-cm_list_node * ln_get_vm_obj_by_pathname(const ln_vm_map * vm_map,
- const char * pathname) {
+cm_lst_node * mc_get_obj_by_pathname(const mc_vm_map * vm_map,
+ const char * pathname) {
- cm_list_node * obj_node;
+ cm_lst_node * obj_node;
- obj_node = _obj_name_find(vm_map, pathname, MAP_UTIL_USE_PATHNAME);
+ obj_node = _obj_name_find(vm_map, pathname, _MAP_UTIL_USE_PATHNAME);
if (!obj_node) return NULL;
return obj_node;
}
-//return object matching basename
-cm_list_node * ln_get_vm_obj_by_basename(const ln_vm_map * vm_map,
- const char * basename) {
+cm_lst_node * mc_get_obj_by_basename(const mc_vm_map * vm_map,
+ const char * basename) {
- cm_list_node * obj_node;
+ cm_lst_node * obj_node;
- obj_node = _obj_name_find(vm_map, basename, MAP_UTIL_USE_BASENAME);
+ obj_node = _obj_name_find(vm_map, basename, _MAP_UTIL_USE_BASENAME);
if (!obj_node) return NULL;
return obj_node;
}
+
+
+void mc_access_to_str(const cm_byte access, char * str_buf) {
+
+ cm_byte bit_mask = 1;
+ const char * on = "rwxs";
+ const char * off = "---p";
+
+ //for every bit in the access mask
+ for (int i = 0; i < 4; ++i) {
+
+ //convert the permission bit to its character representation
+ str_buf[i] = (bit_mask << i) & access ? on[i] : off[i];
+ }
+
+ //add a NULL terminator
+ str_buf[4] = '\0';
+
+ return;
+}
diff --git a/src/lib/map_util.h b/src/lib/map_util.h
index 35f84e3..51cb638 100644
--- a/src/lib/map_util.h
+++ b/src/lib/map_util.h
@@ -1,24 +1,45 @@
#ifndef MAP_UTIL_H
#define MAP_UTIL_H
-#include
+//external libraries
+#include
-#include "liblain.h"
+//local headers
+#include "memcry.h"
+
+
+#ifdef DEBUG
+//internal
+bool _is_map_empty(const mc_vm_map * vm_map);
+cm_lst_node * _get_starting_obj(const mc_vm_map * vm_map);
+cm_lst_node * _get_obj_last_area(const mc_vm_obj * vm_obj);
+cm_lst_node * _fast_addr_find(const mc_vm_map * vm_map,
+ const uintptr_t addr, const int mode);
+cm_lst_node * _obj_name_find(const mc_vm_map * vm_map,
+ const char * name, const int mode);
+#endif
//external
-off_t ln_get_area_offset(const cm_list_node * area_node, const uintptr_t addr);
-off_t ln_get_obj_offset(const cm_list_node * obj_node, const uintptr_t addr);
-off_t ln_get_area_offset_bnd(const cm_list_node * area_node, const uintptr_t addr);
-off_t ln_get_obj_offset_bnd(const cm_list_node * obj_node, const uintptr_t addr);
-cm_list_node * ln_get_vm_area_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, off_t * offset);
-cm_list_node * ln_get_vm_obj_by_addr(const ln_vm_map * vm_map,
- const uintptr_t addr, off_t * offset);
-cm_list_node * ln_get_vm_obj_by_pathname(const ln_vm_map * vm_map,
- const char * pathname);
-cm_list_node * ln_get_vm_obj_by_basename(const ln_vm_map * vm_map,
- const char * basename);
+off_t mc_get_area_off(const cm_lst_node * area_node,
+ const uintptr_t addr);
+off_t mc_get_obj_off(const cm_lst_node * obj_node,
+ const uintptr_t addr);
+off_t mc_get_area_off_bnd(const cm_lst_node * area_node,
+ const uintptr_t addr);
+off_t mc_get_obj_off_bnd(const cm_lst_node * obj_node,
+ const uintptr_t addr);
+
+cm_lst_node * mc_get_area_by_addr(const mc_vm_map * vm_map,
+ const uintptr_t addr, off_t * offset);
+cm_lst_node * mc_get_obj_by_addr(const mc_vm_map * vm_map,
+ const uintptr_t addr, off_t * offset);
+
+cm_lst_node * mc_get_obj_by_pathname(const mc_vm_map * vm_map,
+ const char * pathname);
+cm_lst_node * mc_get_obj_by_basename(const mc_vm_map * vm_map,
+ const char * basename);
+void mc_access_to_str(const cm_byte access, char * str_buf);
#endif
diff --git a/src/lib/memcry.h b/src/lib/memcry.h
new file mode 100644
index 0000000..6755c89
--- /dev/null
+++ b/src/lib/memcry.h
@@ -0,0 +1,293 @@
+#ifndef MEMCRY_H
+#define MEMCRY_H
+
+#ifdef __cplusplus
+extern "C"{
+#endif
+
+//standard library
+#include
+#include
+#include
+
+//system headers
+#include
+#include
+
+//external libraries
+#include
+
+
+//easy extract from a cm_list_node pointer
+#define MC_GET_NODE_AREA(node) ((mc_vm_area *) (node->data))
+#define MC_GET_NODE_OBJ(node) ((mc_vm_obj *) (node->data))
+#define MC_GET_NODE_PTR(node) *((cm_lst_node **) (node->data))
+
+//mc_vm_area.access bitmasks
+#define MC_ACCESS_READ 0x01
+#define MC_ACCESS_WRITE 0x02
+#define MC_ACCESS_EXEC 0x04
+#define MC_ACCESS_SHARED 0x08
+
+//pseudo object id
+#define MC_ZERO_OBJ_ID -1
+
+//-Wsign-compare '-1'
+#define MC_UNDEF_ADDR UINTPTR_MAX
+
+
+//interface types
+enum mc_iface_type {
+ PROCFS = 0,
+ KRNCRY = 1
+};
+
+
+/*
+ * --- [DATA TYPES] ---
+ */
+
+// [memory area]
+typedef struct {
+
+ char * pathname;
+ char * basename;
+
+ uintptr_t start_addr;
+ uintptr_t end_addr;
+
+ cm_byte access; //logically AND with MC_ACCESS macros
+
+ //mutually exclusive
+ cm_lst_node * obj_node_p; //STORES: parent mc_vm_obj *
+ cm_lst_node * last_obj_node_p; //STORES: last encountered mc_vm_obj *
+
+ int id;
+ bool mapped; //set to false when a map update discovers area to be unmapped
+
+} mc_vm_area;
+
+
+// [backing object]
+typedef struct {
+
+ uintptr_t start_addr;
+ uintptr_t end_addr;
+
+ cm_lst vm_area_node_ps; //STORES: cm_lst_node * of mc_vm_area
+ cm_lst last_vm_area_node_ps; //STORES: cm_lst_node * of mc_vm_area
+
+ int id;
+ bool mapped; //set to false when a map update discovers obj. to be unmapped
+
+ char * basename; //[restrict .NAME_MAX]
+ char * pathname; //[restrict .PATH_MAX]
+
+} mc_vm_obj;
+
+
+// [memory map]
+typedef struct {
+
+ //up to date entries
+ cm_lst vm_areas; //STORES: mc_vm_area
+ cm_lst vm_objs; //STORES: mc_vm_obj
+
+ //unmapped entries (storage for future deallocation)
+ cm_lst vm_areas_unmapped; //STORES: cm_list_node * of mc_vm_area
+ cm_lst vm_objs_unmapped; //STORES: cm_list_node * of mc_vm_obj
+
+ // [internal]
+ int next_id_area;
+ int next_id_obj;
+
+} mc_vm_map;
+
+
+
+// [session]
+struct _mc_session;
+
+typedef struct {
+
+ int (*open)(struct _mc_session *, const int);
+ int (*close)(struct _mc_session *);
+ int (*update_map)(const struct _mc_session *, mc_vm_map *);
+ int (*read)(const struct _mc_session *,
+ const uintptr_t, cm_byte *, const size_t);
+ int (*write)(const struct _mc_session *,
+ const uintptr_t, const cm_byte *, const size_t);
+
+} mc_iface;
+
+
+struct _mc_session {
+
+ union {
+ struct {
+ int fd_mem;
+ pid_t pid;
+ }; //procfs_data
+ struct {
+ char major;
+ int fd_dev_krncry;
+ }; //krncry_data
+ };
+
+ long page_size;
+ mc_iface iface;
+
+};
+typedef struct _mc_session mc_session;
+
+
+
+/*
+ * --- [FUNCTIONS] ---
+ */
+
+// [util]
+//return: basename = success, NULL = fail/error
+extern char * mc_pathname_to_basename(const char * pathname);
+//must destroy 'pid_vector' manually on success | pid = success, -1 = fail/error
+extern pid_t mc_pid_by_name(const char * comm, cm_vct * pid_vector);
+//`name_buf` must be at least NAME_MAX in size.
+//return: 0 = success, -1 = fail/error
+extern int mc_name_by_pid(const pid_t pid, char * name_buf);
+//'out' must have space for double the length of 'inp' + 1
+extern void mc_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out);
+
+
+// [virtual interface]
+//all return 0 = success, -1 = fail/error
+extern int mc_open(mc_session * session,
+ const enum mc_iface_type iface, const pid_t pid);
+extern int mc_close(mc_session * session);
+extern int mc_update_map(const mc_session * session, mc_vm_map * vm_map);
+extern int mc_read(const mc_session * session, const uintptr_t addr,
+ cm_byte * buf, const size_t buf_sz);
+extern int mc_write(const mc_session * session, const uintptr_t addr,
+ const cm_byte * buf, const size_t buf_sz);
+
+// --- [map]
+//all return 0 = success, -1 = fail/error
+extern void mc_new_vm_map(mc_vm_map * map);
+extern int mc_del_vm_map(mc_vm_map * map);
+extern int mc_map_clean_unmapped(mc_vm_map * map);
+
+
+// --- [map util]
+//return: offset from start of area/object
+extern off_t mc_get_area_off(const cm_lst_node * area_node,
+ const uintptr_t addr);
+extern off_t mc_get_obj_off(const cm_lst_node * obj_node,
+ const uintptr_t addr);
+//return: offset from start of area/object = success, -1 = not in area/object
+extern off_t mc_get_area_off_bnd(const cm_lst_node * area_node,
+ const uintptr_t addr);
+extern off_t mc_get_obj_off_bnd(const cm_lst_node * obj_node,
+ const uintptr_t addr);
+
+//return: area node * = success, NULL = fail/error
+extern cm_lst_node * mc_get_area_by_addr(const mc_vm_map * vm_map,
+ const uintptr_t addr, off_t * offset);
+//return: obj node * = success, NULL = fail/error
+extern cm_lst_node * mc_get_obj_by_addr(const mc_vm_map * vm_map,
+ const uintptr_t addr, off_t * offset);
+extern cm_lst_node * mc_get_obj_by_pathname(const mc_vm_map * vm_map,
+ const char * pathname);
+extern cm_lst_node * mc_get_obj_by_basename(const mc_vm_map * vm_map,
+ const char * basename);
+//`str_buf` must be at least 5 bytes in size.
+//return: 0 = success, -1 = fail/error
+extern void mc_access_to_str(const cm_byte access, char * str_buf);
+
+
+// --- [error handling]
+//void return
+extern void mc_perror(const char * prefix);
+extern const char * mc_strerror(const int mc_errnum);
+
+
+
+/*
+ * --- [ERROR HANDLING] ---
+ */
+
+extern __thread int mc_errno;
+
+
+// [error codes]
+
+// 1XX - user errors
+#define MC_ERR_PROC_MEM 2100
+#define MC_ERR_PROC_MAP 2101
+#define MC_ERR_SEEK_ADDR 2102
+
+// 2XX - internal errors
+#define MC_ERR_INTERNAL_INDEX 2200
+#define MC_ERR_AREA_IN_OBJ 2201
+#define MC_ERR_UNEXPECTED_NULL 2202
+#define MC_ERR_CMORE 2203
+#define MC_ERR_READ_WRITE 2204
+#define MC_ERR_MEMU_TARGET 2205
+#define MC_ERR_MEMU_MAP_SZ 2206
+#define MC_ERR_MEMU_MAP_GET 2207
+#define MC_ERR_PROC_STATUS 2208
+#define MC_ERR_PROC_NAV 2209
+
+// 3XX - environmental errors
+#define MC_ERR_MEM 2300
+#define MC_ERR_PAGESIZE 2301
+#define MC_ERR_KRNCRY_MAJOR 2302
+#define MC_ERR_MEMU_OPEN 2303
+
+
+// [error code messages]
+
+// 1XX - user errors
+#define MC_ERR_PROC_MEM_MSG \
+ "Could not open /proc//mem for specified pid.\n"
+#define MC_ERR_PROC_MAP_MSG \
+ "Could not open /proc//maps for specified pid.\n"
+#define MC_ERR_SEEK_ADDR_MSG \
+ "Could not seek to specified address.\n"
+
+// 2XX - internal errors
+#define MC_ERR_INTERNAL_INDEX_MSG \
+ "Internal: Indexing error.\n"
+#define MC_ERR_AREA_IN_OBJ_MSG \
+ "Internal: Area is not in object when it should be.\n"
+#define MC_ERR_UNEXPECTED_NULL_MSG \
+ "Internal: Unexpected NULL pointer.\n"
+#define MC_ERR_CMORE_MSG \
+ "Internal: CMore error. See cm_perror().\n"
+#define MC_ERR_READ_WRITE_MSG \
+ "Internal: Read/write failed.\n"
+#define MC_ERR_MEMU_TARGET_MSG \
+ "Internal: Krncry target open failed.\n"
+#define MC_ERR_MEMU_MAP_SZ_MSG \
+ "Internal: Krncry map size fetch failed..\n"
+#define MC_ERR_MEMU_MAP_GET_MSG \
+ "Internal: Krncry map transfer failed.\n"
+#define MC_ERR_PROC_STATUS_MSG \
+ "Internal: Failed to use /proc//status.\n"
+#define MC_ERR_PROC_NAV_MSG \
+ "Internal: Failed to navigate /proc directories.\n"
+
+// 3XX - environmental errors
+#define MC_ERR_MEM_MSG \
+ "Failed to acquire the necessary memory.\n"
+#define MC_ERR_PAGESIZE_MSG \
+ "Unable to fetch pagesize through sysconf().\n"
+#define MC_ERR_KRNCRY_MAJOR_MSG \
+ "Could not fetch krncry's major number.\n"
+#define MC_ERR_MEMU_OPEN_MSG \
+ "Could not open krncry device.\n"
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/lib/procfs_iface.c b/src/lib/procfs_iface.c
index c441559..3baaa5a 100644
--- a/src/lib/procfs_iface.c
+++ b/src/lib/procfs_iface.c
@@ -1,29 +1,33 @@
+//standard library
#include
#include
#include
#include
#include
+//system headers
#include
#include
-#include
+//external libraries
+#include
+//local headers
#include "procfs_iface.h"
-#include "liblain.h"
+#include "memcry.h"
#include "map.h"
-#include "lainko.h"
+#include "krncry.h"
-#define LINE_LEN PATH_MAX + 128
-
-
-// --- PROCFS INTERFACE INTERNALS
-
+/*
+ * --- [INTERNAL] ---
+ */
+
//build vm_entry from a line in procfs maps
-static inline void _build_entry(struct vm_entry * entry, const char * line_buf) {
+DBG_STATIC DBG_INLINE
+void _build_entry(struct vm_entry * entry, const char * line_buf) {
int mode = 0;
int done = 0;
@@ -37,6 +41,7 @@ static inline void _build_entry(struct vm_entry * entry, const char * line_buf)
char perm_chars[] = {'r', 'w', 'x', 's'};
cm_byte perm_vals[] = {VM_READ, VM_WRITE, VM_EXEC, VM_SHARED};
+
//save str for start addr
start_str = (char *) line_buf;
@@ -66,7 +71,7 @@ static inline void _build_entry(struct vm_entry * entry, const char * line_buf)
//for every char in perms field
for (int j = 0; j < 4; ++j) {
if (*(line_buf + i + j) == perm_chars[j]) {
- entry->prot += (lainko_pgprot_t) perm_vals[j];
+ entry->prot += (krncry_pgprot_t) perm_vals[j];
}
} //end for
@@ -93,7 +98,7 @@ static inline void _build_entry(struct vm_entry * entry, const char * line_buf)
} //end for every char
- //fill entry (prot already done in switch statement)
+ //fill entry (`prot` already filled inside switch statement)
entry->vm_start = (unsigned long) strtol(start_str, NULL, 16);
entry->vm_end = (unsigned long) strtol(end_str, NULL, 16);
entry->file_off = (unsigned long) strtol(offset_str, NULL, 16);
@@ -104,28 +109,31 @@ static inline void _build_entry(struct vm_entry * entry, const char * line_buf)
}
-// --- CALLED BY VIRTUAL INTERFACE
-//open handles on /proc/pid/mem and /proc/pid/maps
-int _procfs_open(ln_session * session, const int pid) {
+/*
+ * --- [INTERFACE] ---
+ */
+
+int procfs_open(mc_session * session, const int pid) {
int fd;
char mem_buf[PATH_MAX] = {0};
+
session->pid = pid;
- //get page size
+ //get page size to determine maximum read/write size
session->page_size = sysconf(_SC_PAGESIZE);
if (session->page_size < 0) {
- ln_errno = LN_ERR_PAGESIZE;
+ mc_errno = MC_ERR_PAGESIZE;
return -1;
}
- //open memory
+ //open procfs mem file
snprintf(mem_buf, PATH_MAX, "/proc/%d/mem", pid);
fd = open(mem_buf, O_RDWR);
if (fd == -1) {
- ln_errno = LN_ERR_PROC_MEM;
+ mc_errno = MC_ERR_PROC_MEM;
return -1;
}
session->fd_mem = fd;
@@ -134,17 +142,16 @@ int _procfs_open(ln_session * session, const int pid) {
}
-//close handles on /proc/pid/mem and /proc/pid/maps
-int _procfs_close(ln_session * session) {
+int procfs_close(mc_session * session) {
+ //close procfs mem file
close(session->fd_mem);
return 0;
}
-//update or build a map
-int _procfs_update_map(const ln_session * session, ln_vm_map * vm_map) {
+int procfs_update_map(const mc_session * session, mc_vm_map * vm_map) {
int ret;
FILE * fs;
@@ -155,24 +162,25 @@ int _procfs_update_map(const ln_session * session, ln_vm_map * vm_map) {
struct vm_entry new_entry;
_traverse_state state;
+
//open memory map
snprintf(map_buf, PATH_MAX, "/proc/%d/maps", session->pid);
fs = fopen(map_buf, "r");
if (fs == NULL) {
- ln_errno = LN_ERR_PROC_MAP;
+ mc_errno = MC_ERR_PROC_MAP;
return -1;
}
//init traverse state for this map
- _map_init_traverse_state(vm_map, &state);
+ map_init_traverse_state(&state, vm_map);
//while there are entries left
while (fgets(line_buf, LINE_LEN, fs) != NULL) {
memset(&new_entry, 0, sizeof(new_entry));
- _build_entry(&new_entry, line_buf);
-
- ret = _map_send_entry(vm_map, &state, &new_entry);
+ _build_entry(&new_entry, line_buf);
+
+ ret = map_send_entry(&new_entry, &state, vm_map);
if (ret != 0) return -1;
} //end while
@@ -185,15 +193,8 @@ int _procfs_update_map(const ln_session * session, ln_vm_map * vm_map) {
}
-/*
- * Just calling read/write of buf_sz returns success, but the actual read/write
- * fails on sizes above PAGESIZE. So all of the below is necessary.
- *
- */
-
-//read memory
-int _procfs_read(const ln_session * session, const uintptr_t addr,
- cm_byte * buf, const size_t buf_sz) {
+int procfs_read(const mc_session * session, const uintptr_t addr,
+ cm_byte * buf, const size_t buf_sz) {
off_t off_ret;
ssize_t read_bytes, read_done, read_left;
@@ -203,7 +204,7 @@ int _procfs_read(const ln_session * session, const uintptr_t addr,
//seek to address
off_ret = lseek(session->fd_mem, (off_t) addr, SEEK_SET);
if (off_ret == -1) {
- ln_errno = LN_ERR_SEEK_ADDR;
+ mc_errno = MC_ERR_SEEK_ADDR;
return -1;
}
@@ -218,21 +219,22 @@ int _procfs_read(const ln_session * session, const uintptr_t addr,
read_left > session->page_size
? session->page_size : read_left);
//if error or EOF before reading len bytes
- if (read_bytes == -1 || (read_bytes == 0 && read_done < buf_sz)) {
- ln_errno = LN_ERR_READ_WRITE;
+ if (read_bytes == -1 || (read_bytes == 0
+ && read_done < (ssize_t) buf_sz)) {
+
+ mc_errno = MC_ERR_READ_WRITE;
return -1;
}
read_done += read_bytes;
- } while (read_done < buf_sz);
+ } while (read_done < (ssize_t) buf_sz);
return 0;
}
-//write memory
-int _procfs_write(const ln_session * session, const uintptr_t addr,
- const cm_byte * buf, const size_t buf_sz) {
+int procfs_write(const mc_session * session, const uintptr_t addr,
+ const cm_byte * buf, const size_t buf_sz) {
off_t off_ret;
ssize_t write_bytes, write_done, write_left;
@@ -242,7 +244,7 @@ int _procfs_write(const ln_session * session, const uintptr_t addr,
//seek to address
off_ret = lseek(session->fd_mem, (off_t) addr, SEEK_SET);
if (off_ret == -1) {
- ln_errno = LN_ERR_SEEK_ADDR;
+ mc_errno = MC_ERR_SEEK_ADDR;
return -1;
}
@@ -257,13 +259,15 @@ int _procfs_write(const ln_session * session, const uintptr_t addr,
write_left > session->page_size
? session->page_size : write_left);
//if error or EOF before writing len bytes
- if (write_bytes == -1 || (write_bytes == 0 && write_done < buf_sz)) {
- ln_errno = LN_ERR_READ_WRITE;
+ if (write_bytes == -1 || (write_bytes == 0
+ && write_done < (ssize_t) buf_sz)) {
+
+ mc_errno = MC_ERR_READ_WRITE;
return -1;
}
write_done += write_bytes;
- } while (write_done < buf_sz);
+ } while (write_done < (ssize_t) buf_sz);
return 0;
}
diff --git a/src/lib/procfs_iface.h b/src/lib/procfs_iface.h
index 4ecb5ec..364b701 100644
--- a/src/lib/procfs_iface.h
+++ b/src/lib/procfs_iface.h
@@ -1,19 +1,31 @@
#ifndef PROC_IFACE_H
#define PROC_IFACE_H
-#include
+//external libraries
+#include
-#include "liblain.h"
+//local headers
+#include "memcry.h"
+#include "krncry.h"
+#include "debug.h"
+#define LINE_LEN PATH_MAX + 128
+
+
+#ifdef DEBUG
//internal
-int _procfs_open(ln_session * session, const int pid);
-int _procfs_close(ln_session * session);
-int _procfs_update_map(const ln_session * session, ln_vm_map * vm_map);
-int _procfs_read(const ln_session * session, const uintptr_t addr,
- cm_byte * buf, const size_t buf_sz);
-int _procfs_write(const ln_session * session, const uintptr_t addr,
- const cm_byte * buf, const size_t buf_sz);
+void _build_entry(struct vm_entry * entry, const char * line_buf);
+#endif
+
+//interface
+int procfs_open(mc_session * session, const int pid);
+int procfs_close(mc_session * session);
+int procfs_update_map(const mc_session * session, mc_vm_map * vm_map);
+int procfs_read(const mc_session * session, const uintptr_t addr,
+ cm_byte * buf, const size_t buf_sz);
+int procfs_write(const mc_session * session, const uintptr_t addr,
+ const cm_byte * buf, const size_t buf_sz);
#endif
diff --git a/src/lib/util.c b/src/lib/util.c
index 6eb4232..7839d85 100644
--- a/src/lib/util.c
+++ b/src/lib/util.c
@@ -1,8 +1,10 @@
+//standard libraries
#include
#include
#include
#include
+//system headers
#include
#include
@@ -10,24 +12,29 @@
#include
-
-#include "liblain.h"
+//local headers
+#include "memcry.h"
#include "util.h"
+#include "debug.h"
+
+#define _LINE_LEN PATH_MAX + 128
-#define LINE_LEN PATH_MAX + 128
-// --- INTERNAL
+/*
+ * --- [INTERNAL] ---
+ */
//convert the first line of /proc/pid/status to a name (comm)
-static inline void _line_to_name(const char * line_buf, char * name_buf) {
+DBG_STATIC DBG_INLINE
+void _line_to_name(const char * line_buf, char * name_buf) {
line_buf += 5;
char * name_str;
//for every character in the line
- for (int i = 0; i < LINE_LEN; ++i) {
+ for (int i = 0; i < _LINE_LEN; ++i) {
if (line_buf[i] == ' ' || line_buf[i] == '\t') continue;
name_str = (char *) line_buf + 1;
@@ -41,8 +48,13 @@ static inline void _line_to_name(const char * line_buf, char * name_buf) {
}
-//use /proc/pid/status to get the name of a process (mimic utils like 'ps')
-static int _get_status_name(char * name_buf, const pid_t pid) {
+/*
+ * Use `/proc/pid/status` to get the name of a process. This is
+ * how utilities like `ps` and `top` fetch process names.
+ */
+
+DBG_STATIC
+int _get_status_name(char * name_buf, const pid_t pid) {
int ret;
char * fret;
@@ -50,7 +62,7 @@ static int _get_status_name(char * name_buf, const pid_t pid) {
FILE * fs;
char path_buf[PATH_MAX];
- char line_buf[LINE_LEN];
+ char line_buf[_LINE_LEN];
//build path
@@ -59,22 +71,22 @@ static int _get_status_name(char * name_buf, const pid_t pid) {
//get name
fs = fopen(path_buf, "r");
if (fs == NULL) {
- ln_errno = LN_ERR_PROC_STATUS;
+ mc_errno = MC_ERR_PROC_STATUS;
return -1;
}
//read top line containing name (comm) of process
- fret = fgets(line_buf, LINE_LEN, fs);
+ fret = fgets(line_buf, _LINE_LEN, fs);
if (fret == NULL) {
fclose(fs);
- ln_errno = LN_ERR_PROC_STATUS;
+ mc_errno = MC_ERR_PROC_STATUS;
return -1;
}
//close file stream
ret = fclose(fs);
if (ret == -1) {
- ln_errno = LN_ERR_PROC_STATUS;
+ mc_errno = MC_ERR_PROC_STATUS;
return -1;
}
@@ -89,20 +101,20 @@ static int _get_status_name(char * name_buf, const pid_t pid) {
-// --- EXTERNAL
+/*
+ * --- EXTERNAL
+ */
-//returns basename
-const char * ln_pathname_to_basename(const char * pathname) {
+char * mc_pathname_to_basename(const char * pathname) {
char * basename = strrchr(pathname, (int) '/');
- if (basename == NULL) return pathname;
+ if (basename == NULL) return (char *) pathname;
return basename + 1;
}
-//get vector of pids matching name, return first match
-pid_t ln_pid_by_name(const char * comm, cm_vector * pid_vector) {
+pid_t mc_pid_by_name(const char * comm, cm_vct * pid_vector) {
int ret;
int first_recorded = 0;
@@ -118,16 +130,19 @@ pid_t ln_pid_by_name(const char * comm, cm_vector * pid_vector) {
//initialise vector
- ret = cm_new_vector(pid_vector, sizeof(pid_t));
- if (ret) {
- ln_errno = LN_ERR_LIBCMORE;
- return -1;
+ if (pid_vector != NULL) {
+ ret = cm_new_vct(pid_vector, sizeof(pid_t));
+ if (ret) {
+ mc_errno = MC_ERR_CMORE;
+ return -1;
+ }
}
//open proc directory
ds = opendir("/proc");
if (ds == NULL) {
- ln_errno = LN_ERR_PROC_NAV;
+ if (pid_vector != NULL) cm_del_vct(pid_vector);
+ mc_errno = MC_ERR_PROC_NAV;
return -1;
}
@@ -141,8 +156,8 @@ pid_t ln_pid_by_name(const char * comm, cm_vector * pid_vector) {
temp_pid = (pid_t) strtoul(d_ent->d_name, NULL, 10);
if (errno == ERANGE) {
closedir(ds);
- cm_del_vector(pid_vector);
- ln_errno = LN_ERR_PROC_NAV;
+ if (pid_vector != NULL) cm_del_vct(pid_vector);
+ mc_errno = MC_ERR_PROC_NAV;
return -1;
}
@@ -150,27 +165,31 @@ pid_t ln_pid_by_name(const char * comm, cm_vector * pid_vector) {
ret = _get_status_name(name_buf, temp_pid);
if (ret) {
closedir(ds);
- cm_del_vector(pid_vector);
+ if (pid_vector != NULL) cm_del_vct(pid_vector);
return -1;
}
//if found a match
ret = strcmp(name_buf, comm);
if (!ret) {
-
- //add pid_t to list of potential PIDs
- ret = cm_vector_append(pid_vector, (cm_byte *) &temp_pid);
- if (ret) {
- closedir(ds);
- cm_del_vector(pid_vector);
- ln_errno = LN_ERR_LIBCMORE;
- return -1;
- }
+ //add pid_t to list of potential PIDs if a vector is provided
+ if (pid_vector != NULL) {
+
+ ret = cm_vct_apd(pid_vector, (cm_byte *) &temp_pid);
+ if (ret) {
+ closedir(ds);
+ cm_del_vct(pid_vector);
+ mc_errno = MC_ERR_CMORE;
+ return -1;
+ }
+ }
+
//save first pid
if (!first_recorded) {
first_pid = temp_pid;
++first_recorded;
+ if (pid_vector == NULL) break;
}
}//end if found process with matching name
@@ -179,8 +198,8 @@ pid_t ln_pid_by_name(const char * comm, cm_vector * pid_vector) {
ret = closedir(ds);
if (ret) {
- cm_del_vector(pid_vector);
- ln_errno = LN_ERR_PROC_NAV;
+ if (pid_vector != NULL) cm_del_vct(pid_vector);
+ mc_errno = MC_ERR_PROC_NAV;
return -1;
}
@@ -188,8 +207,7 @@ pid_t ln_pid_by_name(const char * comm, cm_vector * pid_vector) {
}
-//get name of a pid
-int ln_name_by_pid(const pid_t pid, char * name_buf) {
+int mc_name_by_pid(const pid_t pid, char * name_buf) {
int ret;
@@ -201,8 +219,7 @@ int ln_name_by_pid(const pid_t pid, char * name_buf) {
}
-//give byte string, return char hex string, double the size
-void ln_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out) {
+void mc_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out) {
cm_byte nibble;
int count;
diff --git a/src/lib/util.h b/src/lib/util.h
index 81422c3..4a55cac 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -1,14 +1,21 @@
#ifndef UTIL_H
#define UTIL_H
-#include
+//external libraries
+#include
-//external
-const char * ln_pathname_to_basename(const char * pathname);
-int ln_pid_by_name(const char * comm, cm_vector * pid_vector);
-int ln_name_by_pid(const pid_t pid, char * name_buf);
-void ln_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out);
+#ifdef DEBUG
+//internal
+void _line_to_name(const char * line_buf, char * name_buf);
+int _get_status_name(char * name_buf, const pid_t pid);
+#endif
+//external
+char * mc_pathname_to_basename(const char * pathname);
+int mc_pid_by_name(const char * comm, cm_vct * pid_vector);
+int mc_name_by_pid(const pid_t pid, char * name_buf);
+void mc_bytes_to_hex(const cm_byte * inp, const int inp_len, char * out);
+
#endif
diff --git a/src/test/Makefile b/src/test/Makefile
index 35bfc66..59144b2 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -2,35 +2,46 @@
# This makefile takes the following variables:
#
-# CC - Compiler.
-# BUILD_DIR - Base build directory.
+# CC - Compiler.
+# BUILD_DIR - Unit test build directory.
+# LIB_BIN_DIR - Library artifact directory.
+#
+# _CFLAGS - Compiler flags.
+# _WARN_OPTS - Compiler warnings.
+
-CFLAGS=-O0 -ggdb -Wno-unused-but-set-variable
+#[parameters]
+CFLAGS=${_CFLAGS} -fsanitize=address -fsanitize-recover=address
+WARN_OPTS+=${_WARN_OPTS} -Wno-unused-variable -Wno-unused-but-set-variable
+LDFLAGS=-L${LIB_BIN_DIR} \
+ -Wl,-rpath=${LIB_BIN_DIR} -lmcry -lcmore -lcheck -lsubunit -static-libasan
-INCLUDE=-L${BUILD_DIR}/lib -Wl,-rpath=${BUILD_DIR}/lib -lcmore -llain
-SOURCES_TEST=iface.c map.c main.c util.c
-HEADERS_TEST=test.h
-OBJECTS_TEST=${SOURCES_TEST:.c=.o}
+#[build constants]
+SOURCES_TEST=check_map.c check_procfs_iface.c check_krncry_iface.c \
+ check_map_util.c check_util.c iface_helper.c info.c \
+ map_helper.c suites.c target_helper.c main.c
+OBJECTS_TEST=${SOURCES_TEST:%.c=${BUILD_DIR}/%.o}
+TARGET_DIR=${shell pwd}/target
-TEST=run_tests
-test:${TEST}
-> echo ${BUILD_DIR}/lib
-> mkdir -p ${BUILD_DIR}/test
-> mv ${TEST} ${BUILD_DIR}/test
+#[targets]
+TESTS=test
-${TEST}: ${OBJECTS_TEST}
-> ${CC} ${CFLAGS} ${OBJECTS_TEST} ${HEADERS_TEST} \
- -o ${TEST} ${INCLUDE}
+tests: tgt ${TESTS}
+> mkdir -p ${BUILD_DIR}
+> mv ${TESTS} ${BUILD_DIR}
-${OBJECTS_LIB}: ${SOURCES_LIB} ${HEADERS_LIB}
-> ${CC} ${CFLAGS} -c ${SOURCES_TEST} -o ${OBJECTS_TEST} ${INCLUDE}
+${TESTS}: ${OBJECTS_TEST}
+> ${CC} ${CFLAGS} ${WARN_OPTS} -o $@ $^ ${LDFLAGS}
-clean_all: clean_src clean_build
+${BUILD_DIR}/%.o: %.c
+> ${CC} ${CFLAGS} ${WARN_OPTS} -c $< -o $@
-clean_src:
-> -rm -f *.o
+tgt:
+> $(MAKE) -C ${TARGET_DIR} target CC='${CC}' BUILD_DIR='${BUILD_DIR}'
-clean_build:
-> -rm ${BUILD_DIR}/test/${TEST}
+clean:
+> ${MAKE} -C ${TARGET_DIR} clean BUILD_DIR='${BUILD_DIR}'
+> -rm -v ${BUILD_DIR}/${TESTS}
+> -rm -v ${OBJECTS_TEST}
diff --git a/src/test/check_krncry_iface.c b/src/test/check_krncry_iface.c
new file mode 100644
index 0000000..4a7618e
--- /dev/null
+++ b/src/test/check_krncry_iface.c
@@ -0,0 +1,110 @@
+//standard library
+#include
+#include
+
+//system headers
+#include
+
+//external libraries
+#include
+#include
+
+//local headers
+#include "suites.h"
+#include "iface_helper.h"
+
+//test target headers
+#include "../lib/memcry.h"
+
+
+
+/*
+ * [BASIC TEST]
+ */
+
+/*
+ * NOTE: Unit tests for interfaces are standardised through the iface helper.
+ */
+
+
+
+/*
+ * --- [HELPERS] ---
+ */
+
+static void _assert_session(mc_session * se, pid_t pid) {
+
+ ck_assert_int_ne(se->major, -1);
+ ck_assert_int_eq(se->fd_dev_krncry, pid);
+
+ return;
+}
+
+
+
+/*
+ * --- [UNIT TESTS] ---
+ */
+
+//krncry_open() & krncry_close() [no fixture]
+START_TEST(test_krncry_mc_open_close) {
+
+ assert_iface_open_close(KRNCRY, _assert_session);
+ return;
+
+} END_TEST
+
+
+//krncry_update_map() [no fixture]
+START_TEST(test_krncry_mc_update_map) {
+
+ assert_iface_update_map(KRNCRY);
+ return;
+
+} END_TEST
+
+
+//krncry_read() & krncry_write() [no fixture]
+START_TEST(test_krncry_mc_read_write) {
+
+ assert_iface_read_write(KRNCRY);
+ return;
+
+} END_TEST
+
+
+
+/*
+ * --- [SUITE] ---
+ */
+
+Suite * krncry_iface_suite() {
+
+ //test cases
+ TCase * tc_krncry_mc_open_close;
+ TCase * tc_krncry_mc_update_map;
+ TCase * tc_krncry_mc_read_write;
+
+ Suite * s = suite_create("krncry_iface");
+
+
+ //tc_krncry_mc_open_close
+ tc_krncry_mc_open_close = tcase_create("krncry_mc_open_close");
+ tcase_add_test(tc_krncry_mc_open_close, test_krncry_mc_open_close);
+
+ //tc_krncry_mc_update_map
+ tc_krncry_mc_update_map = tcase_create("krncry_mc_update_map");
+ tcase_add_test(tc_krncry_mc_update_map, test_krncry_mc_update_map);
+
+ //tc_krncry_mc_read_write
+ tc_krncry_mc_read_write = tcase_create("krncry_mc_read_write");
+ tcase_add_test(tc_krncry_mc_read_write, test_krncry_mc_read_write);
+
+
+ //add test cases to krncry interface test suite
+ suite_add_tcase(s, tc_krncry_mc_open_close);
+ suite_add_tcase(s, tc_krncry_mc_update_map);
+ suite_add_tcase(s, tc_krncry_mc_read_write);
+
+ return s;
+}
diff --git a/src/test/check_map.c b/src/test/check_map.c
new file mode 100644
index 0000000..7bbb879
--- /dev/null
+++ b/src/test/check_map.c
@@ -0,0 +1,2072 @@
+//standard library
+#include
+#include
+#include
+
+//system headers
+#include
+#include
+
+#include
+
+//external libraries
+#include
+#include
+
+//local headers
+#include "suites.h"
+#include "map_helper.h"
+
+//test target headers
+#include "../lib/memcry.h"
+#include "../lib/map.h"
+
+
+
+/*
+ * [ADVANCED TEST]
+ */
+
+/*
+ * NOTE: The virtual memory map management code is complicated and as such,
+ * almost every internal function has independent tests. For these
+ * tests to run, the debug target must be built.
+ *
+ * The following functions do not have unit tests:
+ *
+ * _map_obj_add_area_insert():
+ *
+ * > Tested through `_map_obj_add_area()`
+ * and `_map_obj_add_last_area()`
+ *
+ * _map_obj_find_area_outer_node():
+ *
+ * > Tested through `_map_obj_rmv_area()`
+ * and `_map_obj_rmv_last_area()`
+ *
+ * _map_obj_rmv_area_fast():
+ *
+ * > Tested through `map_obj_rmv_area()`
+ * which calls this function.
+ *
+ * _map_obj_rmv_last_area_fast():
+ *
+ * > Tested through `map_obj_rmv_last_area()`
+ * which calls this function.
+ */
+
+/*
+ * NOTE: Functions are not tested in the same order as they appear in
+ * the map.c source file. This assists with bootstrapping many tests.
+ * The order of testing remains close.
+ */
+
+
+//globals - map
+
+/*
+ * Contains all structures inside a map, artificially allocated statically.
+ */
+
+#define STUB_MAP_AREA_NUM 10
+#define STUB_MAP_OBJ_NUM 4
+static mc_vm_map m;
+
+static mc_vm_area m_a[STUB_MAP_AREA_NUM];
+static cm_lst_node m_a_n[STUB_MAP_AREA_NUM];
+
+static mc_vm_obj m_o[STUB_MAP_OBJ_NUM];
+static cm_lst_node m_o_n[STUB_MAP_OBJ_NUM];
+
+
+//globals - standalone object
+#define AREAS_NUM 4
+#define LAST_AREAS_NUM 2
+static mc_vm_obj o;
+static cm_lst_node o_n;
+
+static mc_vm_area o_a[AREAS_NUM];
+static cm_lst_node o_a_n[AREAS_NUM];
+
+static mc_vm_area o_a_l[LAST_AREAS_NUM];
+static cm_lst_node o_a_l_n[LAST_AREAS_NUM];
+
+
+
+/*
+ * --- [HELPERS] ---
+ */
+
+//initialise a _traverse_state
+static void _init__traverse_state(_traverse_state * state,
+ cm_lst_node * next_area_node,
+ cm_lst_node * prev_obj_node) {
+
+ state->next_area_node = next_area_node;
+ state->prev_obj_node = prev_obj_node;
+
+ return;
+}
+
+
+//initialise a vm_entry stub
+static void _init_vm_entry(struct vm_entry * entry, unsigned long vm_start,
+ unsigned long vm_end, unsigned long file_off,
+ krncry_pgprot_t prot, char * file_path) {
+
+ //set entry vars
+ entry->vm_start = vm_start;
+ entry->vm_end = vm_end;
+ entry->file_off = file_off;
+ entry->prot = prot;
+
+ //set file path
+ if (file_path != NULL) {
+ strncpy(entry->file_path, file_path, PATH_MAX);
+
+ } else {
+ entry->file_path[0] = '\0';
+ }
+
+ return;
+}
+
+
+//connect nodes
+static void _connect_nodes(cm_lst_node * node_1, cm_lst_node * node_2) {
+
+ if (node_1 != NULL) {
+ node_1->next = node_2;
+ }
+
+ if (node_2 != NULL) {
+ node_2->prev = node_1;
+ }
+
+ return;
+}
+
+
+//check if a pointer points to a static allocation
+static bool _map_check_static(void * ptr, void * arr, int len, size_t ent_sz) {
+
+ for (int i = 0; i < len; ++i) {
+ if ((cm_byte *) ptr == (((cm_byte *) arr) + (i * ent_sz))) return true;
+ }
+
+ return false;
+}
+
+
+//remove static pointers from a list
+static void _map_remove_static(cm_lst * lst, void * arr,
+ int len, size_t ent_sz, bool unmapped) {
+
+ cm_lst_node * temp_node, * node, * cmp_node;
+ node = lst->head;
+
+
+ for (int i = 0; i < lst->len; ++i) {
+
+ //determine if comparing node or inner node
+ cmp_node = unmapped ? MC_GET_NODE_PTR(node) : node;
+
+ //static node
+ if (_map_check_static(cmp_node, arr, len, ent_sz)) {
+
+ //adjust iteration (part 1)
+ temp_node = node->next;
+
+ //handle node
+ if (unmapped) cm_lst_rmv_n(lst, node);
+ if (!unmapped) cm_lst_uln_n(lst, node);
+
+ //adjust iteration (part 2)
+ node = temp_node;
+ i -= 1;
+
+ //dynamic node
+ } else {
+ node = node->next;
+
+ } //end if
+
+ } //end for
+
+ return;
+}
+
+
+/*
+ * --- [FIXTURES] ---
+ */
+
+//empty map fixture
+static void _setup_empty_vm_map() {
+
+ //construct the map
+ mc_new_vm_map(&m);
+
+ return;
+}
+
+
+#ifdef DEBUG
+
+/*
+ * NOTE: I recognise setting this up and tearing it down is an enormous pain,
+ * and a time sink, however without it the majority of internal
+ * functions can't be tested.
+ */
+
+//stub map fixture
+static void _setup_stub_vm_map() {
+
+ /*
+ * Stub map:
+ *
+ * 0) 0x1000 - 0x2000 /bin/cat r--
+ * 1) 0x2000 - 0x3000 /bin/cat rw-
+ * 2) 0x3000 - 0x4000 /bin/cat r-x
+ * 3) 0x4000 - 0x5000 [heap] rw- <- gap
+ * 4) 0x6000 - 0x7000 rw- <- gap
+ * 5) 0x8000 - 0x9000 /lib/foo r--
+ * 6) 0x9000 - 0xA000 /lib/foo rw-
+ * 7) 0xA000 - 0xB000 /lib/foo r-x <- gap
+ * 8) 0xC000 - 0xD000 rw- <- gap
+ * 9) 0xE000 - 0xF000 [stack] rw-
+ */
+
+ int ret;
+ struct vm_entry entry;
+
+
+ //construct the map
+ mc_new_vm_map(&m);
+
+ //construct objects
+ _map_new_vm_obj(&m_o[0], &m, "/bin/cat");
+ _map_new_vm_obj(&m_o[1], &m, "[heap]");
+ _map_new_vm_obj(&m_o[2], &m, "/lib/foo");
+ _map_new_vm_obj(&m_o[3], &m, "[stack]");
+
+ //construct object nodes
+ for (int i = 0; i < STUB_MAP_OBJ_NUM; ++i) {
+
+ create_lst_wrapper(&m_o_n[i], &m_o[i]);
+ }
+
+
+ //construct areas
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x100,
+ MC_ACCESS_READ, "/bin/cat");
+ _map_init_vm_area(&m_a[0], &entry, &m_o_n[0], NULL, &m);
+
+ _init_vm_entry(&entry, 0x2000, 0x3000, 0x200,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, "/bin/cat");
+ _map_init_vm_area(&m_a[1], &entry, &m_o_n[0], NULL, &m);
+
+ _init_vm_entry(&entry, 0x3000, 0x4000, 0x300,
+ MC_ACCESS_READ | MC_ACCESS_EXEC, "/bin/cat");
+ _map_init_vm_area(&m_a[2], &entry, &m_o_n[0], NULL, &m);
+
+ _init_vm_entry(&entry, 0x4000, 0x5000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, "[heap]");
+ _map_init_vm_area(&m_a[3], &entry, &m_o_n[1], NULL, &m);
+
+ _init_vm_entry(&entry, 0x6000, 0x7000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL);
+ _map_init_vm_area(&m_a[4], &entry, NULL, &m_o_n[1], &m);
+
+ _init_vm_entry(&entry, 0x8000, 0x9000, 0x100,
+ MC_ACCESS_READ, "/lib/foo");
+ _map_init_vm_area(&m_a[5], &entry, &m_o_n[2], NULL, &m);
+
+ _init_vm_entry(&entry, 0x9000, 0xA000, 0x200,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, "/lib/foo");
+ _map_init_vm_area(&m_a[6], &entry, &m_o_n[2], NULL, &m);
+
+ _init_vm_entry(&entry, 0xA000, 0xB000, 0x300,
+ MC_ACCESS_READ | MC_ACCESS_EXEC, "/lib/foo");
+ _map_init_vm_area(&m_a[7], &entry, &m_o_n[2], NULL, &m);
+
+ _init_vm_entry(&entry, 0xC000, 0xD000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL);
+ _map_init_vm_area(&m_a[8], &entry, NULL, &m_o_n[2], &m);
+
+ _init_vm_entry(&entry, 0xE000, 0xF000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, "[stack]");
+ _map_init_vm_area(&m_a[9], &entry, &m_o_n[3], NULL, &m);
+
+ //construct area nodes
+ for (int i = 0; i < STUB_MAP_AREA_NUM; ++i) {
+
+ create_lst_wrapper(&m_a_n[i], &m_a[i]);
+ }
+
+
+ //connect areas to objects
+ ret = _map_obj_add_area(&m_o[0], &m_a_n[0]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_area(&m_o[0], &m_a_n[1]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_area(&m_o[0], &m_a_n[2]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_area(&m_o[1], &m_a_n[3]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_last_area(&m_o[1], &m_a_n[4]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_area(&m_o[2], &m_a_n[5]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_area(&m_o[2], &m_a_n[6]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_area(&m_o[2], &m_a_n[7]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_last_area(&m_o[2], &m_a_n[8]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_obj_add_area(&m_o[3], &m_a_n[9]);
+ ck_assert_int_eq(ret, 0);
+
+
+ //connect area nodes
+ for (int i = 0; i < STUB_MAP_AREA_NUM - 1; ++i) {
+ _connect_nodes(&m_a_n[i], &m_a_n[i+1]);
+ }
+ _connect_nodes(&m_a_n[STUB_MAP_AREA_NUM-1], &m_a_n[0]);
+
+ //connect object nodes
+ for (int i = 0; i < STUB_MAP_OBJ_NUM - 1; ++i) {
+ _connect_nodes(&m_o_n[i], &m_o_n[i+1]);
+ }
+ _connect_nodes(m.vm_objs.head, &m_o_n[0]);
+ _connect_nodes(&m_o_n[STUB_MAP_OBJ_NUM-1], m.vm_objs.head);
+
+
+ //connect area nodes and object nodes to the map
+ m.vm_areas.head = &m_a_n[0];
+
+ m.vm_areas.len += STUB_MAP_AREA_NUM;
+ m.vm_objs.len += STUB_MAP_OBJ_NUM;
+
+ return;
+}
+#endif
+
+
+/*
+ * NOTE: The map starts out containing only statically allocated areas,
+ * objects, and nodes. After tests are carried out, it can contain
+ * a mix of statically and dynamically allocated data. The map
+ * destructor expects exclusively dynamically allocated data. As such,
+ * it is important to remove all statically allocated data from the map
+ * before calling the destructor.
+ */
+
+static void _teardown_vm_map() {
+
+ cm_lst_node * node;
+ mc_vm_obj * obj;
+ mc_vm_area * area;
+
+
+ //setup iteration
+ node = m.vm_objs.head;
+
+ //empty all object lists
+ for (int i = 0; i < m.vm_objs.len; ++i) {
+
+ //empty lists of this object (always dynamic)
+ obj = MC_GET_NODE_OBJ(node);
+ cm_lst_emp(&obj->vm_area_node_ps);
+ cm_lst_emp(&obj->last_vm_area_node_ps);
+ node = node->next;
+ }
+
+ //deallocate names of objects
+ for (int i = 0; i < STUB_MAP_OBJ_NUM; ++i) {
+ free(m_o[i].pathname);
+ }
+
+ //remove static objects & areas
+ _map_remove_static(&m.vm_objs, m_o_n,
+ STUB_MAP_OBJ_NUM, sizeof(cm_lst_node), false);
+ _map_remove_static(&m.vm_areas, m_a_n,
+ STUB_MAP_AREA_NUM, sizeof(cm_lst_node), false);
+ _map_remove_static(&m.vm_objs_unmapped, m_o_n,
+ STUB_MAP_OBJ_NUM, sizeof(cm_lst_node), true);
+ _map_remove_static(&m.vm_areas_unmapped, m_a_n,
+ STUB_MAP_AREA_NUM, sizeof(cm_lst_node), true);
+
+
+ //call regular destructor
+ mc_del_vm_map(&m);
+
+ return;
+}
+
+
+#ifdef DEBUG
+//empty object fixture
+static void _setup_empty_vm_obj() {
+
+ //super of _setup_empty_vm_map
+ _setup_empty_vm_map();
+
+ //construct the new object
+ _map_new_vm_obj(&o, &m, "/foo/bar");
+
+ //populate the object node
+ create_lst_wrapper(&o_n, &o);
+
+ return;
+}
+#endif
+
+
+#ifdef DEBUG
+//stub object fixture
+static void _setup_stub_vm_obj() {
+
+ /*
+ * Object will have an address range of 0x1000 - 0x5000.
+ */
+
+ struct vm_entry entry;
+ uintptr_t addr = 0x1000;
+ uintptr_t last_addr = 0x5000;
+ uintptr_t file_off = 0x200;
+
+ //super of _setup_empty_vm_obj
+ _setup_empty_vm_obj();
+
+
+ //initialise areas
+ for (int i = 0; i < AREAS_NUM; ++i) {
+
+ //setup area
+ _init_vm_entry(&entry, addr, addr + 0x1000,
+ file_off, MC_ACCESS_READ, "/foo/bar");
+ _map_init_vm_area(&o_a[i], &entry, &o_n, NULL, &m);
+ create_lst_wrapper(&o_a_n[i], &o_a[i]);
+ _map_obj_add_area(&o, &o_a_n[i]);
+
+ //advance iteration
+ addr += 0x1000;
+ file_off += 0x200;
+ }
+
+
+ //initialise last areas
+ for (int i = 0; i < LAST_AREAS_NUM; ++i) {
+
+ //setup last area
+ _init_vm_entry(&entry, last_addr, last_addr + 0x1000,
+ 0x0, MC_ACCESS_READ, NULL);
+ _map_init_vm_area(&o_a_l[i], &entry, NULL, &o_n, &m);
+ create_lst_wrapper(&o_a_l_n[i], &o_a_l[i]);
+ _map_obj_add_last_area(&o, &o_a_l_n[i]);
+
+ //advance iteration
+ last_addr += 0x1000;
+ }
+
+ return;
+}
+#endif
+
+
+#ifdef DEBUG
+static void _teardown_vm_obj() {
+
+ //destroy the object
+ _map_del_vm_obj(&o);
+
+ //super of _teardown_vm_map()
+ _teardown_vm_map();
+
+ return;
+}
+#endif
+
+
+
+/*
+ * --- [UNIT TESTS] ---
+ */
+
+//mc_new_vm_map() & mc_del_vm_map() [no fixture]
+START_TEST(test_mc_new_del_vm_map) {
+
+ /*
+ * Using ASAN to check for memory leaks in the destructor.
+ */
+
+ mc_vm_obj * zero_obj;
+
+
+ //only test: construct the map
+ mc_new_vm_map(&m);
+
+ assert_vm_map(&m, 0, 1, 0, 0, 0, 0);
+
+ //check the pseudo object is present
+ zero_obj = MC_GET_NODE_OBJ(m.vm_objs.head);
+ assert_vm_obj(zero_obj, "0x0", "0x0",
+ 0x0, 0x0, 0, 0, MC_ZERO_OBJ_ID, true);
+
+ mc_del_vm_map(&m);
+
+ return;
+
+} END_TEST
+
+
+#ifdef DEBUG
+//_map_new_vm_obj() & _map_del_vm_obj() [empty map fixture]
+START_TEST(test__map_new_del_vm_obj) {
+
+ mc_vm_obj obj;
+
+ //only test: construct the object
+ _map_new_vm_obj(&obj, &m, "/foo/bar");
+
+ assert_vm_obj(&obj, "/foo/bar", "bar",
+ MC_UNDEF_ADDR, MC_UNDEF_ADDR, 0, 0, 0, true);
+ assert_vm_map(&m, 0, 1, 0, 0, 0, 1);
+
+ _map_del_vm_obj(&obj);
+
+ return;
+}
+
+
+//_map_make_zero_obj() [empty map fixture]
+START_TEST(test__map_make_zero_obj) {
+
+ mc_vm_obj zero_obj;
+
+
+ //create new object
+ _map_new_vm_obj(&zero_obj, &m, "0x0");
+
+ //only test: convert new object to pseudo object
+ _map_make_zero_obj(&zero_obj);
+
+ assert_vm_obj(&zero_obj, "0x0", "0x0",
+ 0x0, 0x0, 0, 0, MC_ZERO_OBJ_ID, true);
+ assert_vm_map(&m, 0, 1, 0, 0, 0, 1);
+
+ //destroy pseudo object
+ _map_del_vm_obj(&zero_obj);
+
+ return;
+}
+
+
+//_map_init_vm_area() [empty object fixture]
+START_TEST(test__map_init_vm_area) {
+
+ mc_vm_area area;
+ struct vm_entry entry;
+
+
+ //first test: create a stub entry & initialise the new area
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x800,
+ MC_ACCESS_READ, "/foo/bar");
+ _map_init_vm_area(&area, &entry, &o_n, NULL, &m);
+
+ assert_vm_area(&area, "/foo/bar", "bar", 0x1000, 0x2000,
+ MC_ACCESS_READ, &o_n, NULL, 0, true);
+ assert_vm_map(&m, 0, 1, 0, 0, 1, 1);
+
+
+ //second test: create a stub entry & initialise another new area
+ _init_vm_entry(&entry, 0x2000, 0x4000, 0x800,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, "/purr/meow");
+ _map_init_vm_area(&area, &entry, NULL, &o_n, &m);
+
+ assert_vm_area(&area, NULL, NULL, 0x2000, 0x4000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, &o_n, 1, true);
+ assert_vm_map(&m, 0, 1, 0, 0, 2, 1);
+
+ return;
+
+} END_TEST
+
+
+//_map_obj_add_area() [empty object fixture]
+START_TEST(test__map_obj_add_area) {
+
+ int ret;
+
+ mc_vm_area area[4];
+ cm_lst_node area_node[4], * area_node_ptr;
+ struct vm_entry entry;
+
+ uintptr_t state_first[1] = {0x2000};
+ uintptr_t state_lower[2] = {0x1000, 0x2000};
+ uintptr_t state_higher[3] = {0x1000, 0x2000, 0x4000};
+ uintptr_t state_middle[4] = {0x1000, 0x2000, 0x3000, 0x4000};
+
+
+ //initialise first area
+ _init_vm_entry(&entry, 0x2000, 0x3000, 0x800, MC_ACCESS_READ, "/foo/bar");
+ _map_init_vm_area(&area[0], &entry, &o_n, NULL, &m);
+ create_lst_wrapper(&area_node[0], &area[0]);
+
+
+ //first test: add first area to the backing object
+ _map_obj_add_area(&o, &area_node[0]);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x2000, 0x3000, 1, 0, 0, true);
+ assert_vm_obj_list(&o.vm_area_node_ps, state_first, 1);
+ area_node_ptr = MC_GET_NODE_PTR(o.vm_area_node_ps.head);
+ assert_vm_area(MC_GET_NODE_AREA(area_node_ptr), "/foo/bar", "bar",
+ 0x2000, 0x3000, MC_ACCESS_READ, &o_n, NULL, 0, true);
+
+
+ //initialise lower area
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x600, MC_ACCESS_WRITE, "/foo/bar");
+ _map_init_vm_area(&area[1], &entry, &o_n, NULL, &m);
+ create_lst_wrapper(&area_node[1], &area[1]);
+
+ //second test: add lower area to the backing object
+ _map_obj_add_area(&o, &area_node[1]);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x1000, 0x3000, 2, 0, 0, true);
+ assert_vm_obj_list(&o.vm_area_node_ps, state_lower, 2);
+ area_node_ptr = MC_GET_NODE_PTR(o.vm_area_node_ps.head);
+ assert_vm_area(MC_GET_NODE_AREA(area_node_ptr), "/foo/bar", "bar",
+ 0x1000, 0x2000, MC_ACCESS_WRITE, &o_n, NULL, 1, true);
+
+
+ //initialise higher area
+ _init_vm_entry(&entry, 0x4000, 0x5000, 0x900, MC_ACCESS_EXEC, "/foo/bar");
+ _map_init_vm_area(&area[2], &entry, &o_n, NULL, &m);
+ create_lst_wrapper(&area_node[2], &area[2]);
+
+ //third test: add lower area to the backing object
+ _map_obj_add_area(&o, &area_node[2]);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x1000, 0x5000, 3, 0, 0, true);
+ assert_vm_obj_list(&o.vm_area_node_ps, state_higher, 3);
+ area_node_ptr = MC_GET_NODE_PTR(o.vm_area_node_ps.head->prev);
+ assert_vm_area(MC_GET_NODE_AREA(area_node_ptr), "/foo/bar", "bar",
+ 0x4000, 0x5000, MC_ACCESS_EXEC, &o_n, NULL, 2, true);
+
+
+ //initialise middle area
+ _init_vm_entry(&entry, 0x3000, 0x4000, 0x880, MC_ACCESS_READ, "/foo/bar");
+ _map_init_vm_area(&area[3], &entry, &o_n, NULL, &m);
+ create_lst_wrapper(&area_node[3], &area[3]);
+
+ //fourth test: add middle area to the backing object
+ _map_obj_add_area(&o, &area_node[3]);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x1000, 0x5000, 4, 0, 0, true);
+ assert_vm_obj_list(&o.vm_area_node_ps, state_middle, 4);
+ area_node_ptr = MC_GET_NODE_PTR(o.vm_area_node_ps.head->next->next);
+ assert_vm_area(MC_GET_NODE_AREA(area_node_ptr), "/foo/bar", "bar",
+ 0x3000, 0x4000, MC_ACCESS_READ, &o_n, NULL, 3, true);
+
+ return;
+
+} END_TEST
+
+
+//_map_obj_add_last_area() [empty object fixture]
+START_TEST(test__map_obj_add_last_area) {
+
+ int ret;
+
+ mc_vm_area last_area[4];
+ cm_lst_node last_area_node[4], * last_area_node_ptr;
+ struct vm_entry entry;
+
+ uintptr_t state_first[1] = {0x2000};
+ uintptr_t state_lower[2] = {0x1000, 0x2000};
+ uintptr_t state_higher[3] = {0x1000, 0x2000, 0x4000};
+ uintptr_t state_middle[4] = {0x1000, 0x2000, 0x3000, 0x4000};
+
+
+ //initialise first area
+ _init_vm_entry(&entry, 0x2000, 0x3000, 0x800, MC_ACCESS_READ, NULL);
+ _map_init_vm_area(&last_area[0], &entry, NULL, &o_n, &m);
+ create_lst_wrapper(&last_area_node[0], &last_area[0]);
+
+ //first test: add first area to the backing object
+ _map_obj_add_area_insert(&o.last_vm_area_node_ps, &last_area_node[0]);
+
+ assert_vm_obj(&o, "/foo/bar", "bar",
+ MC_UNDEF_ADDR, MC_UNDEF_ADDR, 0, 1, 0, true);
+ assert_vm_obj_list(&o.last_vm_area_node_ps, state_first, 1);
+ last_area_node_ptr = MC_GET_NODE_PTR(o.last_vm_area_node_ps.head);
+ assert_vm_area(MC_GET_NODE_AREA(last_area_node_ptr), NULL, NULL,
+ 0x2000, 0x3000, MC_ACCESS_READ, NULL, &o_n, 0, true);
+
+
+ //initialise lower area
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x600, MC_ACCESS_WRITE, NULL);
+ _map_init_vm_area(&last_area[1], &entry, NULL, &o_n, &m);
+ create_lst_wrapper(&last_area_node[1], &last_area[1]);
+
+ //second test: add lower area to the backing object
+ _map_obj_add_area_insert(&o.last_vm_area_node_ps, &last_area_node[1]);
+
+ assert_vm_obj(&o, "/foo/bar", "bar",
+ MC_UNDEF_ADDR, MC_UNDEF_ADDR, 0, 2, 0, true);
+ assert_vm_obj_list(&o.last_vm_area_node_ps, state_lower, 2);
+ last_area_node_ptr = MC_GET_NODE_PTR(o.last_vm_area_node_ps.head);
+ assert_vm_area(MC_GET_NODE_AREA(last_area_node_ptr), NULL, NULL,
+ 0x1000, 0x2000, MC_ACCESS_WRITE, NULL, &o_n, 1, true);
+
+
+ //initialise higher area
+ _init_vm_entry(&entry, 0x4000, 0x5000, 0x900, MC_ACCESS_EXEC, NULL);
+ _map_init_vm_area(&last_area[2], &entry, NULL, &o_n, &m);
+ create_lst_wrapper(&last_area_node[2], &last_area[2]);
+
+ //third test: add lower area to the backing object
+ _map_obj_add_area_insert(&o.last_vm_area_node_ps, &last_area_node[2]);
+
+ assert_vm_obj(&o, "/foo/bar", "bar",
+ MC_UNDEF_ADDR, MC_UNDEF_ADDR, 0, 3, 0, true);
+ assert_vm_obj_list(&o.last_vm_area_node_ps, state_higher, 3);
+ last_area_node_ptr = MC_GET_NODE_PTR(o.last_vm_area_node_ps.head->prev);
+ assert_vm_area(MC_GET_NODE_AREA(last_area_node_ptr), NULL, NULL,
+ 0x4000, 0x5000, MC_ACCESS_EXEC, NULL, &o_n, 2, true);
+
+
+ //initialise middle area
+ _init_vm_entry(&entry, 0x3000, 0x4000, 0x880, MC_ACCESS_READ, NULL);
+ _map_init_vm_area(&last_area[3], &entry, NULL, &o_n, &m);
+ create_lst_wrapper(&last_area_node[3], &last_area[3]);
+
+ //fourth test: add middle area to the backing object
+ _map_obj_add_area_insert(&o.last_vm_area_node_ps, &last_area_node[3]);
+
+ assert_vm_obj(&o, "/foo/bar", "bar",
+ MC_UNDEF_ADDR, MC_UNDEF_ADDR, 0, 4, 0, true);
+ assert_vm_obj_list(&o.last_vm_area_node_ps, state_middle, 4);
+ last_area_node_ptr
+ = MC_GET_NODE_PTR(o.last_vm_area_node_ps.head->next->next);
+ assert_vm_area(MC_GET_NODE_AREA(last_area_node_ptr), NULL, NULL,
+ 0x3000, 0x4000, MC_ACCESS_READ, NULL, &o_n, 3, true);
+
+ return;
+
+} END_TEST
+
+
+//_map_obj_rmv_area() [stub object fixture]
+START_TEST(test__map_obj_rmv_area) {
+
+ int ret;
+
+ uintptr_t state_middle[3] = {0x1000, 0x3000, 0x4000};
+ uintptr_t state_first[2] = {0x3000, 0x4000};
+ uintptr_t state_last[1] = {0x3000};
+
+
+ //first test: remove middle area
+ ret = _map_obj_rmv_area(&o, &o_a_n[1]);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x1000, 0x5000, 3, 2, 0, true);
+ assert_vm_obj_list(&o.vm_area_node_ps, state_middle, 3);
+
+
+ //second test: remove first area
+ ret = _map_obj_rmv_area(&o, &o_a_n[0]);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x3000, 0x5000, 2, 2, 0, true);
+ assert_vm_obj_list(&o.vm_area_node_ps, state_first, 2);
+
+
+ //third test: remove last area
+ ret = _map_obj_rmv_area(&o, &o_a_n[3]);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x3000, 0x4000, 1, 2, 0, true);
+ assert_vm_obj_list(&o.vm_area_node_ps, state_last, 1);
+
+
+ //fourth test: remove only remaining area
+ ret = _map_obj_rmv_area(&o, &o_a_n[2]);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_obj(&o, "/foo/bar", "bar",
+ MC_UNDEF_ADDR, MC_UNDEF_ADDR, 0, 2, 0, true);
+ assert_vm_obj_list(&o.vm_area_node_ps, NULL, 0);
+
+ return;
+
+} END_TEST
+
+
+//_map_obj_rmv_last_area [stub object fixture]
+START_TEST(test__map_obj_rmv_last_area) {
+
+ int ret;
+
+ uintptr_t state_first[1] = {0x6000};
+
+
+ //first test: remove first last area
+ ret = _map_obj_rmv_last_area(&o, &o_a_l_n[0]);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x1000, 0x5000, 4, 1, 0, true);
+ assert_vm_obj_list(&o.last_vm_area_node_ps, state_first, 1);
+
+
+ //second test: remove only remaining last area
+ ret = _map_obj_rmv_last_area(&o, &o_a_l_n[1]);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_obj(&o, "/foo/bar", "bar", 0x1000, 0x5000, 4, 0, 0, true);
+ assert_vm_obj_list(&o.last_vm_area_node_ps, NULL, 0);
+
+ return;
+}
+
+
+//_map_is_pathname_in_obj() [empty object fixture]
+START_TEST(test__map_is_pathname_in_obj) {
+
+ bool ret;
+
+
+ //first test: path is in the object
+ ret = _map_is_pathname_in_obj("/foo/bar", &o);
+ ck_assert(ret);
+
+ //second test: path is not in the object
+ ret = _map_is_pathname_in_obj("anonmap", &o);
+ ck_assert(!ret);
+
+ return;
+
+} END_TEST
+
+
+//_map_find_obj_for_area [empty map fixture]
+START_TEST(test__map_find_obj_for_area) {
+
+ int ret;
+
+ mc_vm_obj objs[3];
+ cm_lst_node obj_nodes[3];
+ char * obj_paths[3] = {"/lib/libc", "/lib/libpthread", "anonmap"};
+
+ struct vm_entry entry;
+ _traverse_state state;
+
+
+ //construct test objects
+ for (int i = 0; i < 3; ++i) {
+ _map_new_vm_obj(&objs[i], &m, obj_paths[i]);
+ create_lst_wrapper(&obj_nodes[i], &objs[i]);
+ }
+
+ //connect test objects
+ _connect_nodes(&obj_nodes[0], &obj_nodes[1]);
+ _connect_nodes(&obj_nodes[1], &obj_nodes[2]);
+
+
+ //first test: new object, state empty
+ _init__traverse_state(&state, NULL, NULL);
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x500,
+ MC_ACCESS_READ, "/lib/libc");
+
+ ret = _map_find_obj_for_area(&entry, &state);
+ ck_assert_int_eq(ret, _MAP_OBJ_NEW);
+
+
+ //second test: new object, state full
+ _init__traverse_state(&state, NULL, &obj_nodes[0]);
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x500,
+ MC_ACCESS_READ, "anonmap");
+
+ ret = _map_find_obj_for_area(&entry, &state);
+ ck_assert_int_eq(ret, _MAP_OBJ_NEW);
+
+
+ //third test: previous object
+ _init__traverse_state(&state, NULL, &obj_nodes[0]);
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x500,
+ MC_ACCESS_READ, "/lib/libc");
+
+ ret = _map_find_obj_for_area(&entry, &state);
+ ck_assert_int_eq(ret, _MAP_OBJ_PREV);
+
+
+ //fourth test: next object
+ _init__traverse_state(&state, NULL, &obj_nodes[0]);
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x500,
+ MC_ACCESS_READ, "/lib/libpthread");
+
+ ret = _map_find_obj_for_area(&entry, &state);
+ ck_assert_int_eq(ret, _MAP_OBJ_NEXT);
+
+
+ //destruct test objects
+ for (int i = 0; i < 3; ++i) {
+ _map_del_vm_obj(&objs[i]);
+ }
+
+ return;
+
+} END_TEST
+
+
+//_map_backtrack_unmapped_obj_last_vm_areas() [stub map fixture]
+START_TEST(test__map_backtrack_unmapped_obj_last_vm_areas) {
+
+ /*
+ * NOTE: For this test, `m_o{_n}` arrays are used to refer to objects
+ * in the stub map. Note `m_o{_n}[0]` is not the first object in
+ * in the map; the map contains a pseudo object that is not in
+ * the `m_o{_n}` arrays.
+ */
+
+ int ret;
+
+ mc_vm_obj * zero_obj;
+ cm_lst_node * zero_node;
+
+ uintptr_t first_state[2] = {0x6000, 0xC000};
+ uintptr_t second_state[2] = {0x6000, 0xC000};
+ uintptr_t third_state[2] = {0x6000, 0xC000};
+
+
+ //first test: backtrack `/lib/foo`'s last area (index: 8)
+ ret = _map_backtrack_unmapped_obj_last_vm_areas(&m_o_n[2]);
+ ck_assert_int_eq(ret, 0);
+
+ //check `/lib/foo` no longer has any last areas associated with it
+ assert_vm_obj(&m_o[2], "/lib/foo", "foo", 0x8000, 0xB000, 3, 0, 2, true);
+
+ //check `[heap]` now has two last areas associated with it
+ assert_vm_obj(&m_o[1], "[heap]", "[heap]", 0x4000, 0x5000, 1, 2, 1, true);
+ assert_vm_obj_list(&m_o[1].last_vm_area_node_ps, first_state, 2);
+
+ //check the transfered last area (index: 8) now points to `[heap]`
+ assert_vm_area(&m_a[8], NULL, NULL, 0xC000, 0xD000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE,
+ NULL, &m_o_n[1], 8, true);
+
+
+ //second test: backtrack `[heap]`'s two last areas (indeces: 4, 8)
+ ret = _map_backtrack_unmapped_obj_last_vm_areas(&m_o_n[1]);
+ ck_assert_int_eq(ret, 0);
+
+ //check `[heap]` no longer has any last areas associated with it
+ assert_vm_obj(&m_o[1], "[heap]", "[heap]", 0x4000, 0x5000, 1, 0, 1, true);
+
+ //check `/bin/cat` now has two last areas associated with it
+ assert_vm_obj(&m_o[0], "/bin/cat", "cat", 0x1000, 0x4000, 3, 2, 0, true);
+ assert_vm_obj_list(&m_o[0].last_vm_area_node_ps, first_state, 2);
+
+ //check the transfered last areas (indeces: 4, 8) now point to `/bin/cat`
+ assert_vm_area(&m_a[4], NULL, NULL, 0x6000, 0x7000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, &m_o_n[0], 4, true);
+
+ assert_vm_area(&m_a[8], NULL, NULL, 0xC000, 0xD000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, &m_o_n[0], 8, true);
+
+
+ //third test: backtrack `/bin/cat`'s two lat areas (indeces: 4, 8)
+ ret = _map_backtrack_unmapped_obj_last_vm_areas(&m_o_n[0]);
+ ck_assert_int_eq(ret, 0);
+
+ //get the pseudo object
+ zero_node = cm_lst_get_n(&m.vm_objs, 0);
+ zero_obj = MC_GET_NODE_OBJ(zero_node);
+
+ //check `/bin/cat` no longer has any last areas associated with it
+ assert_vm_obj(&m_o[0], "/bin/cat", "cat", 0x1000, 0x4000, 3, 0, 0, true);
+
+ //check the pseudo object now has two last areas associated with it
+ assert_vm_obj(zero_obj, "0x0", "0x0", 0x0, 0x0,
+ 0, 2, MC_ZERO_OBJ_ID, true);
+ assert_vm_obj_list(&zero_obj->last_vm_area_node_ps, third_state, 2);
+
+ //check the transfered last areas (indeces: 4, 8) now point to `/bin/cat`
+ assert_vm_area(&m_a[4], NULL, NULL, 0x6000, 0x7000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, zero_node, 4, true);
+
+ assert_vm_area(&m_a[8], NULL, NULL, 0xC000, 0xD000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, zero_node, 8, true);
+
+ return;
+
+} END_TEST
+
+
+//_map_forward_unmapped_obj_last_vm_areas() [stub map fixture]
+START_TEST(test__map_forward_unmapped_obj_last_vm_areas) {
+
+ int ret;
+
+ uintptr_t first_heap_state[2] = {0x6000, 0xC000};
+
+ uintptr_t second_heap_state[1] = {0x6000};
+ uintptr_t second_lib_foo_state[1] = {0xC000};
+
+
+ //setup the test by backtracking `/lib/foo`'s and `[heap]`'s last areas.
+ ret = _map_backtrack_unmapped_obj_last_vm_areas(&m_o_n[2]);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_backtrack_unmapped_obj_last_vm_areas(&m_o_n[1]);
+ ck_assert_int_eq(ret, 0);
+
+
+ //first test: pretend `[heap]` object was just inserted
+ ret = _map_forward_unmapped_obj_last_vm_areas(&m_o_n[1]);
+ ck_assert_int_eq(ret, 0);
+
+ //check `/bin/cat` has no last areas associated with it
+ assert_vm_obj(&m_o[0], "/bin/cat", "cat", 0x1000, 0x4000, 3, 0, 0, true);
+ assert_vm_obj_list(&m_o[0].last_vm_area_node_ps, NULL, 0);
+
+ //check the transferred last areas (indeces 4, 8) now point to correct objs
+ assert_vm_area(&m_a[4], NULL, NULL, 0x6000, 0x7000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, &m_o_n[1], 4, true);
+
+ assert_vm_area(&m_a[8], NULL, NULL, 0xC000, 0xD000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, &m_o_n[1], 8, true);
+
+
+ //second test: pretend the `/lib/foo` object was just inserted
+ ret = _map_forward_unmapped_obj_last_vm_areas(&m_o_n[2]);
+ ck_assert_int_eq(ret, 0);
+
+ //check `[heap]` has only one last area associated with it
+ assert_vm_obj(&m_o[1], "[heap]", "[heap]", 0x4000, 0x5000, 1, 1, 1, true);
+ assert_vm_obj_list(&m_o[1].last_vm_area_node_ps, second_heap_state, 1);
+
+ //check `/lib/foo` now has one last area associated with it
+ assert_vm_obj(&m_o[2], "/lib/foo", "foo", 0x8000, 0xB000, 3, 1, 2, true);
+ assert_vm_obj_list(&m_o[2].last_vm_area_node_ps, second_lib_foo_state, 1);
+
+ //check the transferred last areas (indeces: 4, 8) now point to correct objs
+ assert_vm_area(&m_a[4], NULL, NULL, 0x6000, 0x7000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, &m_o_n[1], 4, true);
+
+ assert_vm_area(&m_a[8], NULL, NULL, 0xC000, 0xD000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL, &m_o_n[2], 8, true);
+
+ return;
+
+} END_TEST
+
+
+//_map_unlink_unmapped_obj() [stub map fixture]
+START_TEST(test__map_unlink_unmapped_obj) {
+
+ int ret;
+
+ _traverse_state state;
+ mc_vm_obj * obj;
+
+ uintptr_t heap_state[2] = {0x6000, 0xC000};
+
+ struct obj_check obj_state[4] = { //start index: 0
+ {"0x0", 0x0, 0x0},
+ {"cat", 0x1000, 0x4000},
+ {"[heap]", 0x4000, 0x5000},
+ {"[stack]", 0xE000, 0xF000}
+ };
+
+ struct obj_check unmapped_obj_state[1] = { //start index: 0
+ {"foo", MC_UNDEF_ADDR, MC_UNDEF_ADDR}
+ };
+
+
+ //only test: unlink `/lib/foo`
+ state.prev_obj_node = &m_o_n[2];
+
+ //clear constituent areas
+ ret = cm_lst_emp(&m_o[2].vm_area_node_ps);
+ ck_assert_int_eq(ret, 0);
+
+ ret = _map_unlink_unmapped_obj(&m_o_n[2], &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ //check `/lib/foo` has no last areas associated with it, and is unmapped
+ assert_vm_obj(&m_o[2], "/lib/foo", "foo",
+ MC_UNDEF_ADDR, MC_UNDEF_ADDR, 0, 0, 2, false);
+ assert_vm_obj_list(&m_o[2].last_vm_area_node_ps, NULL, 0);
+
+ //check `[heap]` has 2 last areas associated with it
+ assert_vm_obj(&m_o[1], "[heap]", "[heap]", 0x4000, 0x5000, 1, 2, 1, true);
+ assert_vm_obj_list(&m_o[1].last_vm_area_node_ps, heap_state, 2);
+
+ //check state of mapped objects
+ assert_vm_map_objs(&m.vm_objs, obj_state, 0, 4, true);
+
+ //check state of unmapped objects
+ assert_vm_map_objs(&m.vm_objs_unmapped, unmapped_obj_state, 0, 1, false);
+
+ //check removed object has no links to other objects anymore
+ ck_assert(m_o[2].mapped == false);
+ ck_assert_int_eq(m_o[2].start_addr, MC_UNDEF_ADDR);
+ ck_assert_int_eq(m_o[2].end_addr, MC_UNDEF_ADDR);
+ ck_assert_ptr_null(m_o_n[2].next);
+ ck_assert_ptr_null(m_o_n[2].prev);
+
+ //check previous object state has been backtracked
+ ck_assert_ptr_eq(state.prev_obj_node, &m_o_n[1]);
+
+ return;
+
+} END_TEST
+
+
+//_map_unlink_unmapped_area() [stub map fixture]
+START_TEST(test__map_unlink_unmapped_area) {
+
+ int ret;
+ _traverse_state state;
+
+ //remove /lib/foo:1: object state
+ struct obj_check first_objs[3] = { //start index: 2
+ {"[heap]", 0x4000, 0x5000},
+ {"foo", 0x9000, 0xB000},
+ {"[stack]", 0xE000, 0xF000}
+ };
+
+ //remove /lib/foo:1: area state
+ struct area_check first_areas[3] = { //start index: 4
+ {"", 0x6000, 0x7000},
+ {"foo", 0x9000, 0xA000},
+ {"foo", 0xA000, 0xB000}
+ };
+
+ struct area_check first_areas_unmapped[1] = { //start index: 0
+ {"foo", 0x8000, 0x9000}
+ };
+
+
+ //remove [heap]: object state
+ struct obj_check second_objs[2] = { //start index: 1
+ {"cat", 0x1000, 0x4000},
+ {"foo", 0x9000, 0xB000}
+ };
+
+ struct obj_check second_objs_unmapped[1] = { //start index: 0
+ {"[heap]", MC_UNDEF_ADDR, MC_UNDEF_ADDR}
+ };
+
+ //remove [heap]: area state:
+ struct area_check second_areas[2] = { //start index: 2
+ {"cat", 0x3000, 0x4000},
+ {"", 0x6000, 0x7000}
+ };
+
+ struct area_check second_areas_unmapped[2] = { //start index: 0
+ {"foo", 0x8000, 0x9000},
+ {"[heap]", 0x4000, 0x5000}
+ };
+
+
+ //first test: remove first area of `/lib/foo`
+ state.prev_obj_node = &m_o_n[1];
+
+ ret = _map_unlink_unmapped_area(&m_a_n[5], &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_area(&m_a[5], "/lib/foo", "foo",
+ 0x8000, 0x9000, MC_ACCESS_READ,
+ NULL, NULL, 5, false);
+
+ assert_vm_map_objs(&m.vm_objs, first_objs, 2, 3, true);
+ assert_vm_map_objs(&m.vm_objs_unmapped,
+ NULL, 0, 0, false);
+ assert_vm_map_areas(&m.vm_areas, first_areas, 4, 3, true);
+ assert_vm_map_areas(&m.vm_areas_unmapped,
+ first_areas_unmapped, 0, 1, false);
+
+ ck_assert_ptr_eq(state.prev_obj_node, &m_o_n[1]);
+
+
+ //second test: remove only area of '[heap]'
+ /*state.prev_obj_node = &m_o_n[0];
+
+ ret = _map_unlink_unmapped_area(&m_a_n[3], &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_area(&m_a[3], "[heap]", "[heap]",
+ 0x4000, 0x5000, MC_ACCESS_READ | MC_ACCESS_WRITE,
+ NULL, NULL, 3, false);
+
+ assert_vm_map_objs(&m.vm_objs, second_objs, 1, 2, true);
+ assert_vm_map_objs(&m.vm_objs_unmapped,
+ second_objs_unmapped, 0, 1, false);
+ assert_vm_map_areas(&m.vm_areas, second_areas, 2, 2, true);
+ assert_vm_map_areas(&m.vm_areas_unmapped,
+ second_areas_unmapped, 0, 2, false);
+
+ ck_assert_ptr_eq(state.prev_obj_node, &m_o_n[0]);
+ */
+ return;
+
+} END_TEST
+
+
+//_map_check_area_eql() [empty map fixture]
+START_TEST(test__map_check_area_eql) {
+
+ int ret;
+
+ struct vm_entry entry;
+ mc_vm_area area;
+ cm_lst_node area_node;
+
+ mc_vm_obj obj;
+ cm_lst_node obj_node;
+
+
+ //construct a vm_obj
+ _map_new_vm_obj(&obj, &m, "/bin/cat");
+ create_lst_wrapper(&obj_node, &obj);
+
+ //create a vm_area
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x0, MC_ACCESS_READ, "/bin/cat");
+ _map_init_vm_area(&area, &entry, &obj_node, NULL, &m);
+ create_lst_wrapper(&area_node, &area);
+
+
+ //first test: entry same as vm_area
+ ret = _map_check_area_eql(&entry, &area_node);
+ ck_assert_int_eq(ret, 0);
+
+
+ //second test: entry start address is different
+ entry.vm_start = 0x500;
+ ret = _map_check_area_eql(&entry, &area_node);
+ ck_assert_int_eq(ret, -1);
+ entry.vm_start = area.start_addr;
+
+
+ //third test: entry end address is different
+ entry.vm_end = 0x2500;
+ ret = _map_check_area_eql(&entry, &area_node);
+ ck_assert_int_eq(ret, -1);
+ entry.vm_end = area.end_addr;
+
+
+ //fourth test: entry permissions are different
+ entry.prot = MC_ACCESS_READ | MC_ACCESS_WRITE;
+ ret = _map_check_area_eql(&entry, &area_node);
+ ck_assert_int_eq(ret, -1);
+ entry.prot = area.access;
+
+
+ //fifth test: both entry and area don't have a path
+ entry.file_path[0] = '\0';
+ area.pathname = NULL;
+ ret = _map_check_area_eql(&entry, &area_node);
+ ck_assert_int_eq(ret, 0);
+ entry.file_path[0] = '/';
+ area.pathname = obj.pathname;
+
+
+ //sixth test: entry has a path, area does not
+ area.pathname = NULL;
+ ret = _map_check_area_eql(&entry, &area_node);
+ ck_assert_int_eq(ret, -1);
+ area.pathname = obj.pathname;
+
+
+ //seventh test: entry does not have a path, area does
+ entry.file_path[0] = '\0';
+ ret = _map_check_area_eql(&entry, &area_node);
+ ck_assert_int_eq(ret, -1);
+ entry.file_path[0] = '/';
+
+
+ //destroy pseudo object
+ _map_del_vm_obj(&obj);
+
+ return;
+
+} END_TEST
+
+
+//_map_state_inc_area() [stub map fixture]
+START_TEST(test__map_state_inc_area) {
+
+ _traverse_state state;
+
+
+ //first test: keep
+ state.next_area_node = &m_a_n[0];
+ _map_state_inc_area(&state, _STATE_AREA_NODE_KEEP, NULL, &m);
+ ck_assert_int_eq(MC_GET_NODE_AREA(state.next_area_node)->id, 0);
+
+ //second test: advance - success
+ state.next_area_node = &m_a_n[0];
+ _map_state_inc_area(&state, _STATE_AREA_NODE_ADVANCE, NULL, &m);
+ ck_assert_int_eq(MC_GET_NODE_AREA(state.next_area_node)->id, 1);
+
+ //third test: advance - refuse (reached the end)
+ state.next_area_node = &m_a_n[9];
+ _map_state_inc_area(&state, _STATE_AREA_NODE_ADVANCE, NULL, &m);
+ ck_assert_ptr_null(state.next_area_node);
+
+ //fourth test: reassign
+ state.next_area_node = &m_a_n[0];
+ _map_state_inc_area(&state, _STATE_AREA_NODE_REASSIGN, &m_a_n[2], &m);
+ ck_assert_int_eq(MC_GET_NODE_AREA(state.next_area_node)->id, 2);
+
+ return;
+
+} END_TEST
+
+
+//_map_state_inc_obj() [stub map fixture]
+START_TEST(test__map_state_inc_obj) {
+
+ _traverse_state state;
+
+
+ //first test: advance from pseudo object
+ state.prev_obj_node = m.vm_objs.head;
+ _map_state_inc_obj(&state, &m);
+ ck_assert_int_eq(MC_GET_NODE_OBJ(state.prev_obj_node)->id, 0);
+
+ //second test: advance from regular object
+ state.prev_obj_node = &m_o_n[0];
+ _map_state_inc_obj(&state, &m);
+ ck_assert_int_eq(MC_GET_NODE_OBJ(state.prev_obj_node)->id, 1);
+
+ //third test: advance from last object
+ state.prev_obj_node = &m_o_n[3];
+ _map_state_inc_obj(&state, &m);
+ ck_assert_int_eq(MC_GET_NODE_OBJ(state.prev_obj_node)->id, 3);
+
+ return;
+
+} END_TEST
+
+
+//_map_resync_area() [stub map fixture]
+START_TEST(test__map_resync_area) {
+
+ //remove [heap]: object state
+ struct obj_check first_objs[2] = { //start index: 1
+ {"cat", 0x1000, 0x4000},
+ {"foo", 0x8000, 0xB000}
+ };
+
+ struct obj_check first_objs_unmapped[1] = { //start index: 0
+ {"[heap]", MC_UNDEF_ADDR, MC_UNDEF_ADDR}
+ };
+
+ //remove [heap]: area state
+ struct area_check first_areas[2] = { //start index: 2
+ {"cat", 0x3000, 0x4000},
+ {"", 0x6000, 0x7000}
+ };
+
+ struct area_check first_areas_unmapped[1] = { //start index 0
+ {"[heap]", 0x4000, 0x5000}
+ };
+
+
+
+ //remove /lib/foo:1,2: object state
+ struct obj_check second_objs[3] = { //start index: 1
+ {"cat", 0x1000, 0x4000},
+ {"foo", 0xA000, 0xB000},
+ {"[stack]", 0xE000, 0xF000}
+ };
+
+ struct obj_check second_objs_unmapped[1] = { //start index 0
+ {"[heap]", MC_UNDEF_ADDR, MC_UNDEF_ADDR}
+ };
+
+ //remove /lib/foo:1,2: area state
+ struct area_check second_areas[3] = { //start index 3
+ {"", 0x6000, 0x7000},
+ {"foo", 0xA000, 0xB000},
+ {"", 0xC000, 0xD000}
+ };
+
+ struct area_check second_areas_unmapped[3] = { //start index 0
+ {"[heap]", 0x4000, 0x5000},
+ {"foo", 0x8000, 0x9000},
+ {"foo", 0x9000, 0xA000},
+ };
+
+
+
+ //remove /bin/cat:1,2,3: object state
+ struct obj_check third_objs[2] = { //start index: 1
+ {"foo", 0xA000, 0xB000},
+ {"[stack]", 0xE000, 0xF000}
+ };
+
+ struct obj_check third_objs_unmapped[2] = { //start index: 0
+ {"[heap]", MC_UNDEF_ADDR, MC_UNDEF_ADDR},
+ {"cat", MC_UNDEF_ADDR, MC_UNDEF_ADDR}
+ };
+
+ //remove /bin/cat:1,2,3: area state
+ struct area_check third_areas[2] = { //start index: 0
+ {"", 0x6000, 0x7000},
+ {"foo", 0xA000, 0xB000}
+ };
+
+ struct area_check third_areas_unmapped[6] = { //start index: 0
+ {"[heap]", 0x4000, 0x5000},
+ {"foo", 0x8000, 0x9000},
+ {"foo", 0x9000, 0xA000},
+ {"cat", 0x1000, 0x2000},
+ {"cat", 0x2000, 0x3000},
+ {"cat", 0x3000, 0x4000}
+ };
+
+ int ret;
+
+ struct vm_entry entry;
+ _traverse_state state;
+
+
+ //correct by removing `[heap]`
+ _init_vm_entry(&entry, 0x6000, 0x7000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL);
+ _init__traverse_state(&state, &m_a_n[3], &m_o_n[0]);
+
+ ret = _map_resync_area(&entry, &state, &m);
+ ck_assert_int_eq(ret, 1);
+
+ //check state
+ assert_vm_area(MC_GET_NODE_AREA(state.next_area_node), NULL, NULL,
+ 0x6000, 0x7000, MC_ACCESS_READ | MC_ACCESS_WRITE,
+ NULL, &m_o_n[0], 4, true);
+ assert_vm_obj(MC_GET_NODE_OBJ(state.prev_obj_node),
+ "/bin/cat", "cat", 0x1000, 0x4000, 3, 1, 0, true);
+
+ assert_vm_map_objs(&m.vm_objs, first_objs, 1, 2, true);
+ assert_vm_map_objs(&m.vm_objs_unmapped,
+ first_objs_unmapped, 0, 1, false);
+ assert_vm_map_areas(&m.vm_areas, first_areas, 2, 2, true);
+ assert_vm_map_areas(&m.vm_areas_unmapped,
+ first_areas_unmapped, 0, 1, false);
+
+
+
+ //correct by removing first 2 areas of `/lib/foo`
+ _init_vm_entry(&entry, 0xA000, 0xB000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_EXEC, "/lib/foo");
+ _init__traverse_state(&state, &m_a_n[5], &m_o_n[0]);
+
+ ret = _map_resync_area(&entry, &state, &m);
+ ck_assert_int_eq(ret, 1);
+
+ //check state
+ assert_vm_area(MC_GET_NODE_AREA(state.next_area_node), "/lib/foo", "foo",
+ 0xA000, 0xB000, MC_ACCESS_READ | MC_ACCESS_EXEC,
+ &m_o_n[2], NULL, 7, true);
+
+ assert_vm_obj(MC_GET_NODE_OBJ(state.prev_obj_node), "/bin/cat", "cat",
+ 0x1000, 0x4000, 3, 1, 0, true);
+
+ assert_vm_map_objs(&m.vm_objs, second_objs, 1, 3, true);
+ assert_vm_map_objs(&m.vm_objs_unmapped,
+ second_objs_unmapped, 0, 1, false);
+ assert_vm_map_areas(&m.vm_areas, second_areas, 3, 3, true);
+ assert_vm_map_areas(&m.vm_areas_unmapped,
+ second_areas_unmapped, 0, 3, false);
+
+
+
+ //correct by removing entirety of `/bin/cat`
+ _init_vm_entry(&entry, 0x6000, 0x7000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL);
+ _init__traverse_state(&state, &m_a_n[0], m.vm_objs.head);
+
+ ret = _map_resync_area(&entry, &state, &m);
+ ck_assert_int_eq(ret, 1);
+
+ //check state
+ assert_vm_area(MC_GET_NODE_AREA(state.next_area_node),
+ NULL, NULL, 0x6000, 0x7000,
+ MC_ACCESS_READ | MC_ACCESS_WRITE,
+ NULL, m.vm_objs.head, 4, true);
+
+ assert_vm_obj(MC_GET_NODE_OBJ(m.vm_objs.head), "0x0", "0x0",
+ 0x0, 0x0, 0, 1, MC_ZERO_OBJ_ID, true);
+
+ assert_vm_map_objs(&m.vm_objs, third_objs, 1, 2, true);
+ assert_vm_map_objs(&m.vm_objs_unmapped,
+ third_objs_unmapped, 0, 2, false);
+ assert_vm_map_areas(&m.vm_areas, third_areas, 0, 2, true);
+ assert_vm_map_areas(&m.vm_areas_unmapped,
+ third_areas_unmapped, 0, 6, false);
+
+ return;
+
+} END_TEST
+
+
+//_map_add_obj() [stub map fixture]
+START_TEST(test__map_add_obj) {
+
+ //test data
+ struct obj_check first_objs[3] = { //start index: 3
+ {"foo", 0x8000, 0xB000},
+ {"bar", MC_UNDEF_ADDR, MC_UNDEF_ADDR},
+ {"[stack]", 0xE000, 0xF000}
+ };
+
+ struct obj_check second_objs[3] = { //start index: 1
+ {"[heap]", 0x4000, 0x5000},
+ {"dog", MC_UNDEF_ADDR, MC_UNDEF_ADDR},
+ {"foo", 0x8000, 0xB000}
+ };
+
+
+ //test vars
+ cm_lst_node * ret_node;
+
+ struct vm_entry entry;
+ _traverse_state state;
+
+
+ //first test: no forwarding last memory areas
+ _init_vm_entry(&entry, 0xD000, 0xE000, 0x0, MC_ACCESS_READ, "/lib/bar");
+ _init__traverse_state(&state, NULL, &m_o_n[2]);
+
+ ret_node = _map_add_obj(&entry, &state, &m);
+ ck_assert_ptr_nonnull(ret_node);
+
+ assert_vm_map_objs(&m.vm_objs, first_objs, 3, 3, true);
+ assert_lst_len(&MC_GET_NODE_OBJ(ret_node)->last_vm_area_node_ps, 0);
+
+
+ //second test: forwarding last memory areas
+ _init_vm_entry(&entry, 0x5000, 0x6000, 0x0, MC_ACCESS_READ, "/bin/dog");
+ _init__traverse_state(&state, NULL, &m_o_n[1]);
+
+ ret_node = _map_add_obj(&entry, &state, &m);
+ ck_assert_ptr_nonnull(ret_node);
+
+ assert_vm_map_objs(&m.vm_objs, second_objs, 2, 3, true);
+ assert_lst_len(&MC_GET_NODE_OBJ(ret_node)->last_vm_area_node_ps, 0);
+
+ return;
+
+} END_TEST
+
+
+//_map_add_area() [stub map fixture]
+START_TEST(test__map_add_area) {
+
+ //test data
+ struct area_check first_areas[3] = { //start_index: 7
+ {"foo", 0xA000, 0xB000},
+ {"foo", 0xB000, 0xC000},
+ {"", 0xC000, 0xD000}
+ };
+
+ struct obj_check first_objs[2] = { //start index: 3
+ {"foo", 0x8000, 0xC000},
+ {"[stack]", 0xE000, 0xF000}
+ };
+
+ struct area_check second_areas[3] = { //start_index: 4
+ {"", 0x6000, 0x7000},
+ {"", 0x7000, 0x8000},
+ {"foo", 0x8000, 0x9000}
+ };
+
+ struct obj_check second_objs[2] = { //start index: 2
+ {"[heap]", 0x4000, 0x5000},
+ {"foo", 0x8000, 0xC000},
+ };
+
+ struct area_check third_areas[3] = { //start_index: 3
+ {"[heap]", 0x4000, 0x5000},
+ {"bar", 0x5000, 0x6000},
+ {"", 0x6000, 0x7000}
+ };
+
+ struct obj_check third_objs[3] = { //start index: 2
+ {"[heap]", 0x4000, 0x5000},
+ {"bar", 0x5000, 0x6000},
+ {"foo", 0x8000, 0xC000}
+ };
+
+
+ //test vars
+ int ret;
+
+ struct vm_entry entry;
+ _traverse_state state;
+
+
+ //first test: add additional area to the end of `/lib/foo`
+ _init_vm_entry(&entry, 0xB000, 0xC000, 0x0, MC_ACCESS_READ, "/lib/foo");
+ _init__traverse_state(&state, &m_a_n[8], &m_o_n[2]);
+
+ ret = _map_add_area(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_map_areas(&m.vm_areas, first_areas, 7, 3, true);
+ assert_vm_map_objs(&m.vm_objs, first_objs, 3, 2, true);
+
+
+ //second test: add area without a backing object before `/lib/foo`
+ _init_vm_entry(&entry, 0x7000, 0x8000, 0x0, MC_ACCESS_READ, NULL);
+ _init__traverse_state(&state, &m_a_n[5], &m_o_n[1]);
+
+ ret = _map_add_area(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_map_areas(&m.vm_areas, second_areas, 4, 3, true);
+ assert_vm_map_objs(&m.vm_objs, second_objs, 2, 2, true);
+
+
+ //third test: add an area that creates a new object `/lib/bar`
+ _init_vm_entry(&entry, 0x5000, 0x6000, 0x0, MC_ACCESS_READ, "/lib/bar");
+ _init__traverse_state(&state, &m_a_n[4], &m_o_n[1]);
+
+ ret = _map_add_area(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_map_areas(&m.vm_areas, third_areas, 3, 3, true);
+ assert_vm_map_objs(&m.vm_objs, third_objs, 2, 3, true);
+
+ return;
+
+} END_TEST
+
+
+
+//map_send_entry() [stub map fixture]
+START_TEST(test_map_send_entry) {
+
+ //test data
+ struct area_check first_areas[2] = { //start_index: 9
+ {"[stack]", 0x0E000, 0x0F000},
+ {"dog", 0x0F000, 0x10000},
+ };
+
+ struct obj_check first_objs[2] = { //start index: 4
+ {"[stack]", 0x0E000, 0x0F000},
+ {"dog", 0x0F000, 0x10000}
+ };
+
+
+ struct area_check second_areas[3] = { //start_index: 9
+ {"[stack]", 0x0E000, 0x0F000},
+ {"dog", 0x0F000, 0x10000},
+ {"dog", 0x10000, 0x11000}
+ };
+
+ struct obj_check second_objs[2] = { //start index: 4
+ {"[stack]", 0x0E000, 0x0F000},
+ {"dog", 0x0F000, 0x11000}
+ };
+
+
+ struct area_check third_areas[2] = { //start_index: 0
+ {"[heap]", 0x04000, 0x05000},
+ {"", 0x06000, 0x07000}
+ };
+
+ struct obj_check third_objs[3] = { //start index: 0
+ {"0x0", 0x0, 0x0},
+ {"[heap]", 0x04000, 0x05000},
+ {"foo", 0x08000, 0x0B000}
+ };
+
+ struct area_check third_areas_unmapped[3] = { //start_index: 0
+ {"cat", 0x01000, 0x02000},
+ {"cat", 0x02000, 0x03000},
+ {"cat", 0x03000, 0x04000}
+ };
+
+ struct obj_check third_objs_unmapped[1] = { //start index: 0
+ {"cat", MC_UNDEF_ADDR, MC_UNDEF_ADDR},
+ };
+
+
+ struct area_check fourth_areas[3] = { //start_index: 0
+ {"[heap]", 0x04000, 0x05000},
+ {"", 0x06000, 0x07000},
+ {"foo", 0x08000, 0x09000},
+ };
+
+ struct obj_check fourth_objs[3] = { //start index: 0
+ {"0x0", 0x0, 0x0},
+ {"[heap]", 0x04000, 0x05000},
+ {"foo", 0x08000, 0x0B000}
+ };
+
+ struct area_check fourth_areas_unmapped[3] = { //start_index: 0
+ {"cat", 0x01000, 0x02000},
+ {"cat", 0x02000, 0x03000},
+ {"cat", 0x03000, 0x04000}
+ };
+
+ struct obj_check fourth_objs_unmapped[1] = { //start index: 0
+ {"cat", MC_UNDEF_ADDR, MC_UNDEF_ADDR},
+ };
+
+ int ret;
+
+ struct vm_entry entry;
+ _traverse_state state;
+
+
+ //first test: send a "/bin/dog" entry to the end of the map
+ _init_vm_entry(&entry, 0x0F000, 0x10000, 0x0, MC_ACCESS_READ, "/bin/dog");
+ _init__traverse_state(&state, NULL, m.vm_objs.head->prev);
+
+ ret = map_send_entry(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_map_areas(&m.vm_areas, first_areas, 9, 2, true);
+ assert_vm_map_objs(&m.vm_objs, first_objs, 4, 2, true);
+
+
+ //second test: send another "/bin/dog" area to the end of the map
+ _init_vm_entry(&entry, 0x10000, 0x11000, 0x0, MC_ACCESS_READ, "/bin/dog");
+ _init__traverse_state(&state, NULL, m.vm_objs.head->prev);
+
+ ret = map_send_entry(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_map_areas(&m.vm_areas, second_areas, 9, 3, true);
+ assert_vm_map_objs(&m.vm_objs, second_objs, 4, 2, true);
+
+
+ //third test: send a "[heap]" entry to the start of the map
+ _init_vm_entry(&entry, 0x04000, 0x05000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, "[heap]");
+ _init__traverse_state(&state, m.vm_areas.head, m.vm_objs.head);
+
+ ret = map_send_entry(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_map_areas(&m.vm_areas, third_areas, 0, 2, true);
+ assert_vm_map_areas(&m.vm_areas_unmapped,
+ third_areas_unmapped, 0, 3, false);
+ assert_vm_map_objs(&m.vm_objs, third_objs, 0, 3, true);
+ assert_vm_map_objs(&m.vm_objs_unmapped,
+ third_objs_unmapped, 0, 1, false);
+
+
+ //fourth test: send a correct entry after the "[heap]" entry
+ _init_vm_entry(&entry, 0x06000, 0x07000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, NULL);
+ _init__traverse_state(&state, m.vm_areas.head->next, m.vm_objs.head->next);
+
+ ret = map_send_entry(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_vm_map_areas(&m.vm_areas, fourth_areas, 0, 3, true);
+ assert_vm_map_areas(&m.vm_areas_unmapped,
+ fourth_areas_unmapped, 0, 3, false);
+ assert_vm_map_objs(&m.vm_objs, fourth_objs, 0, 3, true);
+ assert_vm_map_objs(&m.vm_objs_unmapped,
+ fourth_objs_unmapped, 0, 1, false);
+
+ return;
+
+} END_TEST
+
+
+//map_init_traverse_state() [empty map fixture]
+START_TEST(test_map_init_traverse_state) {
+
+ struct vm_entry entry;
+ _traverse_state state;
+
+
+ //first test: empty map
+ map_init_traverse_state(&state, &m);
+ ck_assert_ptr_null(state.next_area_node);
+ ck_assert_ptr_eq(state.prev_obj_node, m.vm_objs.head);
+
+
+ //add an area
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x0, MC_ACCESS_READ, "/bin/cat");
+ map_send_entry(&entry, &state, &m);
+
+
+ //second test: existing map
+ map_init_traverse_state(&state, &m);
+ ck_assert_ptr_eq(state.next_area_node, m.vm_areas.head);
+ ck_assert_ptr_eq(state.prev_obj_node, m.vm_objs.head);
+
+ return;
+
+} END_TEST
+
+
+//mc_map_clean_unmapped() [stub map fixture]
+START_TEST(test_mc_map_clean_unmapped) {
+
+ int ret;
+
+ struct vm_entry entry;
+ _traverse_state state;
+
+
+ //first test: add /bin/dog and unmap it
+ _init_vm_entry(&entry, 0x0000, 0x1000, 0x0,
+ MC_ACCESS_READ | MC_ACCESS_WRITE, "/bin/dog");
+ _init__traverse_state(&state, m.vm_areas.head, m.vm_objs.head);
+ ret = map_send_entry(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ _init_vm_entry(&entry, 0x1000, 0x2000, 0x0,
+ MC_ACCESS_READ, "/bin/cat");
+ _init__traverse_state(&state, m.vm_areas.head, m.vm_objs.head->next);
+ ret = map_send_entry(&entry, &state, &m);
+ ck_assert_int_eq(ret, 0);
+
+ ret = mc_map_clean_unmapped(&m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_lst_len(&m.vm_areas_unmapped, 0);
+ assert_lst_len(&m.vm_objs_unmapped, 0);
+
+
+ //second test: try cleaning unmapped areas when none are present
+ ret = mc_map_clean_unmapped(&m);
+ ck_assert_int_eq(ret, 0);
+
+ return;
+
+} END_TEST
+#endif
+
+
+/*
+ * --- [SUITE] ---
+ */
+
+ Suite * map_suite() {
+
+ //test cases
+ TCase * tc_new_del_vm_map;
+
+ #ifdef DEBUG
+ TCase * tc__new_del_vm_obj;
+ TCase * tc__make_zero_obj;
+ TCase * tc__init_vm_area;
+ TCase * tc__obj_add_area;
+ TCase * tc__obj_add_last_area;
+ TCase * tc__obj_rmv_area;
+ TCase * tc__obj_rmv_last_area;
+ TCase * tc__is_pathname_in_obj;
+ TCase * tc__find_obj_for_area;
+ TCase * tc__backtrack_unmapped_obj_last_vm_areas;
+ TCase * tc__forward_unmapped_obj_last_vm_areas;
+ TCase * tc__unlink_unmapped_obj;
+ TCase * tc__unlink_unmapped_area;
+ TCase * tc__check_area_eql;
+ TCase * tc__state_inc_area;
+ TCase * tc__state_inc_obj;
+ TCase * tc__resync_area;
+ TCase * tc__add_obj;
+ TCase * tc__add_area;
+ TCase * tc_send_entry;
+ TCase * tc_init_traverse_state;
+ TCase * tc_clean_unmapped;
+ #endif
+
+ Suite * s = suite_create("map");
+
+
+ //tc_new_del_vm_map
+ tc_new_del_vm_map = tcase_create("new_del_vm_map");
+ tcase_add_test(tc_new_del_vm_map, test_mc_new_del_vm_map);
+
+ #ifdef DEBUG
+ //tc__new_del_vm_obj
+ tc__new_del_vm_obj = tcase_create("_new_del_vm_obj");
+ tcase_add_checked_fixture(tc__new_del_vm_obj,
+ _setup_empty_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__new_del_vm_obj, test__map_new_del_vm_obj);
+
+ //tc__make_zero_obj
+ tc__make_zero_obj = tcase_create("_make_zero_obj");
+ tcase_add_checked_fixture(tc__make_zero_obj,
+ _setup_empty_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__make_zero_obj, test__map_make_zero_obj);
+
+ //tc__init_vm_area
+ tc__init_vm_area = tcase_create("_init_vm_area");
+ tcase_add_checked_fixture(tc__init_vm_area,
+ _setup_empty_vm_obj, _teardown_vm_obj);
+ tcase_add_test(tc__init_vm_area, test__map_init_vm_area);
+
+ //tc__obj_add_area
+ tc__obj_add_area = tcase_create("_obj_add_area");
+ tcase_add_checked_fixture(tc__obj_add_area,
+ _setup_empty_vm_obj, _teardown_vm_obj);
+ tcase_add_test(tc__obj_add_area, test__map_obj_add_area);
+
+ //tc__obj_add_last_area
+ tc__obj_add_last_area = tcase_create("_obj_add_last_area");
+ tcase_add_checked_fixture(tc__obj_add_last_area,
+ _setup_empty_vm_obj, _teardown_vm_obj);
+ tcase_add_test(tc__obj_add_last_area, test__map_obj_add_last_area);
+
+ //tc__obj_rmv_area
+ tc__obj_rmv_area = tcase_create("_obj_rmv_area");
+ tcase_add_checked_fixture(tc__obj_rmv_area,
+ _setup_stub_vm_obj, _teardown_vm_obj);
+ tcase_add_test(tc__obj_rmv_area, test__map_obj_rmv_area);
+
+ //tc__obj_rmv_last_area
+ tc__obj_rmv_last_area = tcase_create("_obj_rmv_last_area");
+ tcase_add_checked_fixture(tc__obj_rmv_last_area,
+ _setup_stub_vm_obj, _teardown_vm_obj);
+ tcase_add_test(tc__obj_rmv_last_area, test__map_obj_rmv_last_area);
+
+ //tc__is_pathname_in_obj
+ tc__is_pathname_in_obj = tcase_create("_is_pathname_in_obj");
+ tcase_add_checked_fixture(tc__is_pathname_in_obj,
+ _setup_empty_vm_obj, _teardown_vm_obj);
+ tcase_add_test(tc__is_pathname_in_obj, test__map_is_pathname_in_obj);
+
+ //tc__find_obj_for_area
+ tc__find_obj_for_area = tcase_create("_find_obj_for_area");
+ tcase_add_checked_fixture(tc__find_obj_for_area,
+ _setup_empty_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__find_obj_for_area, test__map_find_obj_for_area);
+
+ //tc__backtrack_unmapped_obj_last_vm_areas
+ tc__backtrack_unmapped_obj_last_vm_areas
+ = tcase_create("_backtrack_unmapped_obj_last_vm_areas");
+ tcase_add_checked_fixture(tc__backtrack_unmapped_obj_last_vm_areas,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__backtrack_unmapped_obj_last_vm_areas,
+ test__map_backtrack_unmapped_obj_last_vm_areas);
+
+ //tc__forward_unmapped_obj_last_vm_areas
+ tc__forward_unmapped_obj_last_vm_areas
+ = tcase_create("_forward_unmapped_obj_last_vm_areas");
+ tcase_add_checked_fixture(tc__forward_unmapped_obj_last_vm_areas,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__forward_unmapped_obj_last_vm_areas,
+ test__map_forward_unmapped_obj_last_vm_areas);
+
+ //tc__unlink_unmapped_obj
+ tc__unlink_unmapped_obj = tcase_create("_unlink_unmapped_obj");
+ tcase_add_checked_fixture(tc__unlink_unmapped_obj,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__unlink_unmapped_obj, test__map_unlink_unmapped_obj);
+
+ //tc__unlink_unmapped_area
+ tc__unlink_unmapped_area = tcase_create("_unlink_unmapped_area");
+ tcase_add_checked_fixture(tc__unlink_unmapped_area,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__unlink_unmapped_area, test__map_unlink_unmapped_area);
+
+ //tc__check_area_eql
+ tc__check_area_eql = tcase_create("_check_area_eql");
+ tcase_add_checked_fixture(tc__check_area_eql,
+ _setup_empty_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__check_area_eql, test__map_check_area_eql);
+
+ //tc__state_inc_area
+ tc__state_inc_area = tcase_create("_state_inc_area");
+ tcase_add_checked_fixture(tc__state_inc_area,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__state_inc_area, test__map_state_inc_area);
+
+ //tc__state_inc_obj
+ tc__state_inc_obj = tcase_create("_state_inc_obj");
+ tcase_add_checked_fixture(tc__state_inc_obj,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__state_inc_obj, test__map_state_inc_obj);
+
+ //tc__resync_area
+ tc__resync_area = tcase_create("_resync_area");
+ tcase_add_checked_fixture(tc__resync_area,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__resync_area, test__map_resync_area);
+
+ //tc__add_obj
+ tc__add_obj = tcase_create("_add_obj");
+ tcase_add_checked_fixture(tc__add_obj,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__add_obj, test__map_add_obj);
+
+ //tc__add_area
+ tc__add_area = tcase_create("_add_area");
+ tcase_add_checked_fixture(tc__add_area,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc__add_area, test__map_add_area);
+
+ //tc_send_entry
+ tc_send_entry = tcase_create("send_entry");
+ tcase_add_checked_fixture(tc_send_entry,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc_send_entry, test_map_send_entry);
+
+ //tc_init_traverse_state
+ tc_init_traverse_state = tcase_create("init_traverse_state");
+ tcase_add_checked_fixture(tc_init_traverse_state,
+ _setup_empty_vm_map, _teardown_vm_map);
+ tcase_add_test(tc_init_traverse_state, test_map_init_traverse_state);
+
+ //tc_clean_unmapped
+ tc_clean_unmapped = tcase_create("clean_unmapped");
+ tcase_add_checked_fixture(tc_clean_unmapped,
+ _setup_stub_vm_map, _teardown_vm_map);
+ tcase_add_test(tc_clean_unmapped, test_mc_map_clean_unmapped);
+ #endif
+
+
+ //add test cases to map test suite
+ suite_add_tcase(s, tc_new_del_vm_map);
+
+ #ifdef DEBUG
+ suite_add_tcase(s, tc__new_del_vm_obj);
+ suite_add_tcase(s, tc__make_zero_obj);
+ suite_add_tcase(s, tc__init_vm_area);
+ suite_add_tcase(s, tc__obj_add_area);
+ suite_add_tcase(s, tc__obj_add_last_area);
+ suite_add_tcase(s, tc__obj_rmv_area);
+ suite_add_tcase(s, tc__obj_rmv_last_area);
+ suite_add_tcase(s, tc__is_pathname_in_obj);
+ suite_add_tcase(s, tc__find_obj_for_area);
+ suite_add_tcase(s, tc__backtrack_unmapped_obj_last_vm_areas);
+ suite_add_tcase(s, tc__forward_unmapped_obj_last_vm_areas);
+ suite_add_tcase(s, tc__unlink_unmapped_obj);
+ suite_add_tcase(s, tc__unlink_unmapped_area);
+ suite_add_tcase(s, tc__check_area_eql);
+ suite_add_tcase(s, tc__state_inc_area);
+ suite_add_tcase(s, tc__state_inc_obj);
+ suite_add_tcase(s, tc__resync_area);
+ suite_add_tcase(s, tc__add_obj);
+ suite_add_tcase(s, tc__add_area);
+ suite_add_tcase(s, tc_send_entry);
+ suite_add_tcase(s, tc_init_traverse_state);
+ suite_add_tcase(s, tc_clean_unmapped);
+ #endif
+
+ return s;
+ }
diff --git a/src/test/check_map_util.c b/src/test/check_map_util.c
new file mode 100644
index 0000000..1bcf653
--- /dev/null
+++ b/src/test/check_map_util.c
@@ -0,0 +1,432 @@
+//standard library
+#include
+#include
+
+//system headers
+#include
+
+//external libraries
+#include
+#include
+
+//local headers
+#include "suites.h"
+#include "target_helper.h"
+
+//test target headers
+#include "../lib/memcry.h"
+
+
+
+/*
+ * [BASIC TEST]
+ */
+
+
+//globals
+static mc_vm_map m;
+static mc_session s;
+static pid_t pid;
+
+
+
+/*
+ * --- [FIXTURES] ---
+ */
+
+//initialise the target
+static void _setup_target() {
+
+ int ret;
+
+
+ pid = start_target();
+ ck_assert_int_ne(pid, -1);
+
+ ret = mc_open(&s, PROCFS, pid);
+ ck_assert_int_eq(ret, 0);
+
+ mc_new_vm_map(&m);
+ ret = mc_update_map(&s, &m);
+ ck_assert_int_eq(ret, 0);
+
+ return;
+}
+
+
+//teardown the target
+static void _teardown_target() {
+
+ int ret;
+
+
+ ret = mc_del_vm_map(&m);
+ ck_assert_int_eq(ret, 0);
+
+ ret = mc_close(&s);
+ ck_assert_int_eq(ret, 0);
+
+ end_target(pid);
+
+ return;
+}
+
+
+
+/*
+ * --- [UNIT TESTS] ---
+ */
+
+//mc_get_area_off() [target fixture]
+START_TEST(test_mc_get_area_off) {
+
+ off_t off;
+
+ mc_vm_obj * o;
+ cm_lst_node * a_n;
+
+
+ //first test: typical offset
+ o = MC_GET_NODE_OBJ(m.vm_objs.head);
+ a_n = MC_GET_NODE_PTR(o->last_vm_area_node_ps.head);
+
+ off = mc_get_area_off(a_n, 0x10800);
+ ck_assert_int_eq(off, 0x800);
+
+
+ //second test: address is lower than area's starting address
+ off = mc_get_area_off(a_n, 0xF800);
+ ck_assert_int_eq(off, -0x800);
+
+ return;
+
+} END_TEST
+
+
+//mc_get_obj_off() [target fixture]
+START_TEST(test_mc_get_obj_off) {
+
+ off_t off;
+
+ cm_lst_node * o_n;
+ mc_vm_obj * o;
+
+
+ //first test: typical offset
+ o_n = m.vm_objs.head;
+ o = MC_GET_NODE_OBJ(o_n);
+
+ off = mc_get_obj_off(o_n, 0x800);
+ ck_assert_int_eq(off, 0x800);
+
+
+ //second test: address is lower than obj's starting address
+ o->end_addr = o->start_addr = 0x1000;
+ off = mc_get_obj_off(o_n, 0x800);
+ ck_assert_int_eq(off, -0x800);
+
+ return;
+
+} END_TEST
+
+
+//mc_get_area_off_bnd() [target fixture]
+START_TEST(test_mc_get_area_off_bnd) {
+
+ off_t off;
+
+ mc_vm_obj * o;
+ cm_lst_node * a_n;
+
+
+ //setup test
+ o = MC_GET_NODE_OBJ(m.vm_objs.head);
+ a_n = MC_GET_NODE_PTR(o->last_vm_area_node_ps.head);
+
+
+ //first test: typical offset
+ off = mc_get_area_off_bnd(a_n, 0x10800);
+ ck_assert_int_eq(off, 0x800);
+
+
+ //second test: address is lower than area's starting address
+ off = mc_get_area_off_bnd(a_n, 0xF800);
+ ck_assert_int_eq(off, -1);
+
+ return;
+
+} END_TEST
+
+
+//mc_get_obj_off_bnd() [target fixture]
+START_TEST(test_mc_get_obj_off_bnd) {
+
+ off_t off;
+
+ cm_lst_node * o_n;
+ mc_vm_obj * o;
+
+
+ //setup test
+ o_n = m.vm_objs.head;
+ o = MC_GET_NODE_OBJ(o_n);
+
+
+ //first test: typical offset
+ o->end_addr = 0x1000;
+ off = mc_get_obj_off_bnd(o_n, 0x800);
+ ck_assert_int_eq(off, 0x800);
+
+
+ //second test: address is lower than obj's starting address
+ o->start_addr = 0x1000;
+ off = mc_get_obj_off_bnd(o_n, 0x800);
+ ck_assert_int_eq(off, -1);
+
+ return;
+
+} END_TEST
+
+
+//mc_get_area_by_addr [target fixture]
+START_TEST(test_mc_get_area_by_addr) {
+
+ cm_lst_node * ret_n;
+ off_t off;
+
+ mc_vm_obj * o;
+ mc_vm_area * a;
+ cm_lst_node * a_n;
+
+
+ //first test: mapped address
+ a_n = m.vm_areas.head->next->next;
+ a = MC_GET_NODE_AREA(a_n);
+
+ ret_n = mc_get_area_by_addr(&m, a->start_addr + 0x200, &off);
+ ck_assert_ptr_eq(ret_n, a_n);
+ ck_assert_int_eq(off, 0x200);
+
+
+ //second test: unmapped address
+ off = 0x0;
+
+ ret_n = mc_get_area_by_addr(&m, 0x1337, &off);
+ ck_assert_ptr_null(ret_n);
+ ck_assert_int_eq(off, 0);
+
+ return;
+
+} END_TEST
+
+
+//mc_get_obj_by_addr [target fixture]
+START_TEST(test_mc_get_obj_by_addr) {
+
+ cm_lst_node * ret_n;
+ off_t off;
+
+ mc_vm_obj * o;
+ cm_lst_node * o_n;
+
+
+ //first test: mapped address
+ o_n = m.vm_objs.head->next->next;
+ o = MC_GET_NODE_OBJ(o_n);
+
+ ret_n = mc_get_obj_by_addr(&m, o->start_addr + 0x200, &off);
+ ck_assert_ptr_eq(ret_n, o_n);
+ ck_assert_int_eq(off, 0x200);
+
+
+ //second test: unmapped address
+ off = 0x0;
+
+ ret_n = mc_get_obj_by_addr(&m, 0x1337, &off);
+ ck_assert_ptr_null(ret_n);
+ ck_assert_int_eq(off, 0);
+
+ return;
+
+} END_TEST
+
+
+//mc_get_obj_by_pathname() [target fixture]
+START_TEST(test_mc_get_obj_by_pathname) {
+
+ cm_lst_node * ret_n;
+
+ mc_vm_obj * o;
+ cm_lst_node * o_n;
+ char * pathname;
+
+
+ //first test: pathname exists
+ o_n = m.vm_objs.head->next->next;
+ o = MC_GET_NODE_OBJ(o_n);
+
+ pathname = o->pathname;
+ ret_n = mc_get_obj_by_pathname(&m, pathname);
+ ck_assert_ptr_eq(ret_n, o_n);
+
+
+ //second test: pathname doesn't exist
+ pathname = "/foo/bar";
+ ret_n = mc_get_obj_by_pathname(&m, pathname);
+ ck_assert_ptr_null(ret_n);
+
+ return;
+
+} END_TEST
+
+
+//mc_get_obj_by_basename() [target_fixture]
+START_TEST(test_mc_get_obj_by_basename) {
+
+ cm_lst_node * ret_n;
+
+ mc_vm_obj * o;
+ cm_lst_node * o_n;
+ char * basename;
+
+
+ //first test: pathname exists
+ o_n = m.vm_objs.head->next->next;
+ o = MC_GET_NODE_OBJ(o_n);
+
+ basename = o->basename;
+ ret_n = mc_get_obj_by_basename(&m, basename);
+ ck_assert_ptr_eq(ret_n, o_n);
+
+
+ //second test: pathname doesn't exist
+ basename = "1337";
+ ret_n = mc_get_obj_by_basename(&m, basename);
+ ck_assert_ptr_null(ret_n);
+
+ return;
+
+} END_TEST
+
+
+//mc_access_to_str() [no fixture]
+START_TEST(test_mc_access_to_str) {
+
+ cm_byte accesses[16] = {
+ 0b0000, 0b0001, 0b0010, 0b0011,
+ 0b0100, 0b0101, 0b0110, 0b0111,
+ 0b1000, 0b1001, 0b1010, 0b1011,
+ 0b1100, 0b1101, 0b1110, 0b1111
+ };
+
+ char * access_strings[16] = {
+ "---p", "r--p", "-w-p", "rw-p",
+ "--xp", "r-xp", "-wxp", "rwxp",
+ "---s", "r--s", "-w-s", "rw-s",
+ "--xs", "r-xs", "-wxs", "rwxs",
+ };
+
+ char str_buf[5];
+
+
+ //only test: try every access combination
+ for (int i = 0; i < 16; ++i) {
+
+ mc_access_to_str(accesses[i], str_buf);
+ ck_assert_str_eq(str_buf, access_strings[i]);
+ }
+
+ return;
+
+} END_TEST
+
+
+
+/*
+ * --- [SUITE] ---
+ */
+
+Suite * map_util_suite() {
+
+ //test cases
+ TCase * tc_get_area_off;
+ TCase * tc_get_obj_off;
+ TCase * tc_get_area_off_bnd;
+ TCase * tc_get_obj_off_bnd;
+ TCase * tc_get_area_by_addr;
+ TCase * tc_get_obj_by_addr;
+ TCase * tc_get_obj_by_pathname;
+ TCase * tc_get_obj_by_basename;
+ TCase * tc_access_to_str;
+
+ Suite * s = suite_create("map_util");
+
+
+ //tc_get_area_off
+ tc_get_area_off = tcase_create("get_area_off");
+ tcase_add_checked_fixture(tc_get_area_off,
+ _setup_target, _teardown_target);
+ tcase_add_test(tc_get_area_off, test_mc_get_area_off);
+
+ //tc_get_obj_off
+ tc_get_obj_off = tcase_create("get_obj_off");
+ tcase_add_checked_fixture(tc_get_obj_off,
+ _setup_target, _teardown_target);
+ tcase_add_test(tc_get_obj_off, test_mc_get_obj_off);
+
+ //tc_get_area_off_bnd
+ tc_get_area_off_bnd = tcase_create("get_area_off_bnd");
+ tcase_add_checked_fixture(tc_get_area_off_bnd,
+ _setup_target, _teardown_target);
+ tcase_add_test(tc_get_area_off_bnd, test_mc_get_area_off_bnd);
+
+ //tc_get_obj_off_bnd
+ tc_get_obj_off_bnd = tcase_create("get_obj_off_bnd");
+ tcase_add_checked_fixture(tc_get_obj_off_bnd,
+ _setup_target, _teardown_target);
+ tcase_add_test(tc_get_obj_off_bnd, test_mc_get_obj_off_bnd);
+
+ //tc_get_area_by_addr
+ tc_get_area_by_addr = tcase_create("get_area_by_addr");
+ tcase_add_checked_fixture(tc_get_area_by_addr,
+ _setup_target, _teardown_target);
+ tcase_add_test(tc_get_area_by_addr, test_mc_get_area_by_addr);
+
+ //tc_get_area_by_addr
+ tc_get_obj_by_addr = tcase_create("get_obj_by_addr");
+ tcase_add_checked_fixture(tc_get_obj_by_addr,
+ _setup_target, _teardown_target);
+ tcase_add_test(tc_get_obj_by_addr, test_mc_get_obj_by_addr);
+
+ //tc_get_obj_by_pathname
+ tc_get_obj_by_pathname = tcase_create("get_obj_by_pathname");
+ tcase_add_checked_fixture(tc_get_obj_by_pathname,
+ _setup_target, _teardown_target);
+ tcase_add_test(tc_get_obj_by_pathname,
+ test_mc_get_obj_by_pathname);
+
+ //tc_get_obj_by_basename
+ tc_get_obj_by_basename = tcase_create("get_obj_by_basename");
+ tcase_add_checked_fixture(tc_get_obj_by_basename,
+ _setup_target, _teardown_target);
+ tcase_add_test(tc_get_obj_by_basename,
+ test_mc_get_obj_by_basename);
+
+ //tc_access_to_str
+ tc_access_to_str = tcase_create("access_to_str");
+ tcase_add_test(tc_access_to_str, test_mc_access_to_str);
+
+
+ //add test cases to map util test suite
+ suite_add_tcase(s, tc_get_area_off);
+ suite_add_tcase(s, tc_get_obj_off);
+ suite_add_tcase(s, tc_get_area_off_bnd);
+ suite_add_tcase(s, tc_get_obj_off_bnd);
+ suite_add_tcase(s, tc_get_area_by_addr);
+ suite_add_tcase(s, tc_get_obj_by_addr);
+ suite_add_tcase(s, tc_get_obj_by_pathname);
+ suite_add_tcase(s, tc_get_obj_by_basename);
+ suite_add_tcase(s, tc_access_to_str);
+
+ return s;
+}
diff --git a/src/test/check_procfs_iface.c b/src/test/check_procfs_iface.c
new file mode 100644
index 0000000..06f04eb
--- /dev/null
+++ b/src/test/check_procfs_iface.c
@@ -0,0 +1,110 @@
+//standard library
+#include
+#include
+
+//system headers
+#include
+
+//external libraries
+#include
+#include
+
+//local headers
+#include "suites.h"
+#include "iface_helper.h"
+
+//test target headers
+#include "../lib/memcry.h"
+
+
+
+/*
+ * [BASIC TEST]
+ */
+
+/*
+ * NOTE: Unit tests for interfaces are standardised through the iface helper.
+ */
+
+
+
+/*
+ * --- [HELPERS] ---
+ */
+
+static void _assert_session(mc_session * se, pid_t pid) {
+
+ ck_assert_int_ne(se->fd_mem, -1);
+ ck_assert_int_eq(se->pid, pid);
+
+ return;
+}
+
+
+
+/*
+ * --- [UNIT TESTS] ---
+ */
+
+//procfs_open() & procfs_close() [no fixture]
+START_TEST(test_procfs_mc_open_close) {
+
+ assert_iface_open_close(PROCFS, _assert_session);
+ return;
+
+} END_TEST
+
+
+//procfs_update_map() [no fixture]
+START_TEST(test_procfs_mc_update_map) {
+
+ assert_iface_update_map(PROCFS);
+ return;
+
+} END_TEST
+
+
+//procfs_read() & procfs_write() [no fixture]
+START_TEST(test_procfs_mc_read_write) {
+
+ assert_iface_read_write(PROCFS);
+ return;
+
+} END_TEST
+
+
+
+/*
+ * --- [SUITE] ---
+ */
+
+Suite * procfs_iface_suite() {
+
+ //test cases
+ TCase * tc_procfs_mc_open_close;
+ TCase * tc_procfs_mc_update_map;
+ TCase * tc_procfs_mc_read_write;
+
+ Suite * s = suite_create("procfs_iface");
+
+
+ //tc_procfs_mc_open_close
+ tc_procfs_mc_open_close = tcase_create("procfs_mc_open_close");
+ tcase_add_test(tc_procfs_mc_open_close, test_procfs_mc_open_close);
+
+ //tc_procfs_mc_update_map
+ tc_procfs_mc_update_map = tcase_create("procfs_mc_update_map");
+ tcase_add_test(tc_procfs_mc_update_map, test_procfs_mc_update_map);
+
+ //tc_procfs_mc_read_write
+ tc_procfs_mc_read_write = tcase_create("procfs_mc_read_write");
+ tcase_add_test(tc_procfs_mc_read_write, test_procfs_mc_read_write);
+
+
+ //add test cases to procfs interface test suite
+ suite_add_tcase(s, tc_procfs_mc_open_close);
+ suite_add_tcase(s, tc_procfs_mc_update_map);
+ suite_add_tcase(s, tc_procfs_mc_read_write);
+
+ return s;
+}
diff --git a/src/test/check_util.c b/src/test/check_util.c
new file mode 100644
index 0000000..e1f01aa
--- /dev/null
+++ b/src/test/check_util.c
@@ -0,0 +1,233 @@
+//standard library
+#include
+#include
+#include
+
+//system headers
+#include
+
+#include
+
+//external libraries
+#include
+#include
+
+//local headers
+#include "suites.h"
+#include "target_helper.h"
+
+//test target headers
+#include "../lib/memcry.h"
+
+
+
+/*
+ * [BASIC TEST]
+ */
+
+
+
+//globals
+static mc_session s;
+static pid_t pid;
+
+
+
+/*
+ * --- [FIXTURES] ---
+ */
+
+//initialise the target
+static void _setup_target() {
+
+ int ret;
+
+ ret = clean_targets();
+ ck_assert_int_eq(ret, 0);
+
+ pid = start_target();
+ ck_assert_int_ne(pid, -1);
+
+ ret = mc_open(&s, PROCFS, pid);
+ ck_assert_int_eq(ret, 0);
+
+ return;
+}
+
+
+//teardown the target
+static void _teardown_target() {
+
+ int ret;
+
+
+ ret = mc_close(&s);
+ ck_assert_int_eq(ret, 0);
+
+ end_target(pid);
+
+ return;
+}
+
+
+
+/*
+ * --- [UNIT TESTS] ---
+ */
+
+//mc_pathname_to_basename() [no fixture]
+START_TEST(test_mc_pathname_to_basename) {
+
+ char * pathname, * basename;
+
+
+ //first test: pathname is a path
+ pathname = "/foo/bar";
+ basename = mc_pathname_to_basename(pathname);
+
+ ck_assert_str_eq(basename, "bar");
+
+
+ //second test: basename is not a path
+ pathname = "baz";
+ basename = mc_pathname_to_basename(pathname);
+
+ ck_assert_str_eq(basename, "baz");
+
+ return;
+
+} END_TEST
+
+
+//mc_pid_by_name() [target fixture]
+START_TEST(test_mc_pid_by_name) {
+
+ pid_t pid;
+ cm_vct v;
+
+
+ //first test: target exists, provide vector
+ pid = mc_pid_by_name(TARGET_NAME, &v);
+
+ ck_assert_int_ne(pid, -1);
+ ck_assert_int_eq(v.len, 1);
+
+ cm_del_vct(&v);
+
+
+ //second test: target exists, do not provide vector
+ pid = mc_pid_by_name(TARGET_NAME, NULL);
+
+ ck_assert_int_ne(pid, -1);
+
+
+ //second test: target does not exist
+ pid = mc_pid_by_name("foo", &v);
+
+ ck_assert_int_eq(pid, -1);
+ ck_assert_int_eq(v.len, 0);
+
+ cm_del_vct(&v);
+
+ return;
+
+} END_TEST
+
+
+//mc_name_by_pid() [no fixture]
+START_TEST(test_mc_name_by_pid) {
+
+ int ret;
+
+ pid_t pid;
+ char name_buf[NAME_MAX];
+
+
+ //setup test
+ pid = start_target();
+ ck_assert_int_ne(pid, -1);
+
+
+ //first test: target exists
+ ret = mc_name_by_pid(pid, name_buf);
+ ck_assert_int_eq(ret, 0);
+ ck_assert_str_eq(name_buf, TARGET_NAME);
+
+
+ //second test: target does not exist
+ memset(name_buf, 0, NAME_MAX);
+ ret = mc_name_by_pid((int) (pow(2, 32)) - 1, name_buf);
+ ck_assert_int_eq(ret, -1);
+ ck_assert_str_eq(name_buf, "\0");
+
+
+ //cleanup
+ end_target(pid);
+
+ return;
+
+} END_TEST
+
+
+//mc_bytes_to_hex() [no fixture]
+START_TEST(test_mc_bytes_to_hex) {
+
+ int ret;
+
+
+ //only test: convert ascii "foo" to hex representation
+ cm_byte str[4] = "foo";
+ char hex_str[8] = {0};
+
+ mc_bytes_to_hex(str, 3, hex_str);
+ ret = memcmp(hex_str, "666f6f", 6);
+ ck_assert_int_eq(ret, 0);
+
+ return;
+
+} END_TEST
+
+
+
+/*
+ * --- [SUITE] ---
+ */
+
+Suite * util_suite() {
+
+ //test cases
+ TCase * tc_pathname_to_basename;
+ TCase * tc_pid_by_name;
+ TCase * tc_name_by_pid;
+ TCase * tc_bytes_to_hex;
+
+ Suite * s = suite_create("util");
+
+
+ //tc_pathname_to_basename
+ tc_pathname_to_basename = tcase_create("pathname_to_basename");
+ tcase_add_test(tc_pathname_to_basename, test_mc_pathname_to_basename);
+
+ //tc_pid_by_name
+ tc_pid_by_name = tcase_create("pid_by_name");
+ tcase_add_checked_fixture(tc_pid_by_name, _setup_target, _teardown_target);
+ tcase_add_test(tc_pid_by_name, test_mc_pid_by_name);
+
+ //tc_name_by_pid
+ tc_name_by_pid = tcase_create("name_by_pid");
+ tcase_add_checked_fixture(tc_name_by_pid, _setup_target, _teardown_target);
+ tcase_add_test(tc_name_by_pid, test_mc_name_by_pid);
+
+ //tc_bytes_to_hex
+ tc_bytes_to_hex = tcase_create("bytes_to_hex");
+ tcase_add_test(tc_bytes_to_hex, test_mc_bytes_to_hex);
+
+
+ //add test cases to util test suite
+ suite_add_tcase(s, tc_pathname_to_basename);
+ suite_add_tcase(s, tc_pid_by_name);
+ suite_add_tcase(s, tc_name_by_pid);
+ suite_add_tcase(s, tc_bytes_to_hex);
+
+ return s;
+}
diff --git a/src/test/iface.c b/src/test/iface.c
deleted file mode 100644
index 2dd7851..0000000
--- a/src/test/iface.c
+++ /dev/null
@@ -1,52 +0,0 @@
-#include
-#include
-#include
-
-#include
-
-#include
-
-#include "../lib/liblain.h"
-#include "test.h"
-
-
-
-//run test for an interface specified by _iface_
-void test_iface(pid_t pid, int iface, ln_vm_map * vm_map, uintptr_t test_addr) {
-
- printf("\n\n --- [IFACE] --- \n\n");
- printf("notice: use a debugger\n");
-
- int ret;
- size_t bytes;
-
- ln_session session;
- cm_byte buf[0x2000];
-
- //prepare write buffer
- memset(buf, (int) 'c', 0x2000);
-
- //open the map
- ret = ln_open(&session, iface, pid);
-
- //initialise the map
- ret = ln_update_map(&session, vm_map);
-
- //wait for target to change
- printf("press enter when target's map has changed: ");
- char cont[8];
- fgets(cont, 8, stdin);
- fgets(cont, 8, stdin);
-
- //update the map
- ret = ln_update_map(&session, vm_map);
-
- //read & write
- //bytes = ln_write(&session, test_addr, buf, 0x2000);
- //bytes = ln_read(&session, test_addr, buf, 0x2000);
-
- //close session
- ret = ln_close(&session);
-
- return;
-}
diff --git a/src/test/iface_helper.c b/src/test/iface_helper.c
new file mode 100644
index 0000000..1b21a5a
--- /dev/null
+++ b/src/test/iface_helper.c
@@ -0,0 +1,289 @@
+//standard library
+#include
+#include
+#include
+
+//system headers
+#include
+
+//external libraries
+#include
+#include
+
+//local headers
+#include "info.h"
+#include "iface_helper.h"
+#include "target_helper.h"
+
+//test target headers
+#include "../lib/memcry.h"
+#include "../lib/procfs_iface.h"
+
+
+
+/*
+ * NOTE: Interface helper is a generic interface tester. It requires the
+ * test suite of an interface to implement a custom `assert_session()`
+ * function that verifies the validity of an open session.
+ */
+
+
+//get address for testing a read / write in a PIE process
+static inline uintptr_t _get_addr(mc_vm_map * m, bool is_last_area,
+ int obj_index, int area_index, off_t off) {
+
+ int ret;
+
+ mc_vm_area * a;
+ cm_lst_node * a_p;
+ mc_vm_obj * o;
+ cm_lst_node * o_p;
+
+
+ //get relevant object
+ o_p = cm_lst_get_n(&m->vm_objs, obj_index);
+ ck_assert_ptr_nonnull(o_p);
+ o = MC_GET_NODE_OBJ(o_p);
+
+ //get relevant area
+ if (is_last_area == true) {
+ ret = cm_lst_get(&o->last_vm_area_node_ps, area_index, &a_p);
+ } else {
+ ret = cm_lst_get(&o->vm_area_node_ps, area_index, &a_p);
+ }
+ ck_assert_int_eq(ret, 0);
+ a = MC_GET_NODE_AREA(a_p);
+
+ return (a->start_addr + off);
+}
+
+
+//assert open() and close() methods of an interface
+void assert_iface_open_close(enum mc_iface_type iface,
+ void (* assert_session)(mc_session *, pid_t)) {
+
+ int ret;
+
+ pid_t pid;
+ mc_session s;
+
+
+ //kill existing targets
+ ret = clean_targets();
+ ck_assert_int_eq(ret, 0);
+
+ //setup tests
+ pid = start_target();
+ ck_assert_int_ne(pid, -1);
+
+
+ //first test: open the target & close the target
+ ret = mc_open(&s, iface, pid);
+ ck_assert_int_eq(ret, 0);
+
+ assert_session(&s, pid);
+
+ ret = mc_close(&s);
+ ck_assert_int_eq(ret, 0);
+
+
+ //second test: re-attach to existing target and
+ // target exits before session is closed
+ ret = mc_open(&s, iface, pid);
+ ck_assert_int_eq(ret, 0);
+
+ assert_session(&s, pid);
+ end_target(pid);
+
+ ret = mc_close(&s);
+ ck_assert_int_eq(ret, 0);
+
+ return;
+}
+
+
+//procfs_update_map() [no fixture]
+void assert_iface_update_map(enum mc_iface_type iface) {
+
+ int ret;
+
+ pid_t pid;
+ mc_session s;
+ mc_vm_map m;
+
+
+ //kill existing targets
+ ret = clean_targets();
+ ck_assert_int_eq(ret, 0);
+
+ //setup tests
+ pid = start_target();
+ ck_assert_int_ne(pid, -1);
+
+ ret = mc_open(&s, iface, pid);
+ ck_assert_int_eq(ret, 0);
+
+ mc_new_vm_map(&m);
+
+
+ //first test: update empty map
+ ret = mc_update_map(&s, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_target_map(&m);
+
+
+ //second test: update filled map (map new areas)
+ change_target_map(pid);
+
+ ret = mc_update_map(&s, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_target_map(&m);
+
+
+ //third test: update filled map (unmap old areas)
+ change_target_map(pid);
+
+ ret = mc_update_map(&s, &m);
+ ck_assert_int_eq(ret, 0);
+
+ assert_target_map(&m);
+
+
+ //fourth test: process exited
+ end_target(pid);
+
+ ret = mc_update_map(&s, &m);
+ ck_assert_int_eq(ret, -1);
+
+
+ //cleanup
+ ret = mc_close(&s);
+ ck_assert_int_eq(ret, 0);
+
+ ret = mc_del_vm_map(&m);
+ ck_assert_int_eq(ret, 0);
+
+ return;
+}
+
+
+//procfs_read() & procfs_write() [no fixture]
+void assert_iface_read_write(enum mc_iface_type iface) {
+
+ /*
+ * NOTE: These tests work with hardcoded offsets extracted from
+ * a compiled `unit_target` binary with rizin. If you find
+ * that these tests are failing for you, verify these
+ * offsets are correct with gdb & `/proc//maps`
+ */
+
+ int ret;
+
+ cm_byte rw_buf[16];
+ uintptr_t tgt_buf_addr;
+
+ pid_t pid;
+ mc_session s;
+ mc_vm_map m;
+
+ const char * w_buf = IFACE_W_BUF_STR;
+
+
+ //kill existing targets
+ ret = clean_targets();
+ ck_assert_int_eq(ret, 0);
+
+ //setup tests
+ pid = start_target();
+ ck_assert_int_ne(pid, -1);
+
+ ret = mc_open(&s, iface, pid);
+ ck_assert_int_eq(ret, 0);
+
+ mc_new_vm_map(&m);
+ ret = mc_update_map(&s, &m);
+ ck_assert_int_eq(ret, 0);
+
+
+ //first test: read & write predefined rw- segment, seek & no seek
+ tgt_buf_addr = _get_addr(&m, false, IFACE_RW_BUF_OBJ_INDEX,
+ IFACE_RW_BUF_AREA_INDEX, IFACE_RW_BUF_OFF);
+
+ //read foreign buffer
+ ret = mc_read(&s, tgt_buf_addr, rw_buf, TARGET_BUF_SZ);
+ ck_assert_int_eq(ret, 0);
+
+ //check foreign buffer was read correctly
+ ret = strncmp((char *) rw_buf, IFACE_RW_BUF_STR, TARGET_BUF_SZ);
+ ck_assert_int_eq(ret, 0);
+
+
+ //write to foreign buffer (first half)
+ ret = mc_write(&s, tgt_buf_addr, (cm_byte *) w_buf, TARGET_BUF_SZ);
+ ck_assert_int_eq(ret, 0);
+
+ //re-read foreign buffer
+ ret = mc_read(&s, tgt_buf_addr, rw_buf, TARGET_BUF_SZ);
+ ck_assert_int_eq(ret, 0);
+
+ //check the write was performed correctly
+ ret = strncmp((char *) rw_buf, IFACE_W_BUF_STR, TARGET_BUF_SZ);
+ ck_assert_int_eq(ret, 0);
+
+
+
+ //second test: read & write to region with no read & write permissions
+ memset(rw_buf, 0, 16);
+ tgt_buf_addr = _get_addr(&m, true, IFACE_NONE_OBJ_INDEX,
+ IFACE_NONE_AREA_INDEX, IFACE_NONE_OFF);
+
+ //write to foreign buffer
+ ret = mc_write(&s, tgt_buf_addr, (cm_byte *) w_buf, TARGET_BUF_SZ);
+ INFO_PRINT("[%s][no perm]: returned %d\n",
+ get_iface_name(iface), ret);
+
+ //read foreign buffer
+ ret = mc_read(&s, tgt_buf_addr, rw_buf, TARGET_BUF_SZ);
+ INFO_PRINT("[%s][no perm]: returned %d\n",
+ get_iface_name(iface), ret);
+
+ //check if write succeeded
+ ret = strncmp((char *) rw_buf, IFACE_W_BUF_STR, TARGET_BUF_SZ);
+ INFO_PRINT("[%s][no perm]: returned %d\n",
+ get_iface_name(iface), ret);
+
+
+
+ //third test: read & write to unmapped memory
+ memset(rw_buf, 0, 16);
+ tgt_buf_addr = 0x1337;
+
+ //write to foreign buffer
+ ret = mc_write(&s, tgt_buf_addr, (cm_byte *) w_buf, TARGET_BUF_SZ);
+ INFO_PRINT("[%s][unmapped]: returned %d\n",
+ get_iface_name(iface), ret);
+
+ //read foreign buffer
+ ret = mc_read(&s, tgt_buf_addr, rw_buf, TARGET_BUF_SZ);
+ INFO_PRINT("[%s][unmapped]: returned %d\n",
+ get_iface_name(iface), ret);
+
+ ret = strncmp((char *) rw_buf, IFACE_W_BUF_STR, TARGET_BUF_SZ);
+ INFO_PRINT("[%s][unmapped]: returned %d\n",
+ get_iface_name(iface), ret);
+
+
+ //cleanup
+ ret = mc_close(&s);
+ ck_assert_int_eq(ret, 0);
+
+ ret = mc_del_vm_map(&m);
+ ck_assert_int_eq(ret, 0);
+
+ end_target(pid);
+
+ return;
+
+} END_TEST
diff --git a/src/test/iface_helper.h b/src/test/iface_helper.h
new file mode 100644
index 0000000..f0ebe84
--- /dev/null
+++ b/src/test/iface_helper.h
@@ -0,0 +1,23 @@
+#ifndef IFACE_HELPER_H
+#define IFACE_HELPER_H
+
+//standard library
+#include
+
+//system headers
+#include
+
+//external libraries
+#include
+
+//test target headers
+#include "../lib/memcry.h"
+
+
+//map helper functions
+void assert_iface_open_close(enum mc_iface_type iface,
+ void (* assert_session)(mc_session *, pid_t));
+void assert_iface_update_map(enum mc_iface_type iface);
+void assert_iface_read_write(enum mc_iface_type iface);
+
+#endif
diff --git a/src/test/info.c b/src/test/info.c
new file mode 100644
index 0000000..013e873
--- /dev/null
+++ b/src/test/info.c
@@ -0,0 +1,16 @@
+//local includes
+#include "../lib/memcry.h"
+
+
+//mc_iface_type names
+static char * _iface_names[2] = {
+ "PROCFS",
+ "KRNCRY"
+};
+
+
+//convert enum to name
+char * get_iface_name(enum mc_iface_type iface) {
+
+ return _iface_names[(int) iface];
+}
diff --git a/src/test/info.h b/src/test/info.h
new file mode 100644
index 0000000..670a6c0
--- /dev/null
+++ b/src/test/info.h
@@ -0,0 +1,17 @@
+#ifndef INFO_H
+#define INFO_H
+
+//standard library
+#include
+
+//local includes
+#include "../lib/memcry.h"
+
+
+#define INFO_PRINT(format, ...)\
+ printf("[INFO]: " format, ##__VA_ARGS__)
+
+
+char * get_iface_name(enum mc_iface_type iface);
+
+#endif
diff --git a/src/test/main.c b/src/test/main.c
index 31d0a61..0cbe6d0 100644
--- a/src/test/main.c
+++ b/src/test/main.c
@@ -1,42 +1,122 @@
+//standard library
#include
-#include
+#include
+//system headers
#include
+#include
-#include
+//external libraries
+#include
+#include
-#include
+//local headers
+#include "suites.h"
-#include "../lib/liblain.h"
-#include "test.h"
+//manipulated by debug.sh; see `suites.h`
+bool _DEBUG_ACTIVE = false;
-int main() {
+//tests bitmask
+#define MAP_TEST 0x1
+#define PROCFS_TEST 0x2
+#define KRNCRY_TEST 0x4
+#define MAP_UTIL_TEST 0x8
+#define UTIL_TEST 0x10
- int ret, iface;
- uintptr_t test_addr;
- pid_t pid;
- char comm[NAME_MAX];
- ln_vm_map vm_map;
- //test utils
- printf("target name: ");
- scanf("%s", comm);
- pid = test_utils(comm);
+//determine which tests to run
+static cm_byte _get_test_mode(int argc, char ** argv) {
- ln_new_vm_map(&vm_map);
+ const struct option long_opts[] = {
+ {"map", no_argument, NULL, 'm'},
+ {"procfs", no_argument, NULL, 'p'},
+ {"krncry", no_argument, NULL, 'k'},
+ {"map-util", no_argument, NULL, 'n'},
+ {"util", no_argument, NULL, 'u'},
+ {0,0,0,0}
+ };
- //test ifaces
- printf("interface (0 - lainko, 1 - procfs): ");
- scanf("%d", &iface);
- printf("rw- buffer to read/write 0x2000 bytes from/to: ");
- scanf("%lx", &test_addr);
- test_iface(pid, iface, &vm_map, test_addr);
+ int opt;
+ cm_byte test_mask = 0;
- //test map
- test_map(&vm_map);
+
+ while((opt = getopt_long(argc, argv, "mpknu", long_opts, NULL)) != -1
+ && opt != 0) {
- ret = ln_del_vm_map(&vm_map);
+ //determine parsed argument
+ switch (opt) {
+ case 'm':
+ test_mask |= MAP_TEST;
+ break;
+
+ case 'p':
+ test_mask |= PROCFS_TEST;
+ break;
+
+ case 'k':
+ test_mask |= KRNCRY_TEST;
+ break;
+
+ case 'n':
+ test_mask |= MAP_UTIL_TEST;
+ break;
+
+ case 'u':
+ test_mask |= UTIL_TEST;
+ break;
+ }
+ }
+
+ return test_mask;
+}
+
+
+//run unit tests
+static void _run_unit_tests(cm_byte test_mask) {
+
+ Suite * s_fake;
+ Suite * s_map;
+ Suite * s_procfs_iface;
+ Suite * s_krncry_iface;
+ Suite * s_map_util;
+ Suite * s_util;
+
+ SRunner * sr;
+
+
+ //initialise test suites
+ s_fake = fake_suite();
+ if (test_mask & MAP_TEST) s_map = map_suite();
+ if (test_mask & PROCFS_TEST) s_procfs_iface = procfs_iface_suite();
+ if (test_mask & KRNCRY_TEST) s_krncry_iface = krncry_iface_suite();
+ if (test_mask & MAP_UTIL_TEST) s_map_util = map_util_suite();
+ if (test_mask & UTIL_TEST) s_util = util_suite();
+
+ //create suite runner
+ sr = srunner_create(s_fake);
+ if (test_mask & MAP_TEST) srunner_add_suite(sr, s_map);
+ if (test_mask & PROCFS_TEST) srunner_add_suite(sr, s_procfs_iface);
+ if (test_mask & KRNCRY_TEST) srunner_add_suite(sr, s_krncry_iface);
+ if (test_mask & MAP_UTIL_TEST) srunner_add_suite(sr, s_map_util);
+ if (test_mask & UTIL_TEST) srunner_add_suite(sr, s_util);
+
+ //run tests
+ srunner_run_all(sr, CK_VERBOSE);
+
+ //cleanup
+ srunner_free(sr);
+
+ return;
+}
+
+
+//dispatch tests
+int main(int argc, char ** argv) {
+
+ cm_byte test_mask = _get_test_mode(argc, argv);
+ _run_unit_tests(test_mask);
+
return 0;
}
diff --git a/src/test/map.c b/src/test/map.c
deleted file mode 100644
index 3b73219..0000000
--- a/src/test/map.c
+++ /dev/null
@@ -1,210 +0,0 @@
-#include
-
-#include
-
-#include "../lib/liblain.h"
-#include "test.h"
-
-
-void print_map_area(ln_vm_area * area, char * prefix) {
-
- fprintf(stderr, "%spathname: %s\n", prefix, area->pathname);
- fprintf(stderr, "%sbasename: %s\n", prefix, area->basename);
-
- fprintf(stderr, "%sstart_addr: %lx\n", prefix, area->start_addr);
- fprintf(stderr, "%send_addr: %lx\n", prefix, area->end_addr);
-
- int mask_iter = 0;
- int mask_start = 1;
- char perm[] = {'r', 'w', 'x', 's'};
-
- //print permissions
- fprintf(stderr, "%saccess: ", prefix);
- do {
-
- if (area->access & mask_start) {
- fprintf(stderr, "%c", perm[mask_iter]);
- } else {
- fprintf(stderr, "%c", '-');
- }
-
- mask_start *= 2;
- ++mask_iter;
- } while (mask_iter < 4);
- fprintf(stderr, "%c", '\n');
-
- fprintf(stderr, "%sid: %d\n", prefix, area->id);
- fprintf(stderr, "%smapped: %s", prefix, area->mapped ? "true" : "false");
- fprintf(stderr, "\n");
-
- return;
-}
-
-
-void print_map_obj(ln_vm_obj * obj) {
-
- int ret;
-
- cm_list_node * node;
- ln_vm_area * area;
-
- fprintf(stderr, "\tpathname: %s\n", obj->pathname);
- fprintf(stderr, "\tbasename: %s\n", obj->basename);
-
- fprintf(stderr, "\tstart_addr: %lx\n", obj->start_addr);
- fprintf(stderr, "\tend_addr: %lx\n", obj->end_addr);
-
- fprintf(stderr, "\tid: %d\n", obj->id);
- fprintf(stderr, "\tmapped: %s", obj->mapped ? "true" : "false");
-
- fprintf(stderr, "\n\tnodes:\n");
-
- for (int i = 0; i < obj->vm_area_node_ptrs.len; ++i) {
-
- ret = cm_list_get_val(&obj->vm_area_node_ptrs, i, (cm_byte *) &node);
- area = LN_GET_NODE_AREA(node);
-
- fprintf(stderr, "\n\t\t[AREA %d]\n", i);
- print_map_area(area, "\t\t");
-
- } //end for
-
- fprintf(stderr, "\n");
-
- return;
-}
-
-
-//print out map areas in chronological order to stdout
-void print_areas(ln_vm_map * vm_map) {
-
- cm_list_node * node;
- ln_vm_area * area;
-
- for (int i = 0; i < vm_map->vm_areas.len; ++i) {
-
- node = cm_list_get_node(&vm_map->vm_areas, i);
- area = LN_GET_NODE_AREA(node);
-
- fprintf(stderr, "\n[AREA %d]\n", i);
- print_map_area(area, "\t");
-
- } //end for
-
- return;
-}
-
-
-void print_objs(ln_vm_map * vm_map) {
-
- cm_list_node * node;
- ln_vm_obj * obj;
-
- for (int i = 0; i < vm_map->vm_objs.len; ++i) {
-
- node = cm_list_get_node(&vm_map->vm_objs, i);
- obj = LN_GET_NODE_OBJ(node);
-
- fprintf(stderr, "\n[OBJ %d]\n", i);
- print_map_obj(obj);
- }
-
- return;
-}
-
-
-//print unmapped areas
-void print_unmapped_areas(ln_vm_map * vm_map) {
-
- int ret;
- cm_list_node * node;
- ln_vm_area * area;
-
- int mask_start = 1;
- int mask_iter = 0;
- char perm[] = {'r', 'w', 'x', 's'};
-
- for (int i = 0; i < vm_map->vm_areas_unmapped.len; ++i) {
-
- ret = cm_list_get_val(&vm_map->vm_areas_unmapped, i, (cm_byte *) &node);
- area = LN_GET_NODE_AREA(node);
-
- fprintf(stderr, "\n[AREA %d]\n", i);
- print_map_area(area, "\t");
-
- } //end for
-
- return;
-}
-
-
-//print unmapped objs
-void print_unmapped_objs(ln_vm_map * vm_map) {
-
- int ret;
- cm_list_node * node;
- ln_vm_obj * obj;
-
- for (int i = 0; i < vm_map->vm_objs_unmapped.len; ++i) {
-
- ret = cm_list_get_val(&vm_map->vm_objs_unmapped, i, (cm_byte *) &node);
- obj = LN_GET_NODE_OBJ(node);
-
- fprintf(stderr, "\n[OBJ %d]\n", i);
- print_map_obj(obj);
- }
-
- return;
-}
-
-
-void test_map(ln_vm_map * vm_map) {
-
- printf("\n\n --- [MAP & MAP UTILS] --- \n\n");
- printf("note: use a debugger to test map utils.\n");
-
- int ret;
- off_t off;
- char * name;
- char * badname = "lain";
-
- cm_list_node * node;
- ln_vm_area * area;
- ln_vm_obj * obj;
-
- //test obj by name
- node = cm_list_get_node(&vm_map->vm_areas, 1);
- area = LN_GET_NODE_AREA(node);
- name = area->pathname;
-
- node = ln_get_vm_obj_by_pathname(vm_map, name);
- obj = LN_GET_NODE_OBJ(node);
- name = obj->basename;
-
- node = ln_get_vm_obj_by_basename(vm_map, name);
- obj = LN_GET_NODE_OBJ(node);
-
- //test area/obj by address
- node = ln_get_vm_area_by_addr(vm_map, obj->start_addr + 0x500, &off);
- area = LN_GET_NODE_AREA(node);
-
- node = ln_get_vm_obj_by_addr(vm_map, area->start_addr + 0x500, &off);
- obj = LN_GET_NODE_OBJ(node);
-
- //dump areas
- print_areas(vm_map);
-
- //dump objs
- print_objs(vm_map);
-
- //dump unmapped areas
- print_unmapped_areas(vm_map);
-
- //dump unmapped objs
- print_unmapped_objs(vm_map);
-
- //clean unmapped areas/objs
- ret = ln_map_clean_unmapped(vm_map);
-
- return;
-}
diff --git a/src/test/map_helper.c b/src/test/map_helper.c
new file mode 100644
index 0000000..494906c
--- /dev/null
+++ b/src/test/map_helper.c
@@ -0,0 +1,325 @@
+//standard library
+#include
+#include
+
+//system headers
+#include
+
+#include
+
+//external libraries
+#include
+#include
+
+//local headers
+#include "map_helper.h"
+#include "suites.h"
+
+//test target headers
+#include "../lib/memcry.h"
+#include "../lib/map.h"
+
+
+//initialise a cm_lst_node stub wrapper
+void create_lst_wrapper(cm_lst_node * node, void * data) {
+
+ node->data = data;
+ node->next = node->prev = NULL;
+
+ return;
+}
+
+
+//assert the length of a list, also works as an integration test for CMore
+void assert_lst_len(cm_lst * list, int len) {
+
+ ck_assert_int_eq(list->len, len);
+
+ //if length is zero (0), ensure head is null
+ if (len == 0) {
+ ck_assert_ptr_null(list->head);
+ return;
+ }
+
+ //if length is one (1), ensure head is not null
+ ck_assert_ptr_nonnull(list->head);
+ cm_lst_node * iter = list->head;
+
+ if (len == 1) {
+ ck_assert_ptr_null(iter->next);
+ ck_assert_ptr_null(iter->prev);
+ return;
+ }
+
+ //if length is greater than 1 (1), iterate over nodes to ensure length
+ ck_assert_ptr_nonnull(iter->next);
+ iter = iter->next;
+
+ for (int i = 1; i < len; ++i) {
+ ck_assert(iter != list->head);
+ iter = iter->next;
+ }
+
+ return;
+}
+
+
+//properly assert a potentially NULL string pair
+void assert_names(char * a, char * b) {
+
+ //determine comparison type
+ int cmp_type = (a == NULL ? 0 : 1) + (b == NULL ? 0 : 2);
+
+
+ switch (cmp_type) {
+
+ case 0:
+ ck_assert_ptr_eq(a, b);
+ break;
+ case 1:
+ ck_assert(*a == '\0' && b == NULL);
+ break;
+ case 2:
+ ck_assert(a == NULL && *b == '\0');
+ break;
+ case 3:
+ ck_assert_str_eq(a, b);
+ break;
+
+ } //end switch
+
+ return;
+}
+
+
+//basic assertion of state for a mc_vm_map
+void assert_vm_map(mc_vm_map * map, int vm_areas_len, int vm_objs_len,
+ int vm_areas_unmapped_len, int vm_objs_unmapped_len,
+ int next_id_area, int next_id_obj) {
+
+ //check mapped lists
+ assert_lst_len(&map->vm_areas, vm_areas_len);
+ assert_lst_len(&map->vm_objs, vm_objs_len);
+
+ //check unmapped lists
+ assert_lst_len(&map->vm_areas_unmapped, vm_areas_unmapped_len);
+ assert_lst_len(&map->vm_objs_unmapped, vm_objs_unmapped_len);
+
+ //check next IDs
+ ck_assert_int_eq(map->next_id_area, next_id_area);
+ ck_assert_int_eq(map->next_id_obj, next_id_obj);
+
+ return;
+}
+
+
+/*
+ * NOTE: The mapped area & object lists store areas and objects directly.
+ * Unmapped area & object lists store unmapped nodes instead, which
+ * means an additional pointer dereference is required.
+ */
+
+//assert the state of all [unmapped] objects inside a mc_vm_map
+void assert_vm_map_objs(cm_lst * lst, struct obj_check * obj_checks,
+ int start_index, int len, bool mapped) {
+
+ int ret;
+
+ cm_lst_node * node;
+ mc_vm_obj * obj;
+
+
+ for (int i = 0; i < len; ++i) {
+
+ if (!mapped) {
+ ret = cm_lst_get(lst, start_index + i, &node);
+ ck_assert_int_eq(ret, 0);
+ obj = MC_GET_NODE_OBJ(node);
+
+ } else {
+ obj = cm_lst_get_p(lst, start_index + i);
+ ck_assert_ptr_nonnull(obj);
+ }
+
+ assert_names(obj->basename, obj_checks[i].basename);
+ ck_assert_int_eq(obj->start_addr, obj_checks[i].start_addr);
+ ck_assert_int_eq(obj->end_addr, obj_checks[i].end_addr);
+ }
+
+ return;
+}
+
+
+//assert only pathnames, not mapped address ranges
+void assert_vm_map_objs_aslr(cm_lst * lst, char (* basenames)[NAME_MAX],
+ int start_index, int len, bool mapped) {
+
+ int ret;
+
+ cm_lst_node * node;
+ mc_vm_obj * obj;
+
+
+ for (int i = 0; i < len; ++i) {
+
+ if (!mapped) {
+ ret = cm_lst_get(lst, start_index + i, &node);
+ ck_assert_int_eq(ret, 0);
+ obj = MC_GET_NODE_OBJ(node);
+
+ } else {
+ obj = cm_lst_get_p(lst, start_index + i);
+ ck_assert_ptr_nonnull(obj);
+ }
+
+ assert_names(obj->basename, basenames[i]);
+ }
+
+ return;
+}
+
+
+//assert the state of all [unmapped] memory areas inside a mc_vm_map
+void assert_vm_map_areas(cm_lst * lst, struct area_check * area_checks,
+ int start_index, int len, bool mapped) {
+
+ int ret;
+
+ cm_lst_node * node;
+ mc_vm_area * area;
+
+ for (int i = 0; i < len; ++i) {
+
+ if (!mapped) {
+ ret = cm_lst_get(lst, start_index + i, &node);
+ ck_assert_int_eq(ret, 0);
+ area = MC_GET_NODE_AREA(node);
+
+ } else {
+ area = cm_lst_get_p(lst, start_index + i);
+ ck_assert_ptr_nonnull(area);
+ }
+
+ assert_names(area->basename, area_checks[i].basename);
+ ck_assert_int_eq(area->start_addr, area_checks[i].start_addr);
+ ck_assert_int_eq(area->end_addr, area_checks[i].end_addr);
+ }
+
+ return;
+}
+
+
+//assert only pathnames, not mapped address ranges
+void assert_vm_map_areas_aslr(cm_lst * lst, char (* basenames)[NAME_MAX],
+ int start_index, int len, bool mapped) {
+
+ int ret;
+
+ cm_lst_node * node;
+ mc_vm_area * area;
+
+ for (int i = 0; i < len; ++i) {
+
+ if (!mapped) {
+ ret = cm_lst_get(lst, start_index + i, &node);
+ ck_assert_int_eq(ret, 0);
+ area = MC_GET_NODE_AREA(node);
+
+ } else {
+ area = cm_lst_get_p(lst, start_index + i);
+ ck_assert_ptr_nonnull(area);
+ }
+
+ assert_names(area->basename, basenames[i]);
+ }
+
+ return;
+}
+
+
+void assert_vm_obj(mc_vm_obj * obj, char * pathname, char * basename,
+ uintptr_t start_addr, uintptr_t end_addr, int vm_areas_len,
+ int last_vm_areas_len, int id, bool mapped) {
+
+ //check names
+ assert_names(obj->pathname, pathname);
+ assert_names(obj->basename, basename);
+
+ //check addresses
+ ck_assert_int_eq(obj->start_addr, start_addr);
+ ck_assert_int_eq(obj->end_addr, end_addr);
+
+ //check area node lists are initialised
+ assert_lst_len(&obj->vm_area_node_ps, vm_areas_len);
+ assert_lst_len(&obj->last_vm_area_node_ps, last_vm_areas_len);
+
+ //check the object ID is correctly set
+ ck_assert_int_eq(obj->id, id);
+
+ //check the object is set as mapped
+ ck_assert(obj->mapped == mapped);
+
+ return;
+}
+
+
+/*
+ * Check state of the object by checking the starting addresses of each of
+ * its constituent areas.
+ */
+
+void assert_vm_obj_list(cm_lst * outer_node_lst,
+ uintptr_t * start_addrs, int start_addrs_len) {
+
+ mc_vm_area * area;
+ cm_lst_node * area_node, * iter_node;
+
+
+ //setup iteration
+ iter_node = outer_node_lst->head;
+
+ //if provided lst is empty, return
+ if (outer_node_lst->len == 0 && outer_node_lst->head == NULL) return;
+
+ //otherwise iterate over area starting addresses
+ for (int i = 0; i < start_addrs_len; ++i) {
+
+ //check starting address
+ area_node = MC_GET_NODE_PTR(iter_node);
+ area = MC_GET_NODE_AREA(area_node);
+ ck_assert_int_eq(area->start_addr, start_addrs[i]);
+
+ //advance iteration
+ iter_node = iter_node->next;
+ }
+}
+
+
+void assert_vm_area(mc_vm_area * area, char * pathname, char * basename,
+ uintptr_t start_addr, uintptr_t end_addr,
+ cm_byte access, cm_lst_node * obj_node_p,
+ cm_lst_node * last_obj_node_p, int id, bool mapped) {
+
+ //check names
+ assert_names(area->pathname, pathname);
+ assert_names(area->basename, basename);
+
+ //check addresses
+ ck_assert_int_eq(area->start_addr, start_addr);
+ ck_assert_int_eq(area->end_addr, end_addr);
+
+ //check access
+ ck_assert(area->access == access);
+
+ //check object pointers
+ ck_assert_ptr_eq(area->obj_node_p, obj_node_p);
+ ck_assert_ptr_eq(area->last_obj_node_p, last_obj_node_p);
+
+ //check the area ID is correctly set
+ ck_assert_int_eq(area->id, id);
+
+ //check the area is mapped
+ ck_assert(area->mapped == mapped);
+
+ return;
+}
diff --git a/src/test/map_helper.h b/src/test/map_helper.h
new file mode 100644
index 0000000..bfd75c7
--- /dev/null
+++ b/src/test/map_helper.h
@@ -0,0 +1,66 @@
+#ifndef MAP_HELPER_H
+#define MAP_HELPER_H
+
+//standard library
+#include
+
+//system headers
+#include
+
+#include
+
+//external libraries
+#include
+
+//test target headers
+#include "../lib/memcry.h"
+
+
+//map test structures
+struct obj_check {
+
+ char basename[NAME_MAX];
+ uintptr_t start_addr;
+ uintptr_t end_addr;
+};
+
+
+struct area_check {
+
+ char basename[NAME_MAX];
+ uintptr_t start_addr;
+ uintptr_t end_addr;
+};
+
+
+//map helper functions
+void create_lst_wrapper(cm_lst_node * node, void * data);
+void assert_lst_len(cm_lst * list, int len);
+
+void assert_names(char * name_1, char * name_2);
+void assert_vm_map(mc_vm_map * map, int vm_areas_len, int vm_objs_len,
+ int vm_areas_unmapped_len, int vm_objs_unmapped_len,
+ int next_id_area, int next_id_obj);
+
+void assert_vm_map_objs(cm_lst * lst, struct obj_check * obj_checks,
+ int start_index, int len, bool mapped);
+void assert_vm_map_objs_aslr(cm_lst * lst, char (* basenames)[NAME_MAX],
+ int start_index, int len, bool mapped);
+
+void assert_vm_map_areas(cm_lst * lst, struct area_check * area_checks,
+ int start_index, int len, bool mapped);
+void assert_vm_map_areas_aslr(cm_lst * lst, char (* basenames)[NAME_MAX],
+ int start_index, int len, bool mapped);
+
+void assert_vm_obj(mc_vm_obj * obj, char * pathname, char * basename,
+ uintptr_t start_addr, uintptr_t end_addr, int vm_areas_len,
+ int last_vm_areas_len, int id, bool mapped);
+void assert_vm_obj_list(cm_lst * outer_node_lst,
+ uintptr_t * start_addrs, int start_addrs_len);
+
+void assert_vm_area(mc_vm_area * area, char * pathname, char * basename,
+ uintptr_t start_addr, uintptr_t end_addr,
+ cm_byte access, cm_lst_node * obj_node_p,
+ cm_lst_node * last_obj_node_p, int id, bool mapped);
+
+#endif
diff --git a/src/test/suites.c b/src/test/suites.c
new file mode 100644
index 0000000..a1c3086
--- /dev/null
+++ b/src/test/suites.c
@@ -0,0 +1,19 @@
+//external libraries
+#include
+
+
+
+/*
+ * `libcheck` requires that a test suite is constructed with at least
+ * one suite. However, all tests should be optional and be selected
+ * through command-line arguments. To circumvent this limitation, the
+ * test runner is constructed with a fake_suite.
+ */
+
+Suite * fake_suite() {
+
+ //create a placeholder suite
+ Suite * s = suite_create("fake suite");
+
+ return s;
+}
diff --git a/src/test/suites.h b/src/test/suites.h
new file mode 100644
index 0000000..4c4a480
--- /dev/null
+++ b/src/test/suites.h
@@ -0,0 +1,27 @@
+#ifndef SUITES_H
+#define SUITES_H
+
+//standard library
+#include
+
+//external libraries
+#include
+
+
+//modify behaviour a debugger breaks
+extern bool _DEBUG_ACTIVE;
+
+
+//unit test suites
+Suite * fake_suite();
+Suite * krncry_iface_suite();
+Suite * procfs_iface_suite();
+Suite * map_suite();
+Suite * map_util_suite();
+Suite * util_suite();
+
+
+//other tests
+//TODO
+
+#endif
diff --git a/src/test/target/Makefile b/src/test/target/Makefile
new file mode 100644
index 0000000..56d5ce1
--- /dev/null
+++ b/src/test/target/Makefile
@@ -0,0 +1,30 @@
+.RECIPEPREFIX:=>
+
+# This makefile takes the following variables:
+#
+# CC - Compiler.
+# BUILD_DIR - Unit test build directory.
+
+
+CFLAGS=-O0 -ggdb
+WARN_OPTS=-Wno-unused-variable -Wno-unused-but-set-variable
+LDFLAGS=-L${LIB_BIN_DIR} -Wl,-rpath=${LIB_BIN_DIR}
+
+SOURCES_TARGET=unit_target.c
+OBJECTS_TARGET=${SOURCES_TARGET:%.c=${BUILD_DIR}/%.o}
+
+TARGET=unit_target
+
+target: ${TARGET}
+> mkdir -p ${BUILD_DIR}
+> mv ${TARGET} ${BUILD_DIR}
+
+${TARGET}: ${OBJECTS_TARGET}
+> ${CC} ${CFLAGS} ${WARN_OPTS} -o $@ $^ ${LDFLAGS}
+
+${BUILD_DIR}/%.o: %.c
+> ${CC} ${CFLAGS} ${WARN_OPTS} -c $< -o $@
+
+clean:
+> -rm -v ${BUILD_DIR}/${TARGET}
+> -rm -v ${OBJECTS_TARGET}
diff --git a/src/test/target/unit_target.c b/src/test/target/unit_target.c
new file mode 100644
index 0000000..2a3c8f8
--- /dev/null
+++ b/src/test/target/unit_target.c
@@ -0,0 +1,90 @@
+//standard library
+#include
+#include
+#include
+#include
+#include
+
+//system headers
+#include
+#include
+#include
+
+#include
+#include
+
+//local headers
+#include "../target_helper.h"
+
+
+/*
+ * Unit target is tied directly to `../target_helper.{c,h}`. Changing this
+ * target likely necessitates also changing the target helper.
+ */
+
+/*
+ * This program sleeps indefinitely, awaiting a signal from its parent,
+ * which will cause it to dlopen() an additional library and hence change
+ * its memory map. This is done from inside unit tests.
+ */
+
+
+//globals
+pid_t parent_pid;
+void * libelf;
+enum target_map_state state = UNCHANGED;
+
+/*
+ * These buffers are used in read/write tests for interfaces.
+ */
+
+ char rw_buf[TARGET_BUF_SZ] = IFACE_RW_BUF_STR;
+
+
+//unit test signal handler
+void sigusr1_handler() {
+
+ if (state == UNCHANGED) {
+ libelf = dlopen("libelf.so.1", RTLD_LAZY);
+ state = MAPPED;
+
+ } else if (state == MAPPED) {
+ dlclose(libelf);
+ state = UNMAPPED;
+ }
+
+ kill(parent_pid, SIGUSR1);
+
+ return;
+}
+
+
+int main(int argc, char ** argv) {
+
+ int ch;
+ void * protected_area;
+
+ //check correct number of args is provided (quiet -Wunused-parameter)
+ if (argc != 2) return -1;
+
+ //recover parent pid
+ parent_pid = atoi(argv[1]);
+
+ //map an area that can't be accessed
+ protected_area = mmap((void *) 0x10000, 0x1000, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+
+ //register unit test handler
+ signal(SIGUSR1, sigusr1_handler);
+
+ //signal parent that initialisation is finished
+ kill(parent_pid, SIGUSR1);
+
+ for (int i = 0; ++i; ) {
+
+ //sleep for 10ms to not hoard the CPU
+ usleep(100000);
+ }
+
+ return 0;
+}
diff --git a/src/test/target_helper.c b/src/test/target_helper.c
new file mode 100644
index 0000000..de65a74
--- /dev/null
+++ b/src/test/target_helper.c
@@ -0,0 +1,315 @@
+//standard library
+#include
+#include
+#include
+#include
+
+//system headers
+#include
+#include
+
+#include
+
+#include
+
+//external libraries
+#include
+
+//local headers
+#include "target_helper.h"
+#include "map_helper.h"
+#include "suites.h"
+
+//test target headers
+#include "../lib/memcry.h"
+
+
+//globals
+static enum target_map_state target_state;
+
+char areas_unchanged[TARGET_AREAS_UNCHANGED][NAME_MAX] = {
+ "",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "",
+ "",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "[stack]",
+ "[vvar]",
+ "[vdso]"
+};
+
+char objs_unchanged[TARGET_OBJS_UNCHANGED][NAME_MAX] = {
+ "0x0",
+ "unit_target",
+ "libc.so.6",
+ "ld-linux-x86-64.so.2",
+ "[stack]",
+ "[vvar]",
+ "[vdso]"
+};
+
+
+char areas_mapped[TARGET_AREAS_MAPPED][NAME_MAX] = {
+ "",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "[heap]",
+ "libz.so.1.2.13",
+ "libz.so.1.2.13",
+ "libz.so.1.2.13",
+ "libz.so.1.2.13",
+ "libz.so.1.2.13",
+ "libelf-0.188.so",
+ "libelf-0.188.so",
+ "libelf-0.188.so",
+ "libelf-0.188.so",
+ "libelf-0.188.so",
+ "",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "",
+ "",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "[stack]",
+ "[vvar]",
+ "[vdso]"
+};
+
+char objs_mapped[TARGET_OBJS_MAPPED][NAME_MAX] = {
+ "0x0",
+ "unit_target",
+ "[heap]",
+ "libz.so.1.2.13",
+ "libelf-0.188.so",
+ "libc.so.6",
+ "ld-linux-x86-64.so.2",
+ "[stack]",
+ "[vvar]",
+ "[vdso]"
+};
+
+
+char areas_unmapped[TARGET_AREAS_UNMAPPED][NAME_MAX] = {
+ "",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "unit_target",
+ "[heap]",
+ "",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "libc.so.6",
+ "",
+ "",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "ld-linux-x86-64.so.2",
+ "[stack]",
+ "[vvar]",
+ "[vdso]"
+};
+
+char objs_unmapped[TARGET_OBJS_UNMAPPED][NAME_MAX] = {
+ "0x0",
+ "unit_target",
+ "[heap]",
+ "libc.so.6",
+ "ld-linux-x86-64.so.2",
+ "[stack]",
+ "[vvar]",
+ "[vdso]"
+};
+
+
+//signal handlers
+static void _sigusr1_handler() {
+
+ //printf(" received signal from unit_target.\n");
+
+ if (target_state == UNINIT) {
+ target_state = UNCHANGED;
+
+ } else if (target_state == UNCHANGED) {
+ target_state = MAPPED;
+
+ } else if (target_state == MAPPED) {
+ target_state = UNMAPPED;
+ }
+
+ return;
+}
+
+
+//helpers
+int clean_targets() {
+
+ int ret;
+ char command_buf[TARGET_KILL_CMD_LEN];
+
+
+ //build command string
+ snprintf(command_buf, TARGET_KILL_CMD_LEN,
+ TARGET_KILL_CMD_FMT, TARGET_NAME);
+
+ //use system() to kill all existing targets
+ ret = system(command_buf);
+ if (ret == -1) return -1;
+
+ return 0;
+}
+
+
+pid_t start_target() {
+
+ int ret;
+ __sighandler_t ret_s;
+
+ pid_t target_pid, parent_pid;
+ char pid_buf[8];
+
+ char * argv[3] = {TARGET_NAME, 0, 0};
+ enum target_map_state old_state;
+
+
+ //setup initial state
+ target_state = old_state = UNINIT;
+
+ //get current pid to pass to target
+ parent_pid = getpid();
+ snprintf(pid_buf, 8, "%d", parent_pid);
+ argv[1] = pid_buf;
+
+ //register signal handler
+ ret_s = signal(SIGUSR1, _sigusr1_handler);
+ ck_assert(ret_s != SIG_ERR);
+
+ //fork a new process
+ target_pid = fork();
+ ck_assert_int_ne(target_pid, -1);
+
+ //change image to target in child
+ if (target_pid == 0) {
+ ret = execve(TARGET_NAME, argv, NULL);
+ ck_assert_int_ne(ret, -1);
+
+ //parent waits for child to complete initialisation
+ } else {
+ while (target_state == old_state) {}
+ }
+
+ return target_pid;
+}
+
+
+void end_target(pid_t pid) {
+
+ int ret;
+ __sighandler_t ret_s;
+
+ pid_t ret_p;
+
+
+ //unregister signal handler
+ ret_s = signal(SIGUSR1, SIG_DFL);
+
+ //terminate target process
+ ret = kill(pid, SIGTERM);
+ ck_assert_int_eq(ret, 0);
+
+ //wait for it to terminate
+ ret_p = waitpid(pid, NULL, 0);
+ ck_assert_int_eq(ret_p, pid);
+
+ return;
+}
+
+
+void change_target_map(pid_t pid) {
+
+ int ret;
+ __sighandler_t ret_s;
+
+ enum target_map_state old_state = target_state;
+
+
+ //assert the target hasn't performed all map transformations already
+ ck_assert(target_state != UNMAPPED);
+
+ //send SIGUSR1 to the target process to request a change in its memory map
+ ret = kill(pid, SIGUSR1);
+ ck_assert_int_eq(ret, 0);
+
+ //busy-wait for target to change its memory map
+ if (_DEBUG_ACTIVE) {
+ if (target_state == MAPPED) target_state = UNMAPPED;
+ if (target_state == UNCHANGED) target_state = MAPPED;
+ } else {
+ while (target_state == old_state) {}
+ }
+
+ return;
+}
+
+
+void assert_target_map(mc_vm_map * map) {
+
+ switch(target_state) {
+
+ case UNCHANGED:
+ assert_vm_map_areas_aslr(&map->vm_areas, areas_unchanged,
+ 0, TARGET_AREAS_UNCHANGED, true);
+ assert_vm_map_objs_aslr(&map->vm_objs, objs_unchanged,
+ 0, TARGET_OBJS_UNCHANGED, true);
+ break;
+
+ case MAPPED:
+ assert_vm_map_areas_aslr(&map->vm_areas, areas_mapped,
+ 0, TARGET_AREAS_MAPPED, true);
+ assert_vm_map_objs_aslr(&map->vm_objs, objs_mapped,
+ 0, TARGET_OBJS_MAPPED, true);
+ break;
+
+ case UNMAPPED:
+ assert_vm_map_areas_aslr(&map->vm_areas, areas_unmapped,
+ 0, TARGET_AREAS_UNMAPPED, true);
+ assert_vm_map_objs_aslr(&map->vm_objs, objs_unmapped,
+ 0, TARGET_OBJS_UNMAPPED, true);
+ break;
+
+ default:
+ ck_assert(false); //invalid state
+
+ } //end switch
+
+ return;
+}
diff --git a/src/test/target_helper.h b/src/test/target_helper.h
new file mode 100644
index 0000000..308c173
--- /dev/null
+++ b/src/test/target_helper.h
@@ -0,0 +1,73 @@
+#ifndef TARGET_HELPER_H
+#define TARGET_HELPER_H
+
+//system headers
+#include
+
+//test target headers
+#include "../lib/memcry.h"
+
+
+/*
+ * The target helper is directly tied to `target/unit_target.c`, changing
+ * the target helper means you likely have to change the unit target and
+ * vice versa.
+ */
+
+
+#define TARGET_AREAS_UNCHANGED 22
+extern char areas_unchanged[TARGET_AREAS_UNCHANGED][NAME_MAX];
+
+#define TARGET_OBJS_UNCHANGED 7
+extern char objs_unchanged[TARGET_OBJS_UNCHANGED][NAME_MAX];
+
+#define TARGET_AREAS_MAPPED 33
+extern char areas_mapped[TARGET_AREAS_MAPPED][NAME_MAX];
+
+#define TARGET_OBJS_MAPPED 10
+extern char objs_mapped[TARGET_OBJS_MAPPED][NAME_MAX];
+
+#define TARGET_AREAS_UNMAPPED 23
+extern char areas_unmapped[TARGET_AREAS_UNMAPPED][NAME_MAX];
+
+#define TARGET_OBJS_UNMAPPED 8
+extern char objs_unmapped[TARGET_OBJS_UNMAPPED][NAME_MAX];
+
+
+
+//the state of the target's memory map
+enum target_map_state {
+ UNINIT, //waiting for child to start
+ UNCHANGED, //default state
+ MAPPED, //new areas mapped
+ UNMAPPED //newly mapped areas now unmapped
+};
+
+
+//target metadata
+#define TARGET_NAME "unit_target"
+
+#define TARGET_KILL_CMD_FMT "kill $(pidof %s) > /dev/null 2> /dev/null"
+#define TARGET_KILL_CMD_LEN NAME_MAX + 64
+
+#define TARGET_BUF_SZ 16 /* must be even */
+#define IFACE_RW_BUF_STR "read & write me "
+#define IFACE_W_BUF_STR "buffer written "
+
+#define IFACE_RW_BUF_OBJ_INDEX 1
+#define IFACE_RW_BUF_AREA_INDEX 4
+#define IFACE_RW_BUF_OFF 0x60
+
+#define IFACE_NONE_OBJ_INDEX 0
+#define IFACE_NONE_AREA_INDEX 0
+#define IFACE_NONE_OFF 0x0
+
+
+//target helpers
+int clean_targets();
+pid_t start_target();
+void end_target(pid_t pid);
+void change_target_map(pid_t pid);
+void assert_target_map(mc_vm_map * map);
+
+#endif
diff --git a/src/test/test.h b/src/test/test.h
deleted file mode 100644
index 36df017..0000000
--- a/src/test/test.h
+++ /dev/null
@@ -1,25 +0,0 @@
-#ifndef TEST_H
-#define TEST_H
-
-#include
-
-#include
-
-#include "../lib/liblain.h"
-
-//utils
-pid_t test_utils(char * comm);
-
-//iface
-void test_iface(pid_t pid, int iface, ln_vm_map * vm_map, uintptr_t test_addr);
-
-//map
-void test_map(ln_vm_map * vm_map);
-
-//extras
-void print_map_area(ln_vm_area * area, char * prefix);
-void print_map_obj(ln_vm_obj * obj);
-void print_map_areas(ln_vm_map * vm_map);
-void print_map_objs(ln_vm_map * vm_map);
-
-#endif
diff --git a/src/test/util.c b/src/test/util.c
deleted file mode 100644
index 57793d6..0000000
--- a/src/test/util.c
+++ /dev/null
@@ -1,51 +0,0 @@
-#include
-
-#include
-
-#include
-
-#include
-
-#include "../lib/liblain.h"
-#include "test.h"
-
-
-pid_t test_utils(char * comm) {
-
- printf("\n\n --- [UTILS] --- \n\n");
-
- int ret;
- pid_t pid, pid_iter;
-
- char name_buf[NAME_MAX];
- cm_byte hex_buf[4] = {0xde, 0xad, 0xc0, 0xde};
- char hexstr_buf[10];
-
- cm_vector pid_vector;
-
- //test pathname to basename conversion
- char * path = "/everything/is/connected";
- char * base = ln_pathname_to_basename(path);
- printf("basename of '%s': '%s'\n", path, base);
-
- //test converting process name to pid(s)
- pid = ln_pid_by_name(comm, &pid_vector);
- printf("comm: %s, pid: %d\n", comm, pid);
-
- for (int i = 0; i < pid_vector.len; ++i) {
- ret = cm_vector_get_val(&pid_vector, i, (cm_byte *) &pid_iter);
- printf("\npid %d: %d\n", i, pid_iter);
- } //end for
- cm_del_vector(&pid_vector);
-
- //test converting pid to process name
- ret = ln_name_by_pid(pid, name_buf);
- printf("pid: %d, comm: %s\n", pid, name_buf);
-
- //test bytes to hex string
-
- ln_bytes_to_hex(hex_buf, 4, hexstr_buf);
- printf("should see 'deadcode': %s\n", hexstr_buf);
-
- return pid;
-}
diff --git a/src/tgt/Makefile b/src/tgt/Makefile
deleted file mode 100644
index 8669016..0000000
--- a/src/tgt/Makefile
+++ /dev/null
@@ -1,33 +0,0 @@
-.RECIPEPREFIX:=>
-
-# This makefile takes the following variables:
-#
-# CC - Compiler.
-# BUILD_DIR - Base build directory.
-
-CFLAGS=-O0 -ggdb -Wno-unused-but-set-variable -L${BUILD_DIR}/lib -Wl,-rpath=${BUILD_DIR}/lib -lcmore -llain
-
-SOURCES_TGT=main.c
-HEADERS_TGT=
-OBJECTS_TGT=${SOURCES_TGT:.c=.o}
-
-TGT=target
-
-tgt:${TGT}
-> mkdir -p ${BUILD_DIR}/tgt
-> mv ${TGT} ${BUILD_DIR}/tgt
-
-${TGT}: ${OBJECTS_TGT}
-> ${CC} ${CFLAGS} ${OBJECTS_TGT} ${HEADERS_TGT} \
- -o ${TGT}
-
-${OBJECTS_LIB}: ${SOURCES_LIB} ${HEADERS_LIB}
-> ${CC} ${CFLAGS} -c ${SOURCES_TGT} -o ${OBJECTS_TGT}
-
-clean_all: clean_src clean_build
-
-clean_src:
-> -rm -f *.o
-
-clean_build:
-> -rm ${BUILD_DIR}/tgt/${TGT}
diff --git a/src/tgt/main.c b/src/tgt/main.c
deleted file mode 100644
index 01dd007..0000000
--- a/src/tgt/main.c
+++ /dev/null
@@ -1,103 +0,0 @@
-#include
-#include
-#include
-#include
-#include
-#include
-
-#define FILESIZE 4096 // 4 KB
-
-int main() {
-
- // Create a mapping before main executable
- void * zero_mapping = mmap((void *) 0x100000, 0x2000,
- PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
- -1, 0x0);
-
- // Open "/etc/fstab" file
- int fd = open("/etc/fstab", O_RDONLY);
- if (fd == -1) {
- perror("Error opening file for reading");
- exit(EXIT_FAILURE);
- }
-
- // Memory map empty 8KB
- char * fillMePtr = mmap(0, 8196, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (fillMePtr == MAP_FAILED) {
- close(fd);
- perror("Error mmapping the file");
- exit(EXIT_FAILURE);
- }
-
- // Memory map the first 4KB of the file
- char *dataPtr = mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0);
- if (dataPtr == MAP_FAILED) {
- close(fd);
- perror("Error mmapping the file");
- exit(EXIT_FAILURE);
- }
-
- // Load libutil.so.1
- void *libutil_handle = dlopen("libutil.so.1", RTLD_LAZY);
- if (!libutil_handle) {
- fprintf(stderr, "%s\n", dlerror());
- exit(EXIT_FAILURE);
- }
-
-
- // Do something with the memory mapped file
- printf("Press enter to continue: ");
- char cont;
- scanf("%c", &cont);
-
-
- // Unmap the file
- if (munmap(zero_mapping, FILESIZE) == -1) {
- perror("Error un-mmapping the file");
- exit(EXIT_FAILURE);
- }
-
- // Unmap the file
- if (munmap(dataPtr, FILESIZE) == -1) {
- perror("Error un-mmapping the file");
- exit(EXIT_FAILURE);
- }
-
- // Close the file descriptor
- close(fd);
-
-
- // Unload libutil.so.1
- if (dlclose(libutil_handle) != 0) {
- fprintf(stderr, "%s\n", dlerror());
- exit(EXIT_FAILURE);
- }
-
- // Load libdl.so.2
- void *lib_handle = dlopen("libdl.so.2", RTLD_LAZY);
- if (!lib_handle) {
- fprintf(stderr, "%s\n", dlerror());
- exit(EXIT_FAILURE);
- }
-
- printf("Press enter to continue again: ");
- char cont2;
- scanf("%c", &cont2);
-
- // Unload libdl.so.2
- if (dlclose(lib_handle) != 0) {
- fprintf(stderr, "%s\n", dlerror());
- exit(EXIT_FAILURE);
- }
-
- // Unmap empty 8KB
- if (munmap(fillMePtr, 8192) == -1) {
- perror("Error un-mapping ");
- exit(EXIT_FAILURE);
- }
-
- return 0;
-}
-
diff --git a/src/tgt/tgt b/src/tgt/tgt
deleted file mode 100755
index 8704017..0000000
Binary files a/src/tgt/tgt and /dev/null differ