clarify memory module to memmap module

This commit is contained in:
2025-05-03 19:00:54 +03:00
parent a4af280cda
commit d1bff36830
14 changed files with 146 additions and 124 deletions

View File

@@ -25,7 +25,7 @@ add_library(hiload SHARED
src/hiload.c
src/symbols.c
src/files.c
src/memory.c
src/memmap.c
src/logger.c
src/moduler.c
src/hielf.c

View File

@@ -1,5 +1,4 @@
#ifndef COMMON_H_
#define COMMON_H_
#pragma once
#include "logger.h"
#include "types.h"
@@ -11,4 +10,3 @@ static inline u32 has_mask(u32 flags, u32 mask) { return flags & mask; }
#define ARRLEN(A) (sizeof((A)) / (sizeof((A)[0])))
#define UNUSED(v) (void)(v)
#endif // COMMON_H_

View File

@@ -2,7 +2,6 @@
#define FILEWATCH_CONTEXT_H_
#include "vector.h"
#include "filewatcher/filewatch.h"
#include "types.h"
/**

View File

@@ -1,12 +1,17 @@
#ifndef HI_FILEWATCHER_H_
#define HI_FILEWATCHER_H_
#include "filewatch_context.h"
#include "filewatch_type.h"
#include "types.h"
/**
* File event watcher using inotify.
*
* Create a FileWatcher and give it files to monitor. FileWatcher creates a thread
* for listening system notifications. It then creates events for each filechange
* for those files under scrutiny.
*
* Paths are stored as absolute, and this doesn't handle symlinks in watched
* files. So if you have e.g. a symlink 'my-proj' to your project folder in
* '/workspace/code/my-proj' paths might exist from either side and they are

View File

@@ -1,5 +1,4 @@
#ifndef ELF_H_
#define ELF_H_
#pragma once
#include <elf.h>
#include <libelf.h>
@@ -10,4 +9,3 @@ void *hi_elf_find_dynamic_segment(void *module_base, size_t n, Elf **elf);
const char *hi_elf_dyntostr(unsigned type);
const char *hi_elf_segtostr(unsigned type);
#endif

View File

@@ -2,12 +2,11 @@
#include "vector.h"
#include "common.h"
#include "filewatcher/filewatch_context.h"
#include "filewatcher/filewatcher.h"
#include "logger.h"
#include "memory.h"
#include "memmap.h"
#include "moduler.h"
#include "string.h"
#include "histring.h"
#include "symbols.h"
#include <assert.h>
@@ -30,7 +29,7 @@ static const char *module_exclude_filter[] = {
typedef struct {
VectorMemoryRegion memory_regions;
VectorMemoryMap memory_regions;
VectorStr enabled_modules;
FileWatcher *filewatcher;
VectorModuleData modules;
@@ -225,7 +224,7 @@ int hi_init(size_t n, const char **enabled_modules) {
}
sc_array_init(&context.memory_regions);
if (read_memory_maps_self(&context.memory_regions) != HI_OK) {
if (memmaps_from_process(&context.memory_regions) != HI_OK) {
log_error("Could not populate program memory maps.\n");
return HI_FAIL;
}

View File

@@ -1,5 +1,4 @@
#ifndef HI_STRING_H_
#define HI_STRING_H_
#pragma once
#include <stddef.h>
@@ -49,4 +48,3 @@ const char *hi_str_starts_with(const char path[static 1], size_t listn,
*/
int hi_path_has_filename(const char *path, const char *filename);
#endif // HI_STRING_H_

View File

@@ -1,5 +1,4 @@
#ifndef LOGGER_H_
#define LOGGER_H_
#pragma once
#include "../3rd/sc/logger/sc_log.h"
@@ -24,4 +23,3 @@ bool log_get_verbose();
#define log_errorv(...) do { if (log_get_verbose()) { \
log_error(__VA_ARGS__); } } while(0)
#endif // LOGGER_H_

View File

@@ -1,4 +1,4 @@
#include "memory.h"
#include "memmap.h"
#include "files.h"
#include "logger.h"
@@ -8,20 +8,17 @@ static inline int ptr_in_range(uptr ptr, uptr start, uptr end) {
return ptr >= start && ptr <= end;
}
HiResult memory_find_region(uptr ptr, struct sc_array_memreg *const regions,
HiResult memmaps_find_by_ptr(uptr ptr, VectorMemoryMap *const maps,
size_t *index) {
for (size_t i = 0; i < sc_array_size(regions); i++) {
uptr start = regions->elems[i].region_start;
for (size_t i = 0; i < sc_array_size(maps); i++) {
uptr start = maps->elems[i].span.start;
// we assume a sorted region by start address, so we can do a quick discard
// here. very useful for relative vs absolute address checks
if (ptr < start)
return HI_FAIL;
uptr end = regions->elems[i].region_end;
uptr end = maps->elems[i].span.end;
if (ptr_in_range(ptr, start, end)) {
if (index)
*index = i;
sc_log_debug("Pointer match (%p) found in index: %u, range: %p - %p\n",
ptr, i, start, end);
return HI_OK;
}
}
@@ -29,8 +26,8 @@ HiResult memory_find_region(uptr ptr, struct sc_array_memreg *const regions,
return HI_FAIL;
}
HiResult read_memory_maps_self(struct sc_array_memreg *regions) {
memory_clear_memregs(regions);
HiResult memmaps_from_process(VectorMemoryMap *outmaps) {
memmaps_clear(outmaps);
char *maps_str = file_to_str_dyn("/proc/self/maps");
if (!maps_str)
@@ -42,7 +39,7 @@ HiResult read_memory_maps_self(struct sc_array_memreg *regions) {
char *line = strtok(strptr, "\n");
while (line) {
MemoryRegion reg = {0};
MemoryMap reg = {0};
char pathname[256];
char perm_str[5];
@@ -53,8 +50,8 @@ HiResult read_memory_maps_self(struct sc_array_memreg *regions) {
// Format example from the file:
// 7fa0b66ca000-7fa0b66cc000 rw-p 00033000 fe:02 28063911 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
int result = sscanf(line, "%lx-%lx %4s %ld %x:%x %lu %255s",
&reg.region_start,
&reg.region_end,
&reg.span.start,
&reg.span.end,
perm_str,
&reg.offset,
&dev_major,
@@ -74,30 +71,28 @@ HiResult read_memory_maps_self(struct sc_array_memreg *regions) {
reg.pathname = strdup(pathname);
}
sc_array_add(regions, reg);
sc_array_add(outmaps, reg);
line = strtok(NULL, "\n");
}
free(maps_str);
return HI_OK;
}
MemoryRegionSpan
memory_get_module_span(const struct sc_array_memreg *const regions,
const char module_name[static 1]) {
MemorySpan memmaps_find_by_name(const char region_name[static 1],
const VectorMemoryMap *const maps) {
uptr start = ~0ull;
uptr end = 0;
for (size_t i = 0; i < sc_array_size(regions); i++) {
for (size_t i = 0; i < sc_array_size(maps); i++) {
const MemoryRegion *reg = &sc_array_at(regions, i);
const MemoryMap *reg = &sc_array_at(maps, i);
if (reg->pathname && strcmp(reg->pathname, module_name) == 0) {
if (reg->region_start < start) {
start = reg->region_start;
if (reg->pathname && strcmp(reg->pathname, region_name) == 0) {
if (reg->span.start < start) {
start = reg->span.start;
}
if (reg->region_end > end) {
end = reg->region_end;
if (reg->span.end > end) {
end = reg->span.end;
}
} else {
// we passed the module regions
@@ -107,24 +102,24 @@ memory_get_module_span(const struct sc_array_memreg *const regions,
}
assert(start < ~0ull && end > 0);
return (MemoryRegionSpan){.region_start = start, .region_end = end};
return (MemorySpan){.start = start, .end = end};
}
void memory_clear_memregs(struct sc_array_memreg *regions) {
for (size_t i = 0; i < sc_array_size(regions); i++) {
memory_free_memreg(&sc_array_at(regions, i));
void memmaps_clear(VectorMemoryMap *maps) {
for (size_t i = 0; i < sc_array_size(maps); i++) {
memmap_free(&sc_array_at(maps, i));
}
sc_array_clear(regions);
sc_array_clear(maps);
}
void memory_term_memregs(struct sc_array_memreg *regions) {
for (size_t i = 0; i < sc_array_size(regions); i++) {
memory_free_memreg(&sc_array_at(regions, i));
void memmaps_term(VectorMemoryMap *maps) {
for (size_t i = 0; i < sc_array_size(maps); i++) {
memmap_free(&sc_array_at(maps, i));
}
sc_array_term(regions);
sc_array_term(maps);
}
void memory_free_memreg(MemoryRegion *reg) {
void memmap_free(MemoryMap *reg) {
if (reg) {
free((char *)reg->pathname);
}

87
src/memmap.h Normal file
View File

@@ -0,0 +1,87 @@
/**
* A module for reading and using data from /proc/pid/maps
*/
#pragma once
#include "types.h"
#include "vector.h"
typedef enum {
HI_MEMORY_READ = (1 << 0),
HI_MEMORY_WRITE = (1 << 1),
HI_MEMORY_EXECUTE = (1 << 2),
HI_MEMORY_SHARED = (1 << 3),
HI_MEMORY_PRIVATE = (1 << 4)
} MemoryPermissions;
typedef struct MemorySpan {
uptr start;
uptr end;
} MemorySpan;
/**
* Holds information of a /proc/pid/maps entry.
*
* See `man proc(5)` for details.
*/
typedef struct MemoryMap {
/**
* Might be null as not all regions have a name or a path.
*/
const char *pathname;
MemorySpan span;
ptrdiff offset;
u64 inode;
u32 permissions; // enum MemoryPermissions
} MemoryMap;
sc_array_def(MemoryMap, memmap);
typedef struct sc_array_memmap VectorMemoryMap;
/// Free memory and zero maps
void memmaps_term(VectorMemoryMap *maps);
/// Clear maps, but don't free the buffer
void memmaps_clear(VectorMemoryMap *maps);
/// Free memory for a single map
void memmap_free(MemoryMap *map);
/**
* Read allocated memory regions of the current process and fill @a outmaps.
*
* Resulting regions are ordered by start address and they don't overlap.
*
* @param outmaps Output map where data is placed. Cleared before use.
* @return HI_FAIL if reading the maps failed
*/
HiResult memmaps_from_process(VectorMemoryMap *outmaps);
/**
* Search for a region that contains the given pointer.
*
* Assumes @a maps is sorted by start range.
*
* @param ptr The needle
* @param maps The haystack
* @param index If non-null, will contain index of the MemoryMap in @a maps
* @return if ptr was found from any region in maps
*/
HiResult memmaps_find_by_ptr(uptr ptr, VectorMemoryMap *const maps,
size_t *index);
/**
* Find the allocated memory region by a pathname.
*
* Assumes @a maps is ordered by start address and by path, i.e. all modules
* are allocated into consecutive memory addresses without gaps or other
* modules or named regions in between. This should be true when filled
* by @a memmaps_from_process.
*
* @param region_name Name for the region, e.g. absolute path to a module
* @param maps The haystack
*/
MemorySpan memmaps_find_by_name(const char region_name[static 1],
const VectorMemoryMap *const maps);

View File

@@ -1,49 +0,0 @@
#pragma once
#include "vector.h"
#include "types.h"
#include <assert.h>
typedef enum {
HI_MEMORY_READ = 1 << 0,
HI_MEMORY_WRITE = 1 << 1,
HI_MEMORY_EXECUTE = 1 << 2,
HI_MEMORY_SHARED = 1 << 3,
HI_MEMORY_PRIVATE = 1 << 4
} MemoryPermissions;
typedef struct {
uptr region_start;
uptr region_end;
} MemoryRegionSpan;
typedef struct {
const char *pathname;
uptr region_start;
uptr region_end;
ptrdiff offset;
u64 inode;
u32 permissions; // enum MemoryPermissions
} MemoryRegion;
sc_array_def(MemoryRegion, memreg);
typedef struct sc_array_memreg VectorMemoryRegion;
// Free memory and init to zero
void memory_term_memregs(VectorMemoryRegion *regions);
// Doesn't free underlying array memory, only for each region
void memory_clear_memregs(VectorMemoryRegion *regions);
// Free child memory
void memory_free_memreg(MemoryRegion *reg);
/* A pointer that can be used to place the memory regions into. Clears regions
* before use, but uses the same buffer. */
HiResult read_memory_maps_self(VectorMemoryRegion *regions);
/* Return index the pointer is found in */
HiResult memory_find_pointer(uptr ptr,
VectorMemoryRegion *const regions,
size_t *index);
MemoryRegionSpan memory_get_module_span(const VectorMemoryRegion *const regions,
const char module_name[static 1]);

View File

@@ -5,7 +5,7 @@
#include "hielf.h"
#include "histring.h"
#include "logger.h"
#include "memory.h"
#include "memmap.h"
#include "symbols.h"
#include "types.h"
@@ -22,7 +22,7 @@
typedef struct {
const char *filename;
void *dlhandle;
MemoryRegionSpan memreg;
MemorySpan memreg;
} PatchData;
static void *adjust_if_relative(void *ptr, void *module_base) {
@@ -168,10 +168,10 @@ cleanup:
}
static HiResult moduler_apply_module_patch(VectorSymbol *psymbols,
MemoryRegionSpan module_memory) {
MemorySpan module_memory) {
void *module_base = (void *)module_memory.region_start;
size_t module_size = module_memory.region_end - module_memory.region_start;
void *module_base = (void *)module_memory.start;
size_t module_size = module_memory.end - module_memory.start;
void *plt = NULL;
ElfW(Rela) *jmprel = NULL;
@@ -296,7 +296,7 @@ PatchData moduler_create_patch(ModuleData *module) {
}
HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
struct sc_array_memreg *memregs) {
VectorMemoryMap *memmaps) {
// Return OK because this isn't an error case. We just don't do it yet
// because we only handle reloading a complete solib. Can't do that with
@@ -324,10 +324,10 @@ HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
patch.dlhandle = new_handle;
// refresh cache
read_memory_maps_self(memregs);
memmaps_from_process(memmaps);
patch.memreg = memory_get_module_span(memregs, patch.filename);
void *patch_base = (void *)patch.memreg.region_start;
patch.memreg = memmaps_find_by_name(patch.filename, memmaps);
void *patch_base = (void *)patch.memreg.start;
VectorSymbol patch_symbols;
symbol_init_symbols(&patch_symbols);
@@ -346,7 +346,7 @@ HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
continue;
log_debug("Patching: %s\n", mod.name);
MemoryRegionSpan module_memory = memory_get_module_span(memregs, mod.name);
MemorySpan module_memory = memmaps_find_by_name(mod.name, memmaps);
moduler_apply_module_patch(&patch_symbols, module_memory);
if (strncmp(mod.name, patch.filename, strlen(mod.name)) == 0) {

View File

@@ -1,8 +1,7 @@
#ifndef MODULER_H_
#define MODULER_H_
#pragma once
#include "vector.h"
#include "memory.h"
#include "memmap.h"
#include "symbols.h"
#include "types.h"
@@ -41,6 +40,4 @@ static inline bool hi_modinfo_has(ModuleInfo flags, ModuleFlags flag) {
#define HI_MODINFO_CLEAR(info, flag) ((info) &= ~flag)
HiResult moduler_reload(VectorModuleData *modules, ModuleData *module,
VectorMemoryRegion *memregs);
#endif // MODULER_H_
VectorMemoryMap *memregs);

View File

@@ -1,5 +1,4 @@
#ifndef TYPES_H_
#define TYPES_H_
#pragma once
#include "hitypes.h"
@@ -22,5 +21,3 @@ typedef ptrdiff_t ptrdiff;
typedef uint8_t bool8;
typedef uint32_t bool32;
#endif // TYPES_H_