memprof 0.2.5 → 0.2.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -5,14 +5,6 @@ What is memprof?
5
5
  Memprof is a memory profiler for Ruby that requires no patches to the Ruby VM.
6
6
  It can help you find Ruby level memory leaks in your application.
7
7
 
8
- Required to install
9
- ===================
10
- If you are using the Linux version, you need to install libelf:
11
-
12
- apt-get install libelfg0-dev
13
-
14
- The experimental OSX version needs no additional libraries.
15
-
16
8
  How to use
17
9
  ==========
18
10
 
@@ -36,30 +28,34 @@ Memprof.stats also takes an (optional) file name to write the output to a file.
36
28
 
37
29
  Supported systems
38
30
  =================
39
- This only works on unstripped binaries.
31
+ **This only works on unstripped binaries.**
40
32
 
41
33
  Currently supporting:
42
34
 
43
- Linux:
35
+ Linux (enable-shared AND disable-shared):
44
36
  x86_64 builds of Ruby Enterprise Edition 1.8.6/1.8.7
45
- x86_64 builds of MRI Ruby (enabled-shared and disable-shared)
37
+ x86_64 builds of MRI Ruby
38
+
39
+ Snow Leopard (enable-shared AND disable-shared):
40
+ x86_64 builds of Ruby Enterprise Edition 1.8.6/1.8.7
41
+ x86_64 builds of MRI Ruby
46
42
 
47
43
  Experimental (somewhat broken) support:
48
44
 
49
- Linux:
45
+ Linux and OSX:
50
46
  i386/i686 support.
51
47
 
52
48
  Snow Leopard:
53
- x86_64 builds of MRI (both enable-shared and disable-shared)
54
- OSX system Ruby, distributed with Snow Leopard
49
+ OSX system Ruby, distributed with Snow Leopard.
55
50
 
56
51
  Coming soon:
57
52
 
58
- Official support for Snow Leopard.
59
-
60
53
  Linux:
61
54
  Tracking object allocationns in C extensions.
62
55
 
56
+ Official support for Ruby 1.9
57
+ Official support for i386/i686
58
+
63
59
  CREDITS
64
60
  =======
65
61
  Jake Douglas for the Mach O/snow leopard support.
data/ext/arch.h CHANGED
@@ -1,6 +1,10 @@
1
1
  #if !defined (_ARCH_H_)
2
2
  #define _ARCH_H_
3
3
 
4
+ /*
5
+ * The only supported architectures at this time are i{3-6}86 and amd64. All
6
+ * other architectures should fail.
7
+ */
4
8
  #if defined(_ARCH_i386_) || defined(_ARCH_i686_)
5
9
  #include "i386.h"
6
10
  #elif defined(_ARCH_x86_64_)
@@ -10,24 +14,63 @@
10
14
  #endif
11
15
 
12
16
  /*
13
- * "normal" trampoline
17
+ * arch_get_st2_tramp - architecture specific stage 2 trampoline getter
18
+ *
19
+ * This function will return a pointer to an area of memory that contains
20
+ * the stage 2 trampoline for this architecture.
21
+ *
22
+ * This function will also set the out parameter size equal to the size of this
23
+ * region, if size is not NULL.
14
24
  */
15
25
  void *
16
26
  arch_get_st2_tramp(size_t *size);
17
27
 
28
+ /*
29
+ * arch_insert_st1_tramp - architecture specific stage 1 trampoline insert
30
+ *
31
+ * Given:
32
+ * start - a start address to attempt to insert a trampoline at
33
+ * trampee - the address of the function to trampoline
34
+ * tramp - a pointer to the trampoline
35
+ *
36
+ * This function will inspect the start address to determine if an instruction
37
+ * which transfers execution to the trampee is present. If so, this function
38
+ * will rewrite the instruction to ensure that tramp is called instead.
39
+ *
40
+ * Returns 0 on success, 1 otherwise.
41
+ */
18
42
  int
19
43
  arch_insert_st1_tramp(void *start, void *trampee, void *tramp);
20
44
 
21
45
  /*
22
- * inline trampoline
46
+ * arch_get_inline_st2_tramp - architecture specific inline stage 2 tramp getter
47
+ *
48
+ * This function will return a pointer to an area of memory that contains the
49
+ * stage 2 inline trampoline for this architecture.
50
+ *
51
+ * This function will also set the out parameter size equal to the size of this
52
+ * region, if size is not NULL.
23
53
  */
24
54
  void *
25
55
  arch_get_inline_st2_tramp(size_t *size);
26
56
 
57
+ /*
58
+ * Given:
59
+ * - addr - The base address of an instruction sequence.
60
+ *
61
+ * - marker - This is the marker to search for which will indicate that the
62
+ * instruction sequence has been located.
63
+ *
64
+ * - trampoline - The address of the handler to redirect execution to.
65
+ *
66
+ * - table_entry - Address of where the stage 2 trampoline code will reside
67
+ *
68
+ * This function will:
69
+ * Insert and setup the stage 1 and stage 2 trampolines if addr points to an
70
+ * instruction that could be from the inlined add_freelist function.
71
+ *
72
+ * This function returns 1 on failure and 0 on success.
73
+ */
27
74
  int
28
75
  arch_insert_inline_st2_tramp(void *addr, void *marker, void *trampoline, void *table_entry);
29
-
30
- void
31
- arch_overwrite_got(void *plt, void *tramp);
32
-
33
76
  #endif
@@ -3,42 +3,90 @@
3
3
  #include <stddef.h>
4
4
  #include <stdint.h>
5
5
 
6
- /* XXX get rid of this */
7
- extern size_t pagesize;
8
-
9
6
  /*
10
- * trampoline specific stuff
7
+ * Instead of getting the pagesize from sysconf over and over again, pollute
8
+ * the global namespace with a dangerous name.
9
+ *
10
+ * XXX change this
11
11
  */
12
- extern struct tramp_st2_entry *tramp_table;
13
- extern size_t tramp_size;
12
+ extern size_t pagesize;
14
13
 
15
14
  /*
16
- * inline trampoline specific stuff
15
+ * Some useful foward declarations for function protoypes here and elsewhere.
17
16
  */
18
- extern struct inline_tramp_st2_entry *inline_tramp_table;
19
- extern size_t inline_tramp_size;
17
+ struct tramp_st2_entry;
18
+ struct inline_tramp_st2_entry;
20
19
 
21
20
  /*
22
- * EXPORTED API.
21
+ * bin_init - Initialize the binary format specific layer.
23
22
  */
24
23
  void
25
24
  bin_init();
26
25
 
26
+ /*
27
+ * bin_find_symbol - find a symbol
28
+ *
29
+ * Given:
30
+ * - sym - a symbol name
31
+ * - size - optional out parameter
32
+ *
33
+ * This function will search for the symbol sym and return its address if
34
+ * found, or NULL if the symbol could not be found.
35
+ *
36
+ * Optionally, this function will set its out parameter size equal to the
37
+ * size of the symbol.
38
+ */
27
39
  void *
28
- bin_find_symbol(char *sym, size_t *size);
40
+ bin_find_symbol(const char *sym, size_t *size);
29
41
 
42
+ /*
43
+ * bin_allocate_page - allocate a page suitable for trampolines
44
+ *
45
+ * This function will allocate a page of memory in the right area of the
46
+ * virtual address space and with the appropriate permissions for stage 2
47
+ * trampoline code to live and execute.
48
+ */
30
49
  void *
31
- bin_find_got_addr(char *sym, void *cookie);
50
+ bin_allocate_page(void);
32
51
 
33
- void *
34
- bin_allocate_page();
52
+ /*
53
+ * bin_type_size - Return size (in bytes) of a given type.
54
+ *
55
+ * Given:
56
+ * - type - a string representation of a type
57
+ *
58
+ * This function will return the size (in bytes) of the type. If no such type
59
+ * can be found, return 0.
60
+ */
61
+ size_t
62
+ bin_type_size(const char *type);
35
63
 
64
+ /*
65
+ * bin_type_member_offset - Return the offset (in bytes) of member in type.
66
+ *
67
+ * Given:
68
+ * - type - a string representation of a type
69
+ * - member - a string representation of a field int he type
70
+ *
71
+ * This function will return the offset (in bytes) of member in type.
72
+ *
73
+ * On failure, this function returns -1.
74
+ */
36
75
  int
37
- bin_type_size(char *type);
76
+ bin_type_member_offset(const char *type, const char *member);
38
77
 
78
+ /*
79
+ * bin_update_image - Update a binary image in memory
80
+ *
81
+ * Given:
82
+ * - trampee - the name of the symbol to hook
83
+ * - tramp - the stage 2 trampoline entry
84
+ *
85
+ * this function will update the binary image so that all calls to trampee will
86
+ * be routed to tramp.
87
+ *
88
+ * Returns 0 on success.
89
+ */
39
90
  int
40
- bin_type_member_offset(char *type, char *member);
41
-
42
- void
43
- bin_update_image(int entry, char *trampee_addr, struct tramp_st2_entry *tramp);
91
+ bin_update_image(const char *trampee_addr, struct tramp_st2_entry *tramp);
44
92
  #endif
data/ext/elf.c CHANGED
@@ -3,12 +3,14 @@
3
3
  #include "bin_api.h"
4
4
  #include "arch.h"
5
5
 
6
+ #include <assert.h>
6
7
  #include <dwarf.h>
7
8
  #include <err.h>
8
9
  #include <error.h>
9
10
  #include <fcntl.h>
10
11
  #include <libdwarf.h>
11
12
  #include <libelf/gelf.h>
13
+ #include <limits.h>
12
14
  #include <link.h>
13
15
  #include <stdio.h>
14
16
  #include <stdlib.h>
@@ -18,11 +20,15 @@
18
20
 
19
21
  #include <sys/mman.h>
20
22
 
21
- /* ruby binary info */
22
- static int has_libruby = 0;
23
+ /* The size of a PLT entry */
24
+ #define PLT_ENTRY_SZ (16)
25
+
26
+ /* Keep track of whether this ruby is built with a shared library or not */
27
+ static int libruby = 0;
23
28
 
24
29
  static Dwarf_Debug dwrf = NULL;
25
30
 
31
+ /* Set of ELF specific state about this Ruby binary/shared object */
26
32
  static struct elf_info *ruby_info = NULL;
27
33
 
28
34
  struct elf_info {
@@ -54,20 +60,88 @@ struct elf_info {
54
60
  const char *filename;
55
61
  };
56
62
 
63
+ /*
64
+ * plt_entry - procedure linkage table entry
65
+ *
66
+ * This struct is intended to be "laid onto" a piece of memory to ease the
67
+ * parsing, use, modification, and length calculation of PLT entries.
68
+ *
69
+ * For example:
70
+ * jmpq *0xaaf00d(%rip) # jump to GOT entry
71
+ *
72
+ * # the following instructions are only hit if function has not been
73
+ * # resolved previously.
74
+ *
75
+ * pushq $0x4e # push ID
76
+ * jmpq 0xcafebabefeedface # invoke the link linker
77
+ */
78
+ struct plt_entry {
79
+ unsigned char jmp[2];
80
+ int32_t jmp_disp;
81
+
82
+ /* There is no reason (currently) to represent the pushq and jmpq
83
+ * instructions which invoke the linker.
84
+ *
85
+ * We don't need those to hook the GOT; we only need the jmp_disp above.
86
+ *
87
+ * TODO represent the extra instructions
88
+ */
89
+ unsigned char pad[10];
90
+ } __attribute__((__packed__));
91
+
92
+ /*
93
+ * get_got_addr - given a PLT entry, return the global offset table entry that
94
+ * the entry uses.
95
+ */
96
+ static void *
97
+ get_got_addr(struct plt_entry *plt)
98
+ {
99
+ assert(plt != NULL);
100
+ /* the jump is relative to the start of the next instruction. */
101
+ return (void *)&(plt->pad) + plt->jmp_disp;
102
+ }
103
+
104
+ /*
105
+ * overwrite_got - given the address of a PLT entry, overwrite the address
106
+ * in the GOT that the PLT entry uses with the address in tramp.
107
+ */
108
+ static void
109
+ overwrite_got(void *plt, void *tramp)
110
+ {
111
+ assert(plt != NULL);
112
+ assert(tramp != NULL);
113
+ memcpy(get_got_addr(plt), &tramp, sizeof(void *));
114
+ return;
115
+ }
57
116
 
117
+ /*
118
+ * do_bin_allocate_page - internal page allocation routine
119
+ *
120
+ * This function allocates a page suitable for stage 2 trampolines. This page
121
+ * is allocated based on the location of the text segment of the Ruby binary
122
+ * or libruby.
123
+ *
124
+ * The page has to be located in a 32bit window from the Ruby code so that
125
+ * jump and call instructions can redirect execution there.
126
+ *
127
+ * This function returns the address of the page found or NULL if no page was
128
+ * found.
129
+ */
58
130
  static void *
59
- do_bin_allocate_page(void *cookie)
131
+ do_bin_allocate_page(struct elf_info *info)
60
132
  {
61
133
  void * ret = NULL, *addr = NULL;
62
- struct elf_info *info = cookie;
63
- uint16_t max = ~0, count = 0;
134
+ uint16_t count = 0;
64
135
 
65
136
  if (!info)
66
137
  return NULL;
67
138
 
68
- if (has_libruby) {
139
+ if (libruby) {
140
+ /* There is a libruby. Start at the end of the text segment and search for
141
+ * a page.
142
+ */
69
143
  addr = info->text_segment + info->text_segment_len;
70
- for (; count < max;addr += pagesize, count += pagesize) {
144
+ for (; count < UINT_MAX; addr += pagesize, count += pagesize) {
71
145
  ret = mmap(addr, pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
72
146
  if (ret != MAP_FAILED) {
73
147
  memset(ret, 0x90, pagesize);
@@ -75,34 +149,69 @@ do_bin_allocate_page(void *cookie)
75
149
  }
76
150
  }
77
151
  } else {
152
+ /* if there is no libruby, use the linux specific MAP_32BIT flag which will
153
+ * grab a page in the lower 4gb of the address space.
154
+ */
155
+ assert((size_t)info->text_segment <= UINT_MAX);
78
156
  return mmap(NULL, pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE|MAP_32BIT, -1, 0);
79
157
  }
80
158
 
81
159
  return NULL;
82
160
  }
83
161
 
162
+ /*
163
+ * bin_allocate_page - allocate a page suitable for holding stage 2 trampolines
164
+ *
165
+ * This function is just a wrapper which passes through some internal state.
166
+ */
84
167
  void *
85
168
  bin_allocate_page()
86
169
  {
87
170
  return do_bin_allocate_page(ruby_info);
88
171
  }
89
172
 
173
+ /*
174
+ * get_plt_addr - architecture specific PLT entry retrieval
175
+ *
176
+ * Given the internal data and an index, this function returns the address of
177
+ * the PLT entry at that address.
178
+ *
179
+ * A PLT entry takes the form:
180
+ *
181
+ * jmpq *0xfeedface(%rip)
182
+ * pushq $0xaa
183
+ * jmpq 17110
184
+ */
90
185
  static inline GElf_Addr
91
- arch_plt_sym_val(struct elf_info *info, size_t ndx) {
186
+ get_plt_addr(struct elf_info *info, size_t ndx) {
187
+ assert(info != NULL);
92
188
  return info->base_addr + info->plt_addr + (ndx + 1) * 16;
93
189
  }
94
190
 
191
+ /*
192
+ * find_got_addr - find the global offset table entry for specific symbol name.
193
+ *
194
+ * Given:
195
+ * - syname - the symbol name
196
+ * - info - internal information about the ELF object to search
197
+ *
198
+ * This function searches the .rela.plt section of an ELF binary, searcing for
199
+ * entries that match the symbol name passed in. If one is found, the address
200
+ * of corresponding entry in .plt is returned.
201
+ */
95
202
  static void *
96
- find_got_addr(char *symname, void *cookie)
203
+ find_plt_addr(const char *symname, struct elf_info *info)
97
204
  {
205
+ assert(symname != NULL);
206
+
98
207
  size_t i = 0;
99
- struct elf_info *info = cookie;
100
208
 
101
- if (cookie == NULL) {
209
+ if (info == NULL) {
102
210
  info = ruby_info;
103
211
  }
104
212
 
105
- for (i = 0; i < info->relplt_count; ++i) {
213
+ /* Search through each of the .rela.plt entries */
214
+ for (i = 0; i < info->relplt_count; i++) {
106
215
  GElf_Rela rela;
107
216
  GElf_Sym sym;
108
217
  GElf_Addr addr;
@@ -118,8 +227,12 @@ find_got_addr(char *symname, void *cookie)
118
227
  return NULL;
119
228
 
120
229
  name = info->dynstr + sym.st_name;
230
+
231
+ /* The name matches the name of the symbol passed in, so get the PLT entry
232
+ * address and return it.
233
+ */
121
234
  if (strcmp(symname, name) == 0) {
122
- addr = arch_plt_sym_val(info, i);
235
+ addr = get_plt_addr(info, i);
123
236
  return (void *)addr;
124
237
  }
125
238
  }
@@ -128,14 +241,33 @@ find_got_addr(char *symname, void *cookie)
128
241
  return NULL;
129
242
  }
130
243
 
244
+ /*
245
+ * do_bin_find_symbol - internal symbol lookup function.
246
+ *
247
+ * Given:
248
+ * - sym - the symbol name to look up
249
+ * - size - an optional out argument holding the size of the symbol
250
+ * - elf - an elf information structure
251
+ *
252
+ * This function will return the address of the symbol (setting size if desired)
253
+ * or NULL if nothing can be found.
254
+ */
131
255
  static void *
132
- do_bin_find_symbol(char *sym, size_t *size, struct elf_info *elf)
256
+ do_bin_find_symbol(const char *sym, size_t *size, struct elf_info *elf)
133
257
  {
134
258
  char *name = NULL;
135
259
 
260
+ assert(sym != NULL);
261
+ assert(elf != NULL);
262
+
263
+ assert(elf->symtab_data != NULL);
264
+ assert(elf->symtab_data->d_buf != NULL);
265
+
136
266
  ElfW(Sym) *esym = (ElfW(Sym)*) elf->symtab_data->d_buf;
137
267
  ElfW(Sym) *lastsym = (ElfW(Sym)*) ((char*) elf->symtab_data->d_buf + elf->symtab_data->d_size);
138
268
 
269
+ assert(esym <= lastsym);
270
+
139
271
  for (; esym < lastsym; esym++){
140
272
  /* ignore weak/numeric/empty symbols */
141
273
  if ((esym->st_value == 0) ||
@@ -144,6 +276,9 @@ do_bin_find_symbol(char *sym, size_t *size, struct elf_info *elf)
144
276
  continue;
145
277
 
146
278
  name = elf_strptr(elf->elf, elf->symtab_shdr.sh_link, (size_t)esym->st_name);
279
+
280
+ assert(name != NULL);
281
+
147
282
  if (strcmp(name, sym) == 0) {
148
283
  if (size) {
149
284
  *size = esym->st_size;
@@ -154,38 +289,64 @@ do_bin_find_symbol(char *sym, size_t *size, struct elf_info *elf)
154
289
  return NULL;
155
290
  }
156
291
 
292
+ /*
293
+ * bin_find_symbol - find the address of a given symbol and set its size if
294
+ * desired.
295
+ *
296
+ * This function is just a wrapper for the internal symbol lookup function.
297
+ */
157
298
  void *
158
- bin_find_symbol(char *sym, size_t *size)
299
+ bin_find_symbol(const char *sym, size_t *size)
159
300
  {
160
301
  return do_bin_find_symbol(sym, size, ruby_info);
161
302
  }
162
303
 
163
- void
164
- bin_update_image(int entry, char *trampee, struct tramp_st2_entry *tramp)
304
+ /*
305
+ * bin_update_image - update the ruby binary image in memory.
306
+ *
307
+ * Given -
308
+ * trampee - the name of the symbol to hook
309
+ * tramp - the stage 2 trampoline entry
310
+ *
311
+ * This function will update the ruby binary image so that all calls to trampee
312
+ * will be routed to tramp.
313
+ *
314
+ * Returns 0 on success
315
+ */
316
+ int
317
+ bin_update_image(const char *trampee, struct tramp_st2_entry *tramp)
165
318
  {
166
319
  void *trampee_addr = NULL;
167
320
 
168
- if (!has_libruby) {
321
+ assert(trampee != NULL);
322
+ assert(tramp != NULL);
323
+ assert(tramp->addr != NULL);
324
+
325
+ if (!libruby) {
169
326
  unsigned char *byte = ruby_info->text_segment;
170
327
  trampee_addr = bin_find_symbol(trampee, NULL);
171
328
  size_t count = 0;
172
329
  int num = 0;
173
330
 
331
+ assert(byte != NULL);
332
+ assert(trampee_addr != NULL);
333
+
174
334
  for(; count < ruby_info->text_segment_len; byte++, count++) {
175
- if (arch_insert_st1_tramp(byte, trampee_addr, tramp)) {
176
- // printf("tramped %x\n", byte);
335
+ if (arch_insert_st1_tramp(byte, trampee_addr, tramp) == 0) {
177
336
  num++;
178
337
  }
179
338
  }
180
339
  } else {
181
- trampee_addr = find_got_addr(trampee, NULL);
182
- arch_overwrite_got(trampee_addr, tramp->addr);
340
+ trampee_addr = find_plt_addr(trampee, NULL);
341
+ assert(trampee_addr != NULL);
342
+ overwrite_got(trampee_addr, tramp->addr);
183
343
  }
344
+ return 0;
184
345
  }
185
346
 
186
347
 
187
348
  static Dwarf_Die
188
- check_die(Dwarf_Die die, char *search, Dwarf_Half type)
349
+ check_die(Dwarf_Die die, const char *search, Dwarf_Half type)
189
350
  {
190
351
  char *name = 0;
191
352
  Dwarf_Error error = 0;
@@ -217,7 +378,7 @@ check_die(Dwarf_Die die, char *search, Dwarf_Half type)
217
378
  }
218
379
 
219
380
  static Dwarf_Die
220
- search_dies(Dwarf_Die die, char *name, Dwarf_Half type)
381
+ search_dies(Dwarf_Die die, const char *name, Dwarf_Half type)
221
382
  {
222
383
  int res = DW_DLV_ERROR;
223
384
  Dwarf_Die cur_die=die;
@@ -269,7 +430,7 @@ search_dies(Dwarf_Die die, char *name, Dwarf_Half type)
269
430
  }
270
431
 
271
432
  static Dwarf_Die
272
- find_die(char *name, Dwarf_Half type)
433
+ find_die(const char *name, Dwarf_Half type)
273
434
  {
274
435
  Dwarf_Die ret = 0;
275
436
  Dwarf_Unsigned cu_header_length = 0;
@@ -328,32 +489,37 @@ find_die(char *name, Dwarf_Half type)
328
489
  return ret ? ret : 0;
329
490
  }
330
491
 
492
+ /*
493
+ * has_libruby - check if this ruby binary is linked against libruby.so
494
+ *
495
+ * This function checks if the curreny binary is linked against libruby. If
496
+ * so, it sets libruby = 1, and fill internal state in the elf_info structure.
497
+ *
498
+ * Returns 1 if this binary is linked to libruby.so, 0 if not.
499
+ */
331
500
  static int
332
- bin_has_libruby(struct elf_info *cookie)
501
+ has_libruby(struct elf_info *lib)
333
502
  {
334
503
  struct link_map *map = _r_debug.r_map;
335
- struct elf_info *lib = cookie;
336
-
337
- if (has_libruby != -1) {
338
- has_libruby = 0;
339
- while (map) {
340
- if (strstr(map->l_name, "libruby.so")) {
341
- if (lib) {
342
- lib->base_addr = (GElf_Addr)map->l_addr;
343
- lib->filename = strdup(map->l_name);
344
- }
345
- has_libruby = 1;
346
- break;
504
+
505
+ libruby = 0;
506
+ while (map) {
507
+ if (strstr(map->l_name, "libruby.so")) {
508
+ if (lib) {
509
+ lib->base_addr = (GElf_Addr)map->l_addr;
510
+ lib->filename = strdup(map->l_name);
347
511
  }
348
- map = map->l_next;
512
+ libruby = 1;
513
+ break;
349
514
  }
515
+ map = map->l_next;
350
516
  }
351
517
 
352
- return has_libruby;
518
+ return libruby;
353
519
  }
354
520
 
355
- int
356
- bin_type_size(char *name)
521
+ size_t
522
+ bin_type_size(const char *name)
357
523
  {
358
524
  Dwarf_Unsigned size = 0;
359
525
  Dwarf_Error error;
@@ -366,14 +532,14 @@ bin_type_size(char *name)
366
532
  res = dwarf_bytesize(die, &size, &error);
367
533
  dwarf_dealloc(dwrf,die,DW_DLA_DIE);
368
534
  if (res == DW_DLV_OK)
369
- return (int)size;
535
+ return size;
370
536
  }
371
537
 
372
- return -1;
538
+ return 0;
373
539
  }
374
540
 
375
541
  int
376
- bin_type_member_offset(char *type, char *member)
542
+ bin_type_member_offset(const char *type, const char *member)
377
543
  {
378
544
  Dwarf_Error error;
379
545
  int res = DW_DLV_ERROR;
@@ -403,12 +569,21 @@ bin_type_member_offset(char *type, char *member)
403
569
  return -1;
404
570
  }
405
571
 
572
+ /*
573
+ * open_elf - Opens a file from disk and gets the elf reader started.
574
+ *
575
+ * Given a filename, this function attempts to open the file and start the
576
+ * elf reader.
577
+ *
578
+ * Returns an Elf object.
579
+ */
406
580
  static Elf *
407
581
  open_elf(const char *filename)
408
582
  {
409
583
  Elf *ret = NULL;
410
584
  int fd = 0;
411
585
 
586
+ assert(filename != NULL);
412
587
  if (elf_version(EV_CURRENT) == EV_NONE)
413
588
  errx(EX_SOFTWARE, "ELF library initialization failed: %s",
414
589
  elf_errmsg(-1));
@@ -423,30 +598,48 @@ open_elf(const char *filename)
423
598
  if (elf_kind(ret) != ELF_K_ELF)
424
599
  errx(EX_DATAERR, "%s is not an ELF object.", filename);
425
600
 
601
+ assert(ret != NULL);
426
602
  return ret;
427
603
  }
428
604
 
605
+ /*
606
+ * dissect_elf - Parses and stores internal data about an ELF object.
607
+ *
608
+ * Given an elf_info structure, this function will attempt to parse the object
609
+ * and store important state needed to rewrite the object later.
610
+ */
429
611
  static void
430
612
  dissect_elf(struct elf_info *info)
431
613
  {
614
+ assert(info != NULL);
615
+
432
616
  size_t shstrndx = 0;
433
617
  Elf *elf = info->elf;
434
618
  Elf_Scn *scn = NULL;
435
619
  GElf_Shdr shdr;
436
620
  size_t j = 0;
437
621
 
438
- if (elf_getshdrstrndx(elf, &shstrndx) == -1)
622
+ if (elf_getshdrstrndx(elf, &shstrndx) == -1) {
439
623
  errx(EX_SOFTWARE, "getshstrndx() failed: %s.", elf_errmsg(-1));
624
+ }
440
625
 
441
- if (gelf_getehdr(elf, &(info->ehdr)) == NULL)
626
+ if (gelf_getehdr(elf, &(info->ehdr)) == NULL) {
442
627
  errx(EX_SOFTWARE, "Couldn't get elf header.");
628
+ }
443
629
 
630
+ /* search each ELF header and store important data for each header... */
444
631
  while ((scn = elf_nextscn(elf, scn)) != NULL) {
445
632
  if (gelf_getshdr(scn, &shdr) != &shdr)
446
633
  errx(EX_SOFTWARE, "getshdr() failed: %s.",
447
634
  elf_errmsg(-1));
448
635
 
449
- /* if there is a dynamic section ... */
636
+
637
+ /*
638
+ * The .dynamic section contains entries that are important to memprof.
639
+ * Specifically, the .rela.plt section information. The .rela.plt section
640
+ * indexes the .plt, which will be important for hooking functions in
641
+ * shared objects.
642
+ */
450
643
  if (shdr.sh_type == SHT_DYNAMIC) {
451
644
  Elf_Data *data;
452
645
  data = elf_getdata(scn, NULL);
@@ -465,7 +658,12 @@ dissect_elf(struct elf_info *info)
465
658
  info->plt_size = dyn.d_un.d_val;
466
659
  }
467
660
  }
468
- } else if (shdr.sh_type == SHT_DYNSYM) {
661
+ }
662
+ /*
663
+ * The .dynsym section has useful pieces, too, like the dynamic symbol
664
+ * table. This table is used when walking the .rela.plt section.
665
+ */
666
+ else if (shdr.sh_type == SHT_DYNSYM) {
469
667
  Elf_Data *data;
470
668
 
471
669
  info->dynsym = elf_getdata(scn, NULL);
@@ -487,17 +685,29 @@ dissect_elf(struct elf_info *info)
487
685
  "Couldn't get .dynstr data");
488
686
 
489
687
  info->dynstr = data->d_buf;
490
- } else if (shdr.sh_type == SHT_PROGBITS &&
688
+ }
689
+ /*
690
+ * Pull out information (start address and length) of the .text section.
691
+ */
692
+ else if (shdr.sh_type == SHT_PROGBITS &&
491
693
  (shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)) &&
492
694
  strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".text") == 0) {
493
695
 
494
696
  info->text_segment = (void *)shdr.sh_addr + info->base_addr;
495
697
  info->text_segment_len = shdr.sh_size;
496
- } else if (shdr.sh_type == SHT_PROGBITS) {
698
+ }
699
+ /*
700
+ * Pull out information (start address) of the .plt section.
701
+ */
702
+ else if (shdr.sh_type == SHT_PROGBITS) {
497
703
  if (strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".plt") == 0) {
498
704
  info->plt_addr = shdr.sh_addr;
499
705
  }
500
- } else if (shdr.sh_type == SHT_SYMTAB) {
706
+ }
707
+ /*
708
+ * The symbol table is also needed for bin_find_symbol
709
+ */
710
+ else if (shdr.sh_type == SHT_SYMTAB) {
501
711
  info->symtab_shdr = shdr;
502
712
  if ((info->symtab_data = elf_getdata(scn, info->symtab_data)) == NULL ||
503
713
  info->symtab_data->d_size == 0) {
@@ -507,11 +717,15 @@ dissect_elf(struct elf_info *info)
507
717
  }
508
718
  }
509
719
 
720
+ /* If this object has no symbol table there's nothing else to do but fail */
510
721
  if (!info->symtab_data) {
511
722
  errx(EX_DATAERR, "binary is stripped. memprof only works on binaries that are not stripped!");
512
723
  }
513
724
 
514
725
 
726
+ /*
727
+ * Walk the sections, pull out, and store the .plt section
728
+ */
515
729
  for (j = 1; j < info->ehdr.e_shnum; j++) {
516
730
  scn = elf_getscn(elf, j);
517
731
  if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
@@ -533,6 +747,11 @@ dissect_elf(struct elf_info *info)
533
747
  return;
534
748
  }
535
749
 
750
+ /*
751
+ * bin_init - initialize the binary parsing/modification layer.
752
+ *
753
+ * This function starts the elf parser and sets up internal state.
754
+ */
536
755
  void
537
756
  bin_init()
538
757
  {
@@ -540,11 +759,12 @@ bin_init()
540
759
 
541
760
  ruby_info = calloc(1, sizeof(*ruby_info));
542
761
 
543
- if (!ruby_info)
544
- return;
762
+ if (!ruby_info) {
763
+ errx(EX_UNAVAILABLE, "Unable to allocate memory to start binary parsing layer");
764
+ }
545
765
 
546
- if (!bin_has_libruby(ruby_info)) {
547
- ruby_info->filename = strdup("/proc/self/exe");
766
+ if (!has_libruby(ruby_info)) {
767
+ ruby_info->filename = "/proc/self/exe";
548
768
  ruby_info->base_addr = 0;
549
769
  }
550
770