memprof 0.2.7 → 0.2.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README +15 -6
- data/ext/bin_api.h +1 -9
- data/ext/elf.c +398 -81
- data/ext/extconf.rb +2 -0
- data/ext/mach.c +97 -86
- data/ext/memprof.c +267 -167
- data/ext/tramp.c +143 -0
- data/ext/tramp.h +23 -0
- data/ext/util.c +72 -0
- data/ext/util.h +40 -0
- data/ext/x86_64.c +2 -2
- data/lib/memprof/usr2.rb +1 -0
- data/memprof.gemspec +3 -3
- metadata +17 -5
data/README
CHANGED
@@ -28,8 +28,6 @@ Memprof.stats also takes an (optional) file name to write the output to a file.
|
|
28
28
|
|
29
29
|
Supported systems
|
30
30
|
=================
|
31
|
-
**This only works on unstripped binaries.**
|
32
|
-
|
33
31
|
Currently supporting:
|
34
32
|
|
35
33
|
Linux (enable-shared AND disable-shared):
|
@@ -40,19 +38,28 @@ Currently supporting:
|
|
40
38
|
x86_64 builds of Ruby Enterprise Edition 1.8.6/1.8.7
|
41
39
|
x86_64 builds of MRI Ruby
|
42
40
|
|
41
|
+
Support for unstripped binaries and stripped binaries if (and only if) the
|
42
|
+
symbol files live in /usr/lib/debug/
|
43
|
+
|
44
|
+
You can get symbol files for system Rubies by installing -dbg packages.
|
45
|
+
|
46
|
+
For example, on ubuntu: apt-get install libruby1.8-dbg
|
47
|
+
|
48
|
+
Snow Leopard:
|
49
|
+
x86_64 builds of Ruby Enterprise Edition 1.8.7
|
50
|
+
x86_64 builds of MRI Ruby (enable-shared and disable-shared)
|
51
|
+
Unstripped binaries only!
|
52
|
+
|
43
53
|
Experimental (somewhat broken) support:
|
44
54
|
|
45
55
|
Linux and OSX:
|
46
56
|
i386/i686 support.
|
47
57
|
|
48
58
|
Snow Leopard:
|
49
|
-
OSX system Ruby, distributed with Snow Leopard
|
59
|
+
OSX system Ruby, distributed with Snow Leopard
|
50
60
|
|
51
61
|
Coming soon:
|
52
62
|
|
53
|
-
Linux:
|
54
|
-
Tracking object allocationns in C extensions.
|
55
|
-
|
56
63
|
Official support for Ruby 1.9
|
57
64
|
Official support for i386/i686
|
58
65
|
|
@@ -61,3 +68,5 @@ CREDITS
|
|
61
68
|
Jake Douglas for the Mach O/snow leopard support.
|
62
69
|
|
63
70
|
Aman Gupta for various bug fixes and other cleanup.
|
71
|
+
|
72
|
+
Rob Benson for 1.9 support and cleanup.
|
data/ext/bin_api.h
CHANGED
@@ -3,14 +3,6 @@
|
|
3
3
|
#include <stddef.h>
|
4
4
|
#include <stdint.h>
|
5
5
|
|
6
|
-
/*
|
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
|
-
*/
|
12
|
-
extern size_t pagesize;
|
13
|
-
|
14
6
|
/*
|
15
7
|
* Some useful foward declarations for function protoypes here and elsewhere.
|
16
8
|
*/
|
@@ -100,5 +92,5 @@ bin_type_member_offset(const char *type, const char *member);
|
|
100
92
|
* Returns 0 on success.
|
101
93
|
*/
|
102
94
|
int
|
103
|
-
bin_update_image(const char *
|
95
|
+
bin_update_image(const char *trampee, struct tramp_st2_entry *tramp);
|
104
96
|
#endif
|
data/ext/elf.c
CHANGED
@@ -2,14 +2,17 @@
|
|
2
2
|
#define _GNU_SOURCE
|
3
3
|
#include "bin_api.h"
|
4
4
|
#include "arch.h"
|
5
|
+
#include "util.h"
|
5
6
|
|
6
7
|
#include <assert.h>
|
7
8
|
#include <dwarf.h>
|
8
9
|
#include <err.h>
|
9
10
|
#include <error.h>
|
11
|
+
#include <errno.h>
|
10
12
|
#include <fcntl.h>
|
11
13
|
#include <libdwarf.h>
|
12
14
|
#include <libelf/gelf.h>
|
15
|
+
#include <libgen.h>
|
13
16
|
#include <limits.h>
|
14
17
|
#include <link.h>
|
15
18
|
#include <stdio.h>
|
@@ -19,10 +22,18 @@
|
|
19
22
|
#include <unistd.h>
|
20
23
|
|
21
24
|
#include <sys/mman.h>
|
25
|
+
#include <sys/stat.h>
|
26
|
+
#include <sys/types.h>
|
27
|
+
|
28
|
+
extern struct memprof_config memprof_config;
|
22
29
|
|
23
30
|
/* The size of a PLT entry */
|
24
31
|
#define PLT_ENTRY_SZ (16)
|
25
32
|
|
33
|
+
/* The system-wide debug symbol directory */
|
34
|
+
/* XXX this should be set via extconf and not hardcoded */
|
35
|
+
#define DEBUGDIR "/usr/lib/debug"
|
36
|
+
|
26
37
|
/* Keep track of whether this ruby is built with a shared library or not */
|
27
38
|
static int libruby = 0;
|
28
39
|
|
@@ -32,6 +43,7 @@ static Dwarf_Debug dwrf = NULL;
|
|
32
43
|
static struct elf_info *ruby_info = NULL;
|
33
44
|
|
34
45
|
struct elf_info {
|
46
|
+
int fd;
|
35
47
|
Elf *elf;
|
36
48
|
|
37
49
|
GElf_Addr base_addr;
|
@@ -48,6 +60,10 @@ struct elf_info {
|
|
48
60
|
size_t plt_size;
|
49
61
|
size_t plt_count;
|
50
62
|
|
63
|
+
GElf_Addr debuglink_addr;
|
64
|
+
size_t debuglink_sz;
|
65
|
+
Elf_Data *debuglink_data;
|
66
|
+
|
51
67
|
GElf_Ehdr ehdr;
|
52
68
|
|
53
69
|
Elf_Data *dynsym;
|
@@ -58,8 +74,28 @@ struct elf_info {
|
|
58
74
|
Elf_Data *symtab_data;
|
59
75
|
|
60
76
|
const char *filename;
|
77
|
+
|
78
|
+
struct elf_info *debug_data;
|
61
79
|
};
|
62
80
|
|
81
|
+
/* These callback return statuses are used to tell the invoker of the callback
|
82
|
+
* (walk_linkmap) whether to continue walking or bail out.
|
83
|
+
*/
|
84
|
+
typedef enum {
|
85
|
+
CB_CONTINUE,
|
86
|
+
CB_EXIT,
|
87
|
+
} linkmap_cb_status;
|
88
|
+
|
89
|
+
/* A callback type that specifies a function that can be fired on each link map
|
90
|
+
* entry.
|
91
|
+
*/
|
92
|
+
typedef linkmap_cb_status (*linkmap_cb)(struct link_map *, void *);
|
93
|
+
|
94
|
+
static void open_elf(struct elf_info *info);
|
95
|
+
static int dissect_elf(struct elf_info *info, int find_debug);
|
96
|
+
static void *find_plt_addr(const char *symname, struct elf_info *info);
|
97
|
+
static void walk_linkmap(linkmap_cb cb, void *data);
|
98
|
+
|
63
99
|
/*
|
64
100
|
* plt_entry - procedure linkage table entry
|
65
101
|
*
|
@@ -106,7 +142,7 @@ get_got_addr(struct plt_entry *plt)
|
|
106
142
|
* in the GOT that the PLT entry uses with the address in tramp.
|
107
143
|
*/
|
108
144
|
static void
|
109
|
-
overwrite_got(void *plt, void *tramp)
|
145
|
+
overwrite_got(void *plt, const void *tramp)
|
110
146
|
{
|
111
147
|
assert(plt != NULL);
|
112
148
|
assert(tramp != NULL);
|
@@ -114,6 +150,79 @@ overwrite_got(void *plt, void *tramp)
|
|
114
150
|
return;
|
115
151
|
}
|
116
152
|
|
153
|
+
struct plt_hook_data {
|
154
|
+
const char *sym;
|
155
|
+
const void *tramp;
|
156
|
+
};
|
157
|
+
|
158
|
+
static linkmap_cb_status
|
159
|
+
hook_func_cb(struct link_map *map, void *data)
|
160
|
+
{
|
161
|
+
struct plt_hook_data *hook_data = data;
|
162
|
+
struct elf_info curr_lib;
|
163
|
+
void *trampee_addr = NULL;
|
164
|
+
|
165
|
+
/* skip a few things we don't care about */
|
166
|
+
if (strstr(map->l_name, "linux-vdso")) {
|
167
|
+
dbg_printf("found vdso (skipping): %s\n", map->l_name);
|
168
|
+
return CB_CONTINUE;
|
169
|
+
} else if (strstr(map->l_name, "ld-linux")) {
|
170
|
+
dbg_printf("found ld-linux (skipping): %s\n", map->l_name);
|
171
|
+
return CB_CONTINUE;
|
172
|
+
} else if (strstr(map->l_name, "libruby")) {
|
173
|
+
dbg_printf("found libruby (skipping): %s\n", map->l_name);
|
174
|
+
return CB_CONTINUE;
|
175
|
+
} else if (strstr(map->l_name, "memprof")) {
|
176
|
+
dbg_printf("found memprof (skipping): %s\n", map->l_name);
|
177
|
+
return CB_CONTINUE;
|
178
|
+
} else if (!map->l_name || map->l_name[0] == '\0') {
|
179
|
+
dbg_printf("found an empty string (skipping)\n");
|
180
|
+
return CB_CONTINUE;
|
181
|
+
}
|
182
|
+
|
183
|
+
memset(&curr_lib, 0, sizeof(curr_lib));
|
184
|
+
dbg_printf("trying to open elf object: %s\n", map->l_name);
|
185
|
+
curr_lib.filename = map->l_name;
|
186
|
+
open_elf(&curr_lib);
|
187
|
+
|
188
|
+
if (curr_lib.elf == NULL) {
|
189
|
+
dbg_printf("opening the elf object (%s) failed! (skipping)\n", map->l_name);
|
190
|
+
return CB_CONTINUE;
|
191
|
+
}
|
192
|
+
|
193
|
+
curr_lib.base_addr = map->l_addr;
|
194
|
+
curr_lib.filename = map->l_name;
|
195
|
+
|
196
|
+
if (dissect_elf(&curr_lib, 0) == 2) {
|
197
|
+
dbg_printf("elf file, %s hit an unrecoverable error (skipping)\n", map->l_name);
|
198
|
+
elf_end(curr_lib.elf);
|
199
|
+
close(curr_lib.fd);
|
200
|
+
return CB_CONTINUE;
|
201
|
+
}
|
202
|
+
|
203
|
+
dbg_printf("dissected the elf file: %s, base: %lx\n",
|
204
|
+
curr_lib.filename, (unsigned long)curr_lib.base_addr);
|
205
|
+
|
206
|
+
if ((trampee_addr = find_plt_addr(hook_data->sym, &curr_lib)) != NULL) {
|
207
|
+
dbg_printf("found: %s @ %p\n", hook_data->sym, trampee_addr);
|
208
|
+
overwrite_got(trampee_addr, hook_data->tramp);
|
209
|
+
}
|
210
|
+
|
211
|
+
elf_end(curr_lib.elf);
|
212
|
+
close(curr_lib.fd);
|
213
|
+
return CB_CONTINUE;
|
214
|
+
}
|
215
|
+
|
216
|
+
static void
|
217
|
+
hook_required_objects(void *tramp)
|
218
|
+
{
|
219
|
+
struct plt_hook_data data;
|
220
|
+
data.sym = "rb_newobj";
|
221
|
+
data.tramp = tramp;
|
222
|
+
walk_linkmap(hook_func_cb, &data);
|
223
|
+
return;
|
224
|
+
}
|
225
|
+
|
117
226
|
/*
|
118
227
|
* do_bin_allocate_page - internal page allocation routine
|
119
228
|
*
|
@@ -131,7 +240,7 @@ static void *
|
|
131
240
|
do_bin_allocate_page(struct elf_info *info)
|
132
241
|
{
|
133
242
|
void * ret = NULL, *addr = NULL;
|
134
|
-
|
243
|
+
uint32_t count = 0;
|
135
244
|
|
136
245
|
if (!info)
|
137
246
|
return NULL;
|
@@ -141,10 +250,10 @@ do_bin_allocate_page(struct elf_info *info)
|
|
141
250
|
* a page.
|
142
251
|
*/
|
143
252
|
addr = info->text_segment + info->text_segment_len;
|
144
|
-
for (; count <
|
145
|
-
ret = mmap(addr, pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
|
253
|
+
for (; count < USHRT_MAX; addr += memprof_config.pagesize, count += memprof_config.pagesize) {
|
254
|
+
ret = mmap(addr, memprof_config.pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
|
146
255
|
if (ret != MAP_FAILED) {
|
147
|
-
memset(ret, 0x90, pagesize);
|
256
|
+
memset(ret, 0x90, memprof_config.pagesize);
|
148
257
|
return ret;
|
149
258
|
}
|
150
259
|
}
|
@@ -153,7 +262,7 @@ do_bin_allocate_page(struct elf_info *info)
|
|
153
262
|
* grab a page in the lower 4gb of the address space.
|
154
263
|
*/
|
155
264
|
assert((size_t)info->text_segment <= UINT_MAX);
|
156
|
-
return mmap(NULL, pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE|MAP_32BIT, -1, 0);
|
265
|
+
return mmap(NULL, memprof_config.pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE|MAP_32BIT, -1, 0);
|
157
266
|
}
|
158
267
|
|
159
268
|
return NULL;
|
@@ -185,7 +294,8 @@ bin_allocate_page()
|
|
185
294
|
static inline GElf_Addr
|
186
295
|
get_plt_addr(struct elf_info *info, size_t ndx) {
|
187
296
|
assert(info != NULL);
|
188
|
-
|
297
|
+
dbg_printf("file: %s, base: %lx\n", info->filename, (unsigned long)info->base_addr);
|
298
|
+
return info->base_addr + info->plt_addr + (ndx + 1) * PLT_ENTRY_SZ;
|
189
299
|
}
|
190
300
|
|
191
301
|
/*
|
@@ -277,9 +387,7 @@ do_bin_find_symbol(const char *sym, size_t *size, struct elf_info *elf)
|
|
277
387
|
|
278
388
|
name = elf_strptr(elf->elf, elf->symtab_shdr.sh_link, (size_t)esym->st_name);
|
279
389
|
|
280
|
-
|
281
|
-
|
282
|
-
if (strcmp(name, sym) == 0) {
|
390
|
+
if (name && strcmp(name, sym) == 0) {
|
283
391
|
if (size) {
|
284
392
|
*size = esym->st_size;
|
285
393
|
}
|
@@ -393,6 +501,13 @@ bin_update_image(const char *trampee, struct tramp_st2_entry *tramp)
|
|
393
501
|
assert(trampee_addr != NULL);
|
394
502
|
overwrite_got(trampee_addr, tramp->addr);
|
395
503
|
}
|
504
|
+
|
505
|
+
if (strcmp("rb_newobj", trampee) == 0) {
|
506
|
+
dbg_printf("Trying to hook rb_newobj in other libraries...\n");
|
507
|
+
hook_required_objects(tramp->addr);
|
508
|
+
dbg_printf("Done searching other libraries for rb_newobj!\n");
|
509
|
+
}
|
510
|
+
|
396
511
|
return 0;
|
397
512
|
}
|
398
513
|
|
@@ -542,31 +657,80 @@ find_die(const char *name, Dwarf_Half type)
|
|
542
657
|
}
|
543
658
|
|
544
659
|
/*
|
545
|
-
*
|
660
|
+
* find_libruby_cb - a callback which attempts to locate libruby in the linkmap
|
546
661
|
*
|
547
|
-
* This
|
548
|
-
*
|
662
|
+
* This callback is fired from walk_linkmap for each item on the linkmap. This
|
663
|
+
* function searchs for libruby and stores data about it in the object passed
|
664
|
+
* through.
|
549
665
|
*
|
550
|
-
*
|
666
|
+
* This function return CB_EXIT once libruby is found to terminate the link map
|
667
|
+
* walker. If libruby isn't found, this function returns CB_CONTINUE.
|
551
668
|
*/
|
552
|
-
static
|
553
|
-
|
669
|
+
static linkmap_cb_status
|
670
|
+
find_libruby_cb(struct link_map *map, void *data)
|
671
|
+
{
|
672
|
+
struct elf_info *lib = data;
|
673
|
+
|
674
|
+
assert(map != NULL);
|
675
|
+
assert(data != NULL);
|
676
|
+
|
677
|
+
if (strstr(map->l_name, "libruby")) {
|
678
|
+
dbg_printf("Found a libruby.so!\n");
|
679
|
+
if (lib) {
|
680
|
+
lib->base_addr = (GElf_Addr)map->l_addr;
|
681
|
+
lib->filename = strdup(map->l_name);
|
682
|
+
}
|
683
|
+
libruby = 1;
|
684
|
+
return CB_EXIT;
|
685
|
+
}
|
686
|
+
return CB_CONTINUE;
|
687
|
+
}
|
688
|
+
|
689
|
+
/*
|
690
|
+
* walk_linkmap - walk the linkmap firing a callback along the way.
|
691
|
+
*
|
692
|
+
* Given:
|
693
|
+
* - cb - a callback function
|
694
|
+
* - data - and any private data to pass through (optional)
|
695
|
+
*
|
696
|
+
* This function will crawl the linkmap and fire the callback on each item
|
697
|
+
* found.
|
698
|
+
*
|
699
|
+
* If the callback returns CB_CONTINUE, this function will move to the next
|
700
|
+
* (if any) item in the link map.
|
701
|
+
*
|
702
|
+
* If the callback returns CB_EXIT, this function will return immediately.
|
703
|
+
*/
|
704
|
+
static void
|
705
|
+
walk_linkmap(linkmap_cb cb, void *data)
|
554
706
|
{
|
555
707
|
struct link_map *map = _r_debug.r_map;
|
556
708
|
|
557
|
-
|
709
|
+
assert(map);
|
710
|
+
|
558
711
|
while (map) {
|
559
|
-
|
560
|
-
|
561
|
-
lib->base_addr = (GElf_Addr)map->l_addr;
|
562
|
-
lib->filename = strdup(map->l_name);
|
563
|
-
}
|
564
|
-
libruby = 1;
|
712
|
+
dbg_printf("Found a linkmap entry: %s\n", map->l_name);
|
713
|
+
if (cb(map, data) == CB_EXIT)
|
565
714
|
break;
|
566
|
-
}
|
567
715
|
map = map->l_next;
|
568
716
|
}
|
569
717
|
|
718
|
+
return;
|
719
|
+
}
|
720
|
+
|
721
|
+
/*
|
722
|
+
* has_libruby - check if this ruby binary is linked against libruby.so
|
723
|
+
*
|
724
|
+
* This function checks if the curreny binary is linked against libruby. If
|
725
|
+
* so, it sets libruby = 1, and fill internal state in the elf_info structure.
|
726
|
+
*
|
727
|
+
* Returns 1 if this binary is linked to libruby.so, 0 if not.
|
728
|
+
*/
|
729
|
+
static int
|
730
|
+
has_libruby(struct elf_info *lib)
|
731
|
+
{
|
732
|
+
libruby = 0;
|
733
|
+
walk_linkmap(find_libruby_cb, lib);
|
570
734
|
return libruby;
|
571
735
|
}
|
572
736
|
|
@@ -627,31 +791,131 @@ bin_type_member_offset(const char *type, const char *member)
|
|
627
791
|
* Given a filename, this function attempts to open the file and start the
|
628
792
|
* elf reader.
|
629
793
|
*
|
630
|
-
* Returns an
|
794
|
+
* Returns an elf_info object that must be freed by the caller.
|
631
795
|
*/
|
632
|
-
static
|
633
|
-
open_elf(
|
796
|
+
static void
|
797
|
+
open_elf(struct elf_info *info)
|
634
798
|
{
|
635
|
-
Elf *ret = NULL;
|
636
|
-
int fd = 0;
|
637
799
|
|
638
|
-
assert(
|
800
|
+
assert(info != NULL);
|
801
|
+
|
639
802
|
if (elf_version(EV_CURRENT) == EV_NONE)
|
640
|
-
errx(EX_SOFTWARE, "ELF library initialization failed: %s",
|
641
|
-
elf_errmsg(-1));
|
803
|
+
errx(EX_SOFTWARE, "ELF library initialization failed: %s", elf_errmsg(-1));
|
642
804
|
|
643
|
-
if ((fd = open(filename, O_RDONLY, 0)) < 0)
|
644
|
-
err(EX_NOINPUT, "open \%s\" failed", filename);
|
805
|
+
if ((info->fd = open(info->filename, O_RDONLY, 0)) < 0)
|
806
|
+
err(EX_NOINPUT, "open \%s\" failed", info->filename);
|
645
807
|
|
646
|
-
if ((
|
647
|
-
errx(EX_SOFTWARE, "elf_begin() failed: %s.",
|
648
|
-
elf_errmsg(-1));
|
808
|
+
if ((info->elf = elf_begin(info->fd, ELF_C_READ, NULL)) == NULL)
|
809
|
+
errx(EX_SOFTWARE, "elf_begin() failed: %s.", elf_errmsg(-1));
|
649
810
|
|
650
|
-
if (elf_kind(
|
651
|
-
errx(EX_DATAERR, "%s is not an ELF object.", filename);
|
811
|
+
if (elf_kind(info->elf) != ELF_K_ELF)
|
812
|
+
errx(EX_DATAERR, "%s is not an ELF object.", info->filename);
|
652
813
|
|
653
|
-
|
654
|
-
|
814
|
+
return;
|
815
|
+
}
|
816
|
+
|
817
|
+
static char *
|
818
|
+
get_debuglink_info(struct elf_info *elf, unsigned long *crc_out)
|
819
|
+
{
|
820
|
+
char *basename = (char *) elf->debuglink_data->d_buf;
|
821
|
+
unsigned long offset = strlen(basename) + 1;
|
822
|
+
offset = (offset + 3) & ~3;
|
823
|
+
|
824
|
+
memcpy(crc_out, elf->debuglink_data->d_buf + offset, 4);
|
825
|
+
|
826
|
+
return basename;
|
827
|
+
}
|
828
|
+
|
829
|
+
static int
|
830
|
+
verify_debug_checksum(const char *filename, unsigned long crc)
|
831
|
+
{
|
832
|
+
struct stat stat_buf;
|
833
|
+
void *region = NULL;
|
834
|
+
int fd = open(filename, O_RDONLY);
|
835
|
+
unsigned long crc_check = 0;
|
836
|
+
|
837
|
+
if (fd == -1) {
|
838
|
+
dbg_printf("Couldn't open debug file: %s, because: %s\n", filename, strerror(errno));
|
839
|
+
return 1;
|
840
|
+
}
|
841
|
+
|
842
|
+
if (fstat(fd, &stat_buf) == -1) {
|
843
|
+
dbg_printf("Couldn't stat debug file: %s, because: %s\n", filename, strerror(errno));
|
844
|
+
return 1;
|
845
|
+
}
|
846
|
+
|
847
|
+
region = mmap(NULL, stat_buf.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
848
|
+
if (region == MAP_FAILED) {
|
849
|
+
dbg_printf("mapping a section of size: %zd has failed!\n", stat_buf.st_size);
|
850
|
+
return 1;
|
851
|
+
}
|
852
|
+
|
853
|
+
crc_check = gnu_debuglink_crc32(crc_check, region, stat_buf.st_size);
|
854
|
+
dbg_printf("supposed crc: %lx, computed crc: %lx\n", crc, crc_check);
|
855
|
+
|
856
|
+
munmap(region, stat_buf.st_size);
|
857
|
+
close(fd);
|
858
|
+
|
859
|
+
return !(crc == crc_check);
|
860
|
+
}
|
861
|
+
|
862
|
+
static int
|
863
|
+
find_debug_syms(struct elf_info *elf)
|
864
|
+
{
|
865
|
+
/*
|
866
|
+
* XXX TODO perhaps allow users to specify an env var (or something) pointing
|
867
|
+
* to where the debug symbols live?
|
868
|
+
*/
|
869
|
+
unsigned long crc = 0;
|
870
|
+
char *basename = get_debuglink_info(elf, &crc);
|
871
|
+
char *debug_file = NULL, *dir = NULL;
|
872
|
+
char *tmp = strdup(elf->filename);
|
873
|
+
|
874
|
+
assert(basename != NULL);
|
875
|
+
dbg_printf(".gnu_debuglink base file name: %s, crc: %lx\n", basename, crc);
|
876
|
+
|
877
|
+
dir = dirname(tmp);
|
878
|
+
debug_file = malloc(strlen(DEBUGDIR) + strlen(dir) +
|
879
|
+
strlen("/") + strlen(basename) + 1);
|
880
|
+
|
881
|
+
strncat(debug_file, DEBUGDIR, strlen(DEBUGDIR));
|
882
|
+
strncat(debug_file, dir, strlen(dir));
|
883
|
+
strncat(debug_file, "/", strlen("/"));
|
884
|
+
strncat(debug_file, basename, strlen(basename));
|
885
|
+
|
886
|
+
elf->debug_data = calloc(1, sizeof(*elf->debug_data));
|
887
|
+
elf->debug_data->filename = debug_file;
|
888
|
+
|
889
|
+
dbg_printf("Possible debug symbols in: %s\n", elf->debug_data->filename);
|
890
|
+
|
891
|
+
if (verify_debug_checksum(elf->debug_data->filename, crc)) {
|
892
|
+
dbg_printf("Checksum verification of debug file: %s failed!", elf->debug_data->filename);
|
893
|
+
free(debug_file);
|
894
|
+
free(elf->debug_data);
|
895
|
+
return 1;
|
896
|
+
}
|
897
|
+
|
898
|
+
open_elf(elf->debug_data);
|
899
|
+
|
900
|
+
if (dissect_elf(elf->debug_data, 0) != 0) {
|
901
|
+
/* free debug_data */
|
902
|
+
dbg_printf("Dissection of debug data failed!\n");
|
903
|
+
free(debug_file);
|
904
|
+
free(elf->debug_data);
|
905
|
+
return 1;
|
906
|
+
}
|
907
|
+
|
908
|
+
elf->symtab_data = elf->debug_data->symtab_data;
|
909
|
+
elf->symtab_shdr = elf->debug_data->symtab_shdr;
|
910
|
+
|
911
|
+
/* XXX free stuff */
|
912
|
+
/* LEAK elf_end(elf->elf); */
|
913
|
+
|
914
|
+
elf->elf = elf->debug_data->elf;
|
915
|
+
close(elf->debug_data->fd);
|
916
|
+
|
917
|
+
dbg_printf("Finished dissecting debug data\n");
|
918
|
+
return 0;
|
655
919
|
}
|
656
920
|
|
657
921
|
/*
|
@@ -659,32 +923,43 @@ open_elf(const char *filename)
|
|
659
923
|
*
|
660
924
|
* Given an elf_info structure, this function will attempt to parse the object
|
661
925
|
* and store important state needed to rewrite the object later.
|
926
|
+
*
|
927
|
+
* TODO better error handling
|
928
|
+
*
|
929
|
+
* Returns 1 on hard errors.
|
930
|
+
* Returns 2 on recoverable errors (missing symbol table).
|
931
|
+
* Returns 0 on success.
|
662
932
|
*/
|
663
|
-
static
|
664
|
-
dissect_elf(struct elf_info *info)
|
933
|
+
static int
|
934
|
+
dissect_elf(struct elf_info *info, int find_debug)
|
665
935
|
{
|
666
936
|
assert(info != NULL);
|
667
937
|
|
668
|
-
size_t shstrndx = 0;
|
938
|
+
size_t shstrndx = 0, j = 0;
|
669
939
|
Elf *elf = info->elf;
|
670
940
|
Elf_Scn *scn = NULL;
|
671
941
|
GElf_Shdr shdr;
|
672
|
-
|
942
|
+
int ret = 0;
|
673
943
|
|
674
944
|
if (elf_getshdrstrndx(elf, &shstrndx) == -1) {
|
675
|
-
|
945
|
+
dbg_printf("getshstrndx() failed: %s.", elf_errmsg(-1));
|
946
|
+
ret = 1;
|
947
|
+
goto out;
|
676
948
|
}
|
677
949
|
|
678
950
|
if (gelf_getehdr(elf, &(info->ehdr)) == NULL) {
|
679
|
-
|
951
|
+
dbg_printf("Couldn't get elf header.");
|
952
|
+
ret = 1;
|
953
|
+
goto out;
|
680
954
|
}
|
681
955
|
|
682
956
|
/* search each ELF header and store important data for each header... */
|
683
957
|
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
684
|
-
if (gelf_getshdr(scn, &shdr) != &shdr)
|
685
|
-
|
686
|
-
|
687
|
-
|
958
|
+
if (gelf_getshdr(scn, &shdr) != &shdr) {
|
959
|
+
dbg_printf("getshdr() failed: %s.", elf_errmsg(-1));
|
960
|
+
ret = 1;
|
961
|
+
goto out;
|
962
|
+
}
|
688
963
|
|
689
964
|
/*
|
690
965
|
* The .dynamic section contains entries that are important to memprof.
|
@@ -699,8 +974,9 @@ dissect_elf(struct elf_info *info)
|
|
699
974
|
for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
|
700
975
|
GElf_Dyn dyn;
|
701
976
|
if (gelf_getdyn(data, j, &dyn) == NULL) {
|
702
|
-
|
703
|
-
|
977
|
+
dbg_printf("Couldn't get .dynamic data from loaded library.");
|
978
|
+
ret = 1;
|
979
|
+
goto out;
|
704
980
|
}
|
705
981
|
|
706
982
|
if (dyn.d_tag == DT_JMPREL) {
|
@@ -720,21 +996,26 @@ dissect_elf(struct elf_info *info)
|
|
720
996
|
|
721
997
|
info->dynsym = elf_getdata(scn, NULL);
|
722
998
|
info->dynsym_count = shdr.sh_size / shdr.sh_entsize;
|
723
|
-
if (info->dynsym == NULL
|
724
|
-
|
725
|
-
|
726
|
-
|
999
|
+
if (info->dynsym == NULL || elf_getdata(scn, info->dynsym) != NULL) {
|
1000
|
+
dbg_printf("Couldn't get .dynsym data ");
|
1001
|
+
ret = 1;
|
1002
|
+
goto out;
|
1003
|
+
}
|
727
1004
|
|
728
1005
|
scn = elf_getscn(elf, shdr.sh_link);
|
729
|
-
if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
|
730
|
-
|
731
|
-
|
1006
|
+
if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) {
|
1007
|
+
dbg_printf("Couldn't get section header.");
|
1008
|
+
ret = 1;
|
1009
|
+
goto out;
|
1010
|
+
}
|
732
1011
|
|
733
1012
|
data = elf_getdata(scn, NULL);
|
734
1013
|
if (data == NULL || elf_getdata(scn, data) != NULL
|
735
|
-
|| shdr.sh_size != data->d_size || data->d_off)
|
736
|
-
|
737
|
-
|
1014
|
+
|| shdr.sh_size != data->d_size || data->d_off) {
|
1015
|
+
dbg_printf("Couldn't get .dynstr data");
|
1016
|
+
ret = 1;
|
1017
|
+
goto out;
|
1018
|
+
}
|
738
1019
|
|
739
1020
|
info->dynstr = data->d_buf;
|
740
1021
|
}
|
@@ -752,9 +1033,21 @@ dissect_elf(struct elf_info *info)
|
|
752
1033
|
* Pull out information (start address) of the .plt section.
|
753
1034
|
*/
|
754
1035
|
else if (shdr.sh_type == SHT_PROGBITS) {
|
755
|
-
|
756
|
-
|
757
|
-
|
1036
|
+
if (strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".plt") == 0) {
|
1037
|
+
info->plt_addr = shdr.sh_addr;
|
1038
|
+
} else if (strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".gnu_debuglink") == 0) {
|
1039
|
+
info->debuglink_addr = shdr.sh_addr;
|
1040
|
+
info->debuglink_sz = shdr.sh_size;
|
1041
|
+
dbg_printf("address: %lx, size: %zd\n", shdr.sh_addr, shdr.sh_size);
|
1042
|
+
|
1043
|
+
if ((info->debuglink_data = elf_getdata(scn, NULL)) == NULL ||
|
1044
|
+
info->debuglink_data->d_size == 0) {
|
1045
|
+
dbg_printf(".gnu_debuglink section existed, but wasn't readable.\n");
|
1046
|
+
ret = 2;
|
1047
|
+
goto out;
|
1048
|
+
}
|
1049
|
+
dbg_printf("size: %zd\n", shdr.sh_size);
|
1050
|
+
}
|
758
1051
|
}
|
759
1052
|
/*
|
760
1053
|
* The symbol table is also needed for bin_find_symbol
|
@@ -763,42 +1056,52 @@ dissect_elf(struct elf_info *info)
|
|
763
1056
|
info->symtab_shdr = shdr;
|
764
1057
|
if ((info->symtab_data = elf_getdata(scn, info->symtab_data)) == NULL ||
|
765
1058
|
info->symtab_data->d_size == 0) {
|
766
|
-
|
767
|
-
|
1059
|
+
dbg_printf("shared lib has a broken symbol table. Is it stripped? "
|
1060
|
+
"memprof only works on shared libs that are not stripped!\n");
|
1061
|
+
ret = 2;
|
1062
|
+
goto out;
|
768
1063
|
}
|
769
1064
|
}
|
770
1065
|
}
|
771
1066
|
|
772
1067
|
/* If this object has no symbol table there's nothing else to do but fail */
|
773
1068
|
if (!info->symtab_data) {
|
774
|
-
|
1069
|
+
dbg_printf("binary is stripped. memprof only works on binaries that are not stripped!\n");
|
1070
|
+
ret = 1;
|
775
1071
|
}
|
776
1072
|
|
777
|
-
|
778
1073
|
/*
|
779
1074
|
* Walk the sections, pull out, and store the .plt section
|
780
1075
|
*/
|
781
1076
|
for (j = 1; j < info->ehdr.e_shnum; j++) {
|
782
1077
|
scn = elf_getscn(elf, j);
|
783
|
-
if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
|
784
|
-
|
785
|
-
|
1078
|
+
if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL) {
|
1079
|
+
dbg_printf("Couldn't get section header from library.");
|
1080
|
+
ret = 1;
|
1081
|
+
goto out;
|
1082
|
+
}
|
786
1083
|
|
787
1084
|
if (shdr.sh_addr == info->relplt_addr
|
788
1085
|
&& shdr.sh_size == info->plt_size) {
|
789
1086
|
info->relplt = elf_getdata(scn, NULL);
|
790
1087
|
info->relplt_count = shdr.sh_size / shdr.sh_entsize;
|
791
|
-
if (info->relplt == NULL
|
792
|
-
|
793
|
-
|
794
|
-
|
1088
|
+
if (info->relplt == NULL || elf_getdata(scn, info->relplt) != NULL) {
|
1089
|
+
dbg_printf("Couldn't get .rel*.plt data from");
|
1090
|
+
ret = 1;
|
1091
|
+
goto out;
|
1092
|
+
}
|
795
1093
|
break;
|
796
1094
|
}
|
797
1095
|
}
|
798
1096
|
|
799
|
-
|
1097
|
+
out:
|
1098
|
+
if (find_debug && ret == 1) {
|
1099
|
+
find_debug_syms(info);
|
1100
|
+
}
|
1101
|
+
return ret;
|
800
1102
|
}
|
801
1103
|
|
1104
|
+
|
802
1105
|
/*
|
803
1106
|
* bin_init - initialize the binary parsing/modification layer.
|
804
1107
|
*
|
@@ -809,6 +1112,9 @@ bin_init()
|
|
809
1112
|
{
|
810
1113
|
Dwarf_Error dwrf_err;
|
811
1114
|
|
1115
|
+
ASSERT_ON_COMPILE(sizeof(unsigned long) == sizeof(GElf_Addr));
|
1116
|
+
ASSERT_ON_COMPILE(sizeof(unsigned long) == sizeof(Elf64_Addr));
|
1117
|
+
|
812
1118
|
ruby_info = calloc(1, sizeof(*ruby_info));
|
813
1119
|
|
814
1120
|
if (!ruby_info) {
|
@@ -816,15 +1122,26 @@ bin_init()
|
|
816
1122
|
}
|
817
1123
|
|
818
1124
|
if (!has_libruby(ruby_info)) {
|
819
|
-
|
1125
|
+
dbg_printf("This ruby binary has no libruby\n");
|
1126
|
+
char *filename = calloc(1, 255);
|
1127
|
+
if (readlink("/proc/self/exe", filename, 255) == -1) {
|
1128
|
+
errx(EX_UNAVAILABLE, "Unable to follow /proc/self/exe symlink: %s", strerror(errno));
|
1129
|
+
}
|
1130
|
+
ruby_info->filename = filename;
|
820
1131
|
ruby_info->base_addr = 0;
|
1132
|
+
dbg_printf("The path to the binary is: %s\n", ruby_info->filename);
|
821
1133
|
}
|
822
1134
|
|
823
|
-
|
824
|
-
|
1135
|
+
open_elf(ruby_info);
|
1136
|
+
|
1137
|
+
if (dissect_elf(ruby_info, 1) == 2) {
|
1138
|
+
errx(EX_DATAERR, "Error trying to parse elf file: %s\n", ruby_info->filename);
|
1139
|
+
}
|
825
1140
|
|
826
1141
|
if (dwarf_elf_init(ruby_info->elf, DW_DLC_READ, NULL, NULL, &dwrf, &dwrf_err) != DW_DLV_OK) {
|
827
1142
|
errx(EX_DATAERR, "unable to read debugging data from binary. was it compiled with -g? is it unstripped?");
|
828
1143
|
}
|
1144
|
+
|
1145
|
+
dbg_printf("bin_init finished\n");
|
829
1146
|
}
|
830
1147
|
#endif
|