Automatically load program load offset
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
||||
CC=gcc
|
||||
CFLAGS=-MD -MP -ggdb -I./mujs
|
||||
LDFLAGS=-lm
|
||||
SRCS=debugus.c linenoise.c hash.c
|
||||
SRCS=debugus.c linenoise.c hash.c pmparser.c
|
||||
OBJS=$(patsubst %.c,%.o,$(SRCS))
|
||||
DEPS=$(patsubst %.c,%.d,$(SRCS))
|
||||
|
||||
|
||||
22
debugus.c
22
debugus.c
@@ -14,6 +14,7 @@
|
||||
#include "linenoise.h"
|
||||
#include "mujs.h"
|
||||
#include "hash.h"
|
||||
#include "pmparser.h"
|
||||
|
||||
#define LOG_ERR(fmt, ...) fprintf(stderr, "Error: " fmt, ##__VA_ARGS__)
|
||||
#define LOG_INF(fmt, ...) fprintf(stdout, "Info: " fmt, ##__VA_ARGS__)
|
||||
@@ -33,6 +34,7 @@
|
||||
|
||||
typedef struct {
|
||||
pid_t pid;
|
||||
pid_t proc_pid;
|
||||
uintptr_t addr;
|
||||
bool enabled;
|
||||
uint8_t data;
|
||||
@@ -180,12 +182,32 @@ void dbg_init_js(Dbg *dbg)
|
||||
#undef make_js_func
|
||||
}
|
||||
|
||||
void dbg_init_load_offset(Dbg *dbg)
|
||||
{
|
||||
procmaps_iterator maps_iter = {0};
|
||||
procmaps_error_t parser_err = PROCMAPS_SUCCESS;
|
||||
|
||||
parser_err = pmparser_parse(dbg->pid, &maps_iter);
|
||||
if (parser_err) {
|
||||
LOG_ERR("Failed to parse /proc/%d/maps (%d)\n", dbg->pid, (int)parser_err);
|
||||
return;
|
||||
}
|
||||
|
||||
// We only need the first one
|
||||
procmaps_struct *mem_region = pmparser_next(&maps_iter);
|
||||
|
||||
dbg->program_load_offset = (uintptr_t)mem_region->addr_start;
|
||||
|
||||
pmparser_free(&maps_iter);
|
||||
}
|
||||
|
||||
void dbg_init(Dbg *dbg, const char *file, pid_t pid)
|
||||
{
|
||||
memset(dbg, 0, sizeof(*dbg));
|
||||
dbg->file = file;
|
||||
dbg->pid = pid;
|
||||
dbg_init_js(dbg);
|
||||
dbg_init_load_offset(dbg);
|
||||
hashtable_init(&dbg->brks, MAX_BRKS);
|
||||
}
|
||||
|
||||
|
||||
301
pmparser.c
Normal file
301
pmparser.c
Normal file
@@ -0,0 +1,301 @@
|
||||
/*
|
||||
@Author : ouadev
|
||||
@date : December 2015
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this software and its
|
||||
documentation for any purpose is hereby granted without fee, provided that
|
||||
the above copyright notice appear in all copies and that both that
|
||||
copyright notice and this permission notice appear in supporting
|
||||
documentation. No representations are made about the suitability of this
|
||||
software for any purpose. It is provided "as is" without express or
|
||||
implied warranty.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "pmparser.h"
|
||||
|
||||
// maximum line length in a procmaps file
|
||||
#define PROCMAPS_LINE_INIT_LENGTH (300)
|
||||
// maximum length of the path of a maps file : /proc/[pid]/maps
|
||||
#define PROCMAPS_MAPS_FILE_PATH_MAX_LENGTH 30
|
||||
// maximum token size while parsing the proc/pid/maps
|
||||
#define PROCMAPS_LINE_TOKEN_MAX_LEN 100
|
||||
|
||||
/**
|
||||
* pmparser_parse_line
|
||||
* @description internal usage
|
||||
*/
|
||||
static void pmparser_parse_line(char *buf, procmaps_struct *mem_reg);
|
||||
|
||||
/**
|
||||
* @brief Copy into dest_ptr the string from src_ptr to the first occurence of delimiter
|
||||
*/
|
||||
static char *pmparser_helper_extract(char *src_ptr, const char *delimiter, char *dest_ptr);
|
||||
|
||||
/**
|
||||
* @brief Main function to parse process memory
|
||||
*
|
||||
* @param pid process ID
|
||||
* @param maps_it output : the memory region iterator over the chained list, it should only be read when return is 0
|
||||
* @return procmaps_error_t outcome of the function
|
||||
*/
|
||||
procmaps_error_t pmparser_parse(int pid, procmaps_iterator *maps_it)
|
||||
{
|
||||
char maps_path[PROCMAPS_MAPS_FILE_PATH_MAX_LENGTH];
|
||||
size_t line_len = 0;
|
||||
char *line_ptr = NULL;
|
||||
procmaps_struct *mem_reg = NULL;
|
||||
procmaps_struct *tail_node = NULL;
|
||||
procmaps_struct *head_node = NULL;
|
||||
size_t node_count = 0;
|
||||
|
||||
if (pid >= 0)
|
||||
{
|
||||
snprintf(maps_path, sizeof(maps_path), "/proc/%d/maps", pid);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(maps_path, "/proc/self/maps");
|
||||
}
|
||||
|
||||
FILE *file = fopen(maps_path, "r");
|
||||
if (!file)
|
||||
{
|
||||
return PROCMAPS_ERROR_OPEN_MAPS_FILE;
|
||||
}
|
||||
|
||||
// scan maps file line by line
|
||||
line_len = PROCMAPS_LINE_INIT_LENGTH;
|
||||
if ((line_ptr = (char *)malloc(line_len)) == NULL)
|
||||
{
|
||||
fclose(file);
|
||||
return PROCMAPS_ERROR_MALLOC_FAIL;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
int read = getline(&line_ptr, &line_len, file);
|
||||
if (read == -1)
|
||||
{
|
||||
if (!feof(file))
|
||||
{
|
||||
return PROCMAPS_ERROR_READ_MAPS_FILE;
|
||||
}
|
||||
else
|
||||
{
|
||||
// end of file occured while no characters have been read
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// allocate a node
|
||||
mem_reg = (procmaps_struct *)malloc(sizeof(procmaps_struct));
|
||||
// fill the node
|
||||
pmparser_parse_line(line_ptr, mem_reg);
|
||||
mem_reg->next = NULL;
|
||||
|
||||
// Attach the node
|
||||
if (tail_node == NULL)
|
||||
{
|
||||
head_node = tail_node = mem_reg;
|
||||
}
|
||||
else
|
||||
{
|
||||
tail_node->next = mem_reg;
|
||||
tail_node = mem_reg;
|
||||
}
|
||||
node_count++;
|
||||
}
|
||||
|
||||
// close file
|
||||
fclose(file);
|
||||
free(line_ptr);
|
||||
|
||||
// set iterator
|
||||
maps_it->head = maps_it->current = head_node;
|
||||
maps_it->count = node_count;
|
||||
|
||||
return PROCMAPS_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief move the iterator to the next memory region
|
||||
*
|
||||
* @param p_procmaps_it
|
||||
* @return procmaps_struct*
|
||||
*/
|
||||
procmaps_struct *pmparser_next(procmaps_iterator *p_procmaps_it)
|
||||
{
|
||||
if (p_procmaps_it->current == NULL)
|
||||
return NULL;
|
||||
procmaps_struct *p_current = p_procmaps_it->current;
|
||||
p_procmaps_it->current = p_procmaps_it->current->next;
|
||||
return p_current;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief free the parser data
|
||||
*
|
||||
* @param p_procmaps_it
|
||||
*/
|
||||
void pmparser_free(procmaps_iterator *p_procmaps_it)
|
||||
{
|
||||
procmaps_struct *cursor = p_procmaps_it->head;
|
||||
procmaps_struct *next = NULL;
|
||||
|
||||
if (p_procmaps_it->head == NULL)
|
||||
return;
|
||||
|
||||
while (cursor != NULL)
|
||||
{
|
||||
next = cursor->next;
|
||||
free(cursor->pathname);
|
||||
free(cursor);
|
||||
cursor = next;
|
||||
}
|
||||
memset(p_procmaps_it, 0x00, sizeof(procmaps_iterator));
|
||||
}
|
||||
|
||||
static char *pmparser_helper_extract(char *src_ptr, const char *delimiter, char *dest_ptr)
|
||||
{
|
||||
char *p_separator = NULL;
|
||||
size_t copy_len = 0;
|
||||
|
||||
p_separator = strstr(src_ptr, delimiter);
|
||||
copy_len = (p_separator - src_ptr);
|
||||
memcpy(dest_ptr, src_ptr, copy_len);
|
||||
dest_ptr[copy_len] = 0x00;
|
||||
|
||||
return p_separator;
|
||||
}
|
||||
|
||||
static void pmparser_parse_line(char *buf, procmaps_struct *mem_reg)
|
||||
{
|
||||
char token[PROCMAPS_LINE_TOKEN_MAX_LEN];
|
||||
size_t pathname_len = 0;
|
||||
char *p_cursor = buf;
|
||||
|
||||
// addr1
|
||||
p_cursor = pmparser_helper_extract(p_cursor, "-", token);
|
||||
p_cursor++;
|
||||
|
||||
sscanf(token, "%lx", (long unsigned *)&mem_reg->addr_start);
|
||||
|
||||
// addr2
|
||||
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
|
||||
p_cursor++;
|
||||
|
||||
sscanf(token, "%lx", (long unsigned *)&mem_reg->addr_end);
|
||||
|
||||
// region size
|
||||
mem_reg->length = (unsigned long)((char*)mem_reg->addr_end - (char*)mem_reg->addr_start);
|
||||
|
||||
// perm
|
||||
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
|
||||
p_cursor++;
|
||||
|
||||
mem_reg->is_r = (token[0] == 'r');
|
||||
mem_reg->is_w = (token[1] == 'w');
|
||||
mem_reg->is_x = (token[2] == 'x');
|
||||
mem_reg->is_p = (token[3] == 'p');
|
||||
|
||||
// offset
|
||||
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
|
||||
p_cursor++;
|
||||
|
||||
sscanf(token, "%lx", &mem_reg->offset);
|
||||
|
||||
// dev
|
||||
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
|
||||
p_cursor++;
|
||||
|
||||
sscanf(token, "%u:%u", &mem_reg->dev_major, &mem_reg->dev_minor);
|
||||
|
||||
// inode
|
||||
p_cursor = pmparser_helper_extract(p_cursor, " ", token);
|
||||
p_cursor++;
|
||||
|
||||
sscanf(token, "%llu", &mem_reg->inode);
|
||||
|
||||
// pathname
|
||||
// find the start of the pathname
|
||||
while (*p_cursor == '\t' || *p_cursor == ' ')
|
||||
p_cursor++;
|
||||
// calculate its size
|
||||
char *ptr_sz = p_cursor;
|
||||
while (*ptr_sz != '\n')
|
||||
{
|
||||
ptr_sz++;
|
||||
}
|
||||
pathname_len = (ptr_sz - p_cursor);
|
||||
// copy it
|
||||
mem_reg->pathname = (char *)malloc(pathname_len * sizeof(char) + 1);
|
||||
memcpy(mem_reg->pathname, p_cursor, pathname_len);
|
||||
mem_reg->pathname[pathname_len] = 0x00;
|
||||
|
||||
// Pathname decoding
|
||||
if (mem_reg->pathname[0] == 0x00)
|
||||
{
|
||||
// empty path name
|
||||
mem_reg->map_type = PROCMAPS_MAP_ANON_MMAPS;
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[stack]", 7) == 0)
|
||||
{
|
||||
// mapping backed by main thread stack
|
||||
mem_reg->map_type = PROCMAPS_MAP_STACK;
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[stack:", 7) == 0)
|
||||
{
|
||||
mem_reg->map_type = PROCMAPS_MAP_STACK_TID;
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[vdso]", 6) == 0)
|
||||
{
|
||||
mem_reg->map_type = PROCMAPS_MAP_VDSO;
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[heap]", 6) == 0)
|
||||
{
|
||||
mem_reg->map_type = PROCMAPS_MAP_HEAP;
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[anon:", 6) == 0)
|
||||
{
|
||||
mem_reg->map_type = PROCMAPS_MAP_ANON_PRIV;
|
||||
pmparser_helper_extract(mem_reg->pathname + 6, "]", token);
|
||||
strncpy(mem_reg->map_anon_name, token, MAPPING_ANON_NAME_MAX_LEN);
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[anon_shmem:", 12) == 0)
|
||||
{
|
||||
mem_reg->map_type = PROCMAPS_MAP_ANON_SHMEM;
|
||||
pmparser_helper_extract(mem_reg->pathname + 12, "]", token);
|
||||
strncpy(mem_reg->map_anon_name, token, MAPPING_ANON_NAME_MAX_LEN);
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[vvar]", 6) == 0)
|
||||
{
|
||||
mem_reg->map_type = PROCMAPS_MAP_VVAR;
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[vsyscall]", 10) == 0)
|
||||
{
|
||||
mem_reg->map_type = PROCMAPS_MAP_VSYSCALL;
|
||||
}
|
||||
else if (strncmp(mem_reg->pathname, "[", 1) == 0)
|
||||
{
|
||||
mem_reg->map_type = PROCMAPS_MAP_OTHER;
|
||||
}
|
||||
else
|
||||
{
|
||||
// file backed mapping then
|
||||
mem_reg->map_type = PROCMAPS_MAP_FILE;
|
||||
|
||||
// is the file deleted ?
|
||||
// file_deleted
|
||||
if (memcmp(mem_reg->pathname + strlen(mem_reg->pathname) - 9, "(deleted)", 9) == 0)
|
||||
{
|
||||
mem_reg->file_deleted = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
mem_reg->file_deleted = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
112
pmparser.h
Normal file
112
pmparser.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
@Author : ouadev
|
||||
@date : December 2015
|
||||
|
||||
Permission to use, copy, modify, distribute, and sell this software and its
|
||||
documentation for any purpose is hereby granted without fee, provided that
|
||||
the above copyright notice appear in all copies and that both that
|
||||
copyright notice and this permission notice appear in supporting
|
||||
documentation. No representations are made about the suitability of this
|
||||
software for any purpose. It is provided "as is" without express or
|
||||
implied warranty.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef H_PMPARSER
|
||||
#define H_PMPARSER
|
||||
|
||||
// Documentation link: https://man7.org/linux/man-pages/man5/proc_pid_maps.5.html
|
||||
|
||||
// maximum length of the name of an anonymous mapping
|
||||
#define MAPPING_ANON_NAME_MAX_LEN 80
|
||||
|
||||
/**
|
||||
* @brief Type of a memory's region mapping.
|
||||
*
|
||||
*/
|
||||
typedef enum
|
||||
{
|
||||
PROCMAPS_MAP_FILE,
|
||||
PROCMAPS_MAP_STACK,
|
||||
PROCMAPS_MAP_STACK_TID,
|
||||
PROCMAPS_MAP_VDSO,
|
||||
PROCMAPS_MAP_VVAR,
|
||||
PROCMAPS_MAP_VSYSCALL,
|
||||
PROCMAPS_MAP_HEAP,
|
||||
PROCMAPS_MAP_ANON_PRIV,
|
||||
PROCMAPS_MAP_ANON_SHMEM,
|
||||
PROCMAPS_MAP_ANON_MMAPS,
|
||||
PROCMAPS_MAP_OTHER,
|
||||
} procmaps_map_type;
|
||||
|
||||
/**
|
||||
* procmaps_struct
|
||||
* @desc hold all the information about an area in the process's VM
|
||||
*/
|
||||
typedef struct procmaps_struct
|
||||
{
|
||||
void *addr_start; //< start address of the area
|
||||
void *addr_end; //< end address
|
||||
size_t length; //< size of the range
|
||||
short is_r;
|
||||
short is_w;
|
||||
short is_x;
|
||||
short is_p;
|
||||
size_t offset; //< offset
|
||||
unsigned int dev_major;
|
||||
unsigned int dev_minor;
|
||||
unsigned long long inode; //< inode of the file that backs the area
|
||||
char *pathname; //< the path of the file that backs the area ( dynamically allocated)
|
||||
procmaps_map_type map_type;
|
||||
char map_anon_name[MAPPING_ANON_NAME_MAX_LEN + 1]; //< name of the anonymous mapping in case map_type is an anon mapping
|
||||
short file_deleted; //< whether the file backing the mapping was deleted
|
||||
// chained list
|
||||
struct procmaps_struct *next; //<handler of the chained list
|
||||
} procmaps_struct;
|
||||
|
||||
/**
|
||||
* @brief procmaps error type
|
||||
*
|
||||
*/
|
||||
typedef enum procmaps_error
|
||||
{
|
||||
PROCMAPS_SUCCESS = 0,
|
||||
PROCMAPS_ERROR_OPEN_MAPS_FILE,
|
||||
PROCMAPS_ERROR_READ_MAPS_FILE,
|
||||
PROCMAPS_ERROR_MALLOC_FAIL,
|
||||
} procmaps_error_t;
|
||||
|
||||
/**
|
||||
* procmaps_iterator
|
||||
* @desc holds iterating information
|
||||
*/
|
||||
typedef struct procmaps_iterator
|
||||
{
|
||||
procmaps_struct *head;
|
||||
procmaps_struct *current;
|
||||
size_t count;
|
||||
} procmaps_iterator;
|
||||
|
||||
/**
|
||||
* @brief Main function to parse process memory
|
||||
* @param pid process ID
|
||||
* @param maps_it output : the memory region iterator over the chained list, t should only be read when return is 0.
|
||||
* @return procmaps_error_t outcome of the function
|
||||
*/
|
||||
procmaps_error_t pmparser_parse(int pid, procmaps_iterator *maps_it);
|
||||
|
||||
/**
|
||||
* pmparser_next
|
||||
* @description move between areas
|
||||
* @param p_procmaps_it the iterator to move on step in the chained list
|
||||
* @return a procmaps structure filled with information about this VM area
|
||||
*/
|
||||
procmaps_struct *pmparser_next(procmaps_iterator *p_procmaps_it);
|
||||
/**
|
||||
* pmparser_free
|
||||
* @description should be called at the end to free the resources
|
||||
* @param p_procmaps_it the iterator structure returned by pmparser_parse
|
||||
*/
|
||||
void pmparser_free(procmaps_iterator *p_procmaps_it);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user