memprof 0.2.5 → 0.2.6

Sign up to get free protection for your applications and to get access to all the features.
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