Skip to content

Commit

Permalink
The Great Splitting Out (Part 2 of ??)
Browse files Browse the repository at this point in the history
(also the 25th birthday commit)

- Merge i386/i686 branch
- Split out assembly for each arch
- Create an x86_gen to reduce code duplication
- More cleanup still needed.
  • Loading branch information
ice799 committed Dec 19, 2009
1 parent bfed129 commit 85f99ac
Show file tree
Hide file tree
Showing 10 changed files with 572 additions and 232 deletions.
30 changes: 30 additions & 0 deletions ext/arch.h
@@ -0,0 +1,30 @@
#if !defined (_ARCH_H_)
#define _ARCH_H_

#if defined(_ARCH_i386_) || defined(_ARCH_i686_)
#include "i386.h"
#elif defined(_ARCH_x86_64_)
#include "x86_64.h"
#else
#error "Unsupported architecture! Cannot continue compilation."
#endif

/*
* "normal" trampoline
*/
void *
arch_get_st2_tramp(size_t *size);

void
arch_insert_st1_tramp(void *start, void *trampee, void *tramp);

/*
* inline trampoline
*/
void *
arch_get_inline_st2_tramp(size_t *size);

void *
arch_set_inline_st2_tramp(void *addr, void *marker, void *trampoline, void *table_entry);

#endif
50 changes: 4 additions & 46 deletions ext/bin_api.h
Expand Up @@ -11,59 +11,17 @@ extern size_t pagesize;
/*
* trampoline specific stuff
*/
extern struct tramp_tbl_entry *tramp_table;
extern struct tramp_st2_entry *tramp_table;
extern size_t tramp_size;

/*
* inline trampoline specific stuff
*/
extern struct inline_tramp_st2_entry *inline_tramp_table;
extern size_t inline_tramp_size;
extern struct inline_tramp_tbl_entry *inline_tramp_table;

/* trampoline types */
struct tramp_inline {
unsigned char jmp[1];
uint32_t displacement;
unsigned char pad[2];
} __attribute__((__packed__));

struct tramp_tbl_entry {
unsigned char rbx_save[1];
unsigned char mov[2];
void *addr;
unsigned char callq[2];
unsigned char rbx_restore[1];
unsigned char ret[1];
} __attribute__((__packed__));

struct inline_tramp_tbl_entry {
unsigned char rex[1];
unsigned char mov[1];
unsigned char src_reg[1];
uint32_t mov_displacement;

struct {
unsigned char push_rdi[1];
unsigned char mov_rdi[3];
uint32_t rdi_source_displacement;
unsigned char push_rbx[1];
unsigned char push_rbp[1];
unsigned char save_rsp[3];
unsigned char align_rsp[4];
unsigned char mov[2];
void *addr;
unsigned char callq[2];
unsigned char leave[1];
unsigned char rbx_restore[1];
unsigned char rdi_restore[1];
} __attribute__((__packed__)) frame;

unsigned char jmp[1];
uint32_t jmp_displacement;
} __attribute__((__packed__));

void
update_callqs(int entry, void *trampee_addr);
update_callqs(int entry, void *trampee_addr, void *tramp);

/*
* EXPORTED API.
Expand All @@ -75,7 +33,7 @@ void *
bin_find_symbol(char *sym, size_t *size);

void
bin_update_image(int entry, void *trampee_addr);
bin_update_image(int entry, void *trampee_addr, void *tramp);

void *
bin_allocate_page();
Expand Down
17 changes: 9 additions & 8 deletions ext/elf.c
@@ -1,9 +1,10 @@
#if defined(HAVE_ELF)

#define _GNU_SOURCE
#include "bin_api.h"

#include <err.h>
#include <fcntl.h>
#include <gelf.h>
#include <libelf/gelf.h>
#include <link.h>
#include <stdio.h>
#include <string.h>
Expand All @@ -12,7 +13,7 @@

#include <sys/mman.h>

static ElfW(Shdr) symtab_shdr;
static GElf_Shdr symtab_shdr;
static Elf *elf = NULL;
static Elf_Data *symtab_data = NULL;

Expand All @@ -30,9 +31,9 @@ bin_allocate_page()
}

void
bin_update_image(int entry, void *trampee_addr)
bin_update_image(int entry, void *trampee, void *tramp)
{
update_callqs(entry, trampee_addr);
update_callqs(entry, trampee, tramp);
}

void *
Expand Down Expand Up @@ -67,7 +68,7 @@ void
bin_init()
{
int fd;
ElfW(Shdr) shdr;
GElf_Shdr shdr;
size_t shstrndx;
char *filename;
Elf_Scn *scn;
Expand Down Expand Up @@ -109,13 +110,13 @@ bin_init()
symtab_shdr = shdr;
if ((symtab_data = elf_getdata(scn,symtab_data)) == NULL || symtab_data->d_size == 0) {
errx(EX_DATAERR, "ruby has a broken symbol table. Is it stripped? "
"memprof only works on binaries that are not stripped!\n", filename);
"memprof only works on binaries that are not stripped!\n");
}
}
}

if (!symtab_data) {
errx(EX_DATAERR, "binary is stripped. memprof only works on binaries that are not stripped!", filename);
errx(EX_DATAERR, "binary is stripped. memprof only works on binaries that are not stripped!");
}
}
#endif
9 changes: 8 additions & 1 deletion ext/extconf.rb
Expand Up @@ -23,9 +23,16 @@ def add_define(name)
$defs.push("-D#{name}")
end

arch = RUBY_PLATFORM[/(.*)-linux/,1]

if arch == 'universal'
arch = 'x86_64'
end

add_define "_ARCH_#{arch}_"

###
# libelf

if RUBY_PLATFORM =~ /linux/
libelf = File.basename('libelf-0.8.13.tar.gz')
dir = File.basename(libelf, '.tar.gz')
Expand Down
106 changes: 106 additions & 0 deletions ext/i386.c
@@ -0,0 +1,106 @@
#if defined (_ARCH_i386_) || defined(_ARCH_i686_)

#include <stdint.h>
#include <string.h>

#include <sys/mman.h>

#include "arch.h"
#include "x86_gen.h"

/* This is the stage 1 inline trampoline for hooking the inlined add_freelist
* function .
*
* NOTE: The original instruction mov %reg, freelist is 7 bytes wide,
* whereas jmpq $displacement is only 5 bytes wide. We *must* pad out
* the next two bytes. This will be important to remember below.
*/
struct inline_st1_tramp {
unsigned char jmp;
uint32_t displacement;
unsigned char pad;
} __attribute__((__packed__)) inline_st1_tramp = {
.jmp = '\xe9',
.displacement = 0,
.pad = '\x90',
};

struct inline_st1_base_short {
unsigned char mov;
void *addr;
} __attribute__((__packed__)) inline_st1_short = {
.mov = '\xa3',
.addr = 0,
};

struct inline_st1_base_long {
unsigned char mov;
unsigned char src_reg;
void *addr;
} __attribute__((__packed__)) inline_st1_long = {
.mov = '\x89',
.src_reg = 0,
.addr = 0
};

static int
arch_check_ins(unsigned char *base)
{

/* if the byte is 0xa3 then we're moving from %eax, so
* the length is only 5, so we don't need the pad.
*
* otherwise, we're moving from something else, so the
* length is going to be 6 and we need a NOP.
*/

/* is it a mov instruction? */
if (*base == 0xa3)
return 0;
else if (*base == 0x89)
return 1;

return -1;
}

int
arch_insert_inline_st2_tramp(void *addr, void *marker, void *trampoline, void *table_entry)
{
struct inline_st1_base_long *long_base = addr;
struct inline_st1_base_short *short_base = addr;
struct inline_st1_tramp *st1_tramp = addr;
void *mov_target = NULL;
size_t pad_length = 0;

if ((pad_length = arch_check_ins(addr)) == -1)
return 1;

if (pad_length == 0) {
mov_target = short_base->addr;
default_inline_st2_tramp.mov = 0x90;
default_inline_st2_tramp.src_reg = 0xa3;
inline_st1_tramp.displacement = table_entry - (void *)(short_base + 1);
default_inline_st2_tramp.jmp_displacement = (void *)(short_base + 1) - (table_entry + sizeof(default_inline_st2_tramp));
} else {
mov_target = long_base->addr;
default_inline_st2_tramp.mov = long_base->mov;
default_inline_st2_tramp.src_reg = long_base->src_reg;
inline_st1_tramp.displacement = table_entry - (void *)(long_base + 1) + 1;
default_inline_st2_tramp.jmp_displacement = (void *)(long_base + 1) - (table_entry + sizeof(default_inline_st2_tramp));
}

if (marker == mov_target) {
default_inline_st2_tramp.mov_addr= default_inline_st2_tramp.frame.freelist = marker;
default_inline_st2_tramp.frame.fn_addr = trampoline;
if (pad_length) {
copy_instructions(addr, &inline_st1_tramp, sizeof(inline_st1_tramp));
} else {
copy_instructions(addr, &inline_st1_tramp, sizeof(inline_st1_tramp) - 1);
}
memcpy(table_entry, &default_inline_st2_tramp, sizeof(default_inline_st2_tramp));
return 0;
}

return 1;
}
#endif
75 changes: 75 additions & 0 deletions ext/i386.h
@@ -0,0 +1,75 @@
#if !defined(_i386_h_)
#define _i386_h_

#include <stdint.h>
#include "arch.h"

/*
* This is the "normal" stage 2 trampoline with a default entry pre-filled
*/
static struct tramp_st2_entry {
unsigned char ebx_save;
unsigned char mov;
void *addr;
unsigned char call[2];
unsigned char ebx_restore;
unsigned char ret;
} __attribute__((__packed__)) default_st2_tramp = {
.ebx_save = 0x53, /* push ebx */
.mov = 0xbb, /* mov addr into ebx */
.addr = 0, /* this is filled in later */
.call = {0xff, 0xd3}, /* calll *ebx */
.ebx_restore = 0x5b, /* pop ebx */
.ret = 0xc3, /* ret */
};


/*
* This is the inline stage 2 trampoline with a default entry pre-filled
*/
static struct inline_tramp_st2_entry {

/* this block will be filled in at runtime to replicate the overwritten
* instruction.
*/
unsigned char mov;
unsigned char src_reg;
void *mov_addr;

/* this frame will arrange freelist to be passed as an argument to
* the third and final trampoline (C level).
*/
struct {
unsigned char push_ebx;
unsigned char pushl[2];
void * freelist;
unsigned char mov_ebx;
void * fn_addr;
unsigned char call[2];
unsigned char pop_ebx;
unsigned char restore_ebx;
} __attribute__((__packed__)) frame;

/* this block jumps back to resume execution */
unsigned char jmp;
uint32_t jmp_displacement;
} __attribute__((__packed__)) default_inline_st2_tramp = {
.mov = 0x89,
.src_reg = 0,
.mov_addr = 0,

.frame = {
.push_ebx = 0x53,
.pushl = {0xff, 0x35},
.freelist = 0,
.mov_ebx = 0xbb,
.fn_addr = 0,
.call = {0xff, 0xd3},
.pop_ebx = 0x5b,
.restore_ebx = 0x5b,
},

.jmp = 0xe9,
.jmp_displacement = 0,
};
#endif

0 comments on commit 85f99ac

Please sign in to comment.