memprof 0.1.3 → 0.2.0
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 +5 -5
- data/ext/arch.h +30 -0
- data/ext/bin_api.h +13 -52
- data/ext/elf.c +479 -45
- data/ext/extconf.rb +96 -8
- data/ext/i386.c +106 -0
- data/ext/i386.h +75 -0
- data/ext/mach.c +12 -0
- data/ext/memprof.c +575 -186
- data/ext/src/libdwarf-20091118.tar.gz +0 -0
- data/ext/src/yajl-1.0.8.tar.gz +0 -0
- data/ext/x86_64.c +124 -0
- data/ext/x86_64.h +78 -0
- data/ext/x86_gen.h +100 -0
- data/memprof.gemspec +10 -1
- data/spec/memprof_spec.rb +85 -0
- metadata +10 -1
data/README
CHANGED
@@ -42,9 +42,12 @@ Currently supporting:
|
|
42
42
|
|
43
43
|
Linux:
|
44
44
|
x86_64 builds of Ruby Enterprise Edition 1.8.6/1.8.7
|
45
|
-
x86_64 builds of MRI Ruby
|
45
|
+
x86_64 builds of MRI Ruby (enabled-shared and disable-shared)
|
46
46
|
|
47
|
-
Experimental support:
|
47
|
+
Experimental (somewhat broken) support:
|
48
|
+
|
49
|
+
Linux:
|
50
|
+
i386/i686 support.
|
48
51
|
|
49
52
|
Snow Leopard:
|
50
53
|
x86_64 builds of MRI (both enable-shared and disable-shared)
|
@@ -56,9 +59,6 @@ Coming soon:
|
|
56
59
|
|
57
60
|
Linux:
|
58
61
|
Tracking object allocationns in C extensions.
|
59
|
-
x86_64 builds of MRI Ruby with --enable-shared
|
60
|
-
|
61
|
-
i386/i686 support for all the above.
|
62
62
|
|
63
63
|
CREDITS
|
64
64
|
=======
|
data/ext/arch.h
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#if !defined (_ARCH_H_)
|
2
|
+
#define _ARCH_H_
|
3
|
+
|
4
|
+
#if defined(_ARCH_i386_) || defined(_ARCH_i686_)
|
5
|
+
#include "i386.h"
|
6
|
+
#elif defined(_ARCH_x86_64_)
|
7
|
+
#include "x86_64.h"
|
8
|
+
#else
|
9
|
+
#error "Unsupported architecture! Cannot continue compilation."
|
10
|
+
#endif
|
11
|
+
|
12
|
+
/*
|
13
|
+
* "normal" trampoline
|
14
|
+
*/
|
15
|
+
void *
|
16
|
+
arch_get_st2_tramp(size_t *size);
|
17
|
+
|
18
|
+
void
|
19
|
+
arch_insert_st1_tramp(void *start, void *trampee, void *tramp);
|
20
|
+
|
21
|
+
/*
|
22
|
+
* inline trampoline
|
23
|
+
*/
|
24
|
+
void *
|
25
|
+
arch_get_inline_st2_tramp(size_t *size);
|
26
|
+
|
27
|
+
int
|
28
|
+
arch_insert_inline_st2_tramp(void *addr, void *marker, void *trampoline, void *table_entry);
|
29
|
+
|
30
|
+
#endif
|
data/ext/bin_api.h
CHANGED
@@ -3,67 +3,20 @@
|
|
3
3
|
#include <stddef.h>
|
4
4
|
#include <stdint.h>
|
5
5
|
|
6
|
-
/*
|
7
|
-
extern void *text_segment;
|
8
|
-
extern unsigned long text_segment_len;
|
6
|
+
/* XXX get rid of this */
|
9
7
|
extern size_t pagesize;
|
10
8
|
|
11
9
|
/*
|
12
10
|
* trampoline specific stuff
|
13
11
|
*/
|
14
|
-
extern struct
|
12
|
+
extern struct tramp_st2_entry *tramp_table;
|
15
13
|
extern size_t tramp_size;
|
16
14
|
|
17
15
|
/*
|
18
16
|
* inline trampoline specific stuff
|
19
17
|
*/
|
18
|
+
extern struct inline_tramp_st2_entry *inline_tramp_table;
|
20
19
|
extern size_t inline_tramp_size;
|
21
|
-
extern struct inline_tramp_tbl_entry *inline_tramp_table;
|
22
|
-
|
23
|
-
/* trampoline types */
|
24
|
-
struct tramp_inline {
|
25
|
-
unsigned char jmp[1];
|
26
|
-
uint32_t displacement;
|
27
|
-
unsigned char pad[2];
|
28
|
-
} __attribute__((__packed__));
|
29
|
-
|
30
|
-
struct tramp_tbl_entry {
|
31
|
-
unsigned char rbx_save[1];
|
32
|
-
unsigned char mov[2];
|
33
|
-
void *addr;
|
34
|
-
unsigned char callq[2];
|
35
|
-
unsigned char rbx_restore[1];
|
36
|
-
unsigned char ret[1];
|
37
|
-
} __attribute__((__packed__));
|
38
|
-
|
39
|
-
struct inline_tramp_tbl_entry {
|
40
|
-
unsigned char rex[1];
|
41
|
-
unsigned char mov[1];
|
42
|
-
unsigned char src_reg[1];
|
43
|
-
uint32_t mov_displacement;
|
44
|
-
|
45
|
-
struct {
|
46
|
-
unsigned char push_rdi[1];
|
47
|
-
unsigned char mov_rdi[3];
|
48
|
-
uint32_t rdi_source_displacement;
|
49
|
-
unsigned char push_rbx[1];
|
50
|
-
unsigned char push_rbp[1];
|
51
|
-
unsigned char save_rsp[3];
|
52
|
-
unsigned char align_rsp[4];
|
53
|
-
unsigned char mov[2];
|
54
|
-
void *addr;
|
55
|
-
unsigned char callq[2];
|
56
|
-
unsigned char leave[1];
|
57
|
-
unsigned char rbx_restore[1];
|
58
|
-
unsigned char rdi_restore[1];
|
59
|
-
} __attribute__((__packed__)) frame;
|
60
|
-
|
61
|
-
unsigned char jmp[1];
|
62
|
-
uint32_t jmp_displacement;
|
63
|
-
} __attribute__((__packed__));
|
64
|
-
|
65
|
-
void
|
66
|
-
update_callqs(int entry, void *trampee_addr);
|
67
20
|
|
68
21
|
/*
|
69
22
|
* EXPORTED API.
|
@@ -74,10 +27,18 @@ bin_init();
|
|
74
27
|
void *
|
75
28
|
bin_find_symbol(char *sym, size_t *size);
|
76
29
|
|
77
|
-
void
|
78
|
-
|
30
|
+
void *
|
31
|
+
bin_find_got_addr(char *sym, void *cookie);
|
79
32
|
|
80
33
|
void *
|
81
34
|
bin_allocate_page();
|
82
35
|
|
36
|
+
int
|
37
|
+
bin_type_size(char *type);
|
38
|
+
|
39
|
+
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);
|
83
44
|
#endif
|
data/ext/elf.c
CHANGED
@@ -1,47 +1,140 @@
|
|
1
1
|
#if defined(HAVE_ELF)
|
2
|
-
|
2
|
+
#define _GNU_SOURCE
|
3
3
|
#include "bin_api.h"
|
4
|
+
#include "arch.h"
|
4
5
|
|
6
|
+
#include <dwarf.h>
|
7
|
+
#include <err.h>
|
5
8
|
#include <fcntl.h>
|
6
|
-
#include <
|
9
|
+
#include <libdwarf.h>
|
10
|
+
#include <libelf/gelf.h>
|
7
11
|
#include <link.h>
|
8
12
|
#include <stdio.h>
|
13
|
+
#include <stdlib.h>
|
9
14
|
#include <string.h>
|
10
15
|
#include <sysexits.h>
|
11
16
|
#include <unistd.h>
|
12
17
|
|
13
18
|
#include <sys/mman.h>
|
14
19
|
|
15
|
-
|
16
|
-
static
|
17
|
-
static Elf_Data *symtab_data = NULL;
|
20
|
+
/* ruby binary info */
|
21
|
+
static int has_libruby = 0;
|
18
22
|
|
19
|
-
|
20
|
-
|
23
|
+
static Dwarf_Debug dwrf = NULL;
|
24
|
+
|
25
|
+
static struct elf_info *ruby_info = NULL;
|
26
|
+
|
27
|
+
struct elf_info {
|
28
|
+
Elf *elf;
|
29
|
+
|
30
|
+
GElf_Addr base_addr;
|
31
|
+
|
32
|
+
void *text_segment;
|
33
|
+
size_t text_segment_len;
|
34
|
+
|
35
|
+
GElf_Addr relplt_addr;
|
36
|
+
Elf_Data *relplt;
|
37
|
+
size_t relplt_count;
|
38
|
+
|
39
|
+
GElf_Addr plt_addr;
|
40
|
+
Elf_Data *plt;
|
41
|
+
size_t plt_size;
|
42
|
+
size_t plt_count;
|
43
|
+
|
44
|
+
GElf_Ehdr ehdr;
|
45
|
+
|
46
|
+
Elf_Data *dynsym;
|
47
|
+
size_t dynsym_count;
|
48
|
+
const char *dynstr;
|
49
|
+
|
50
|
+
GElf_Shdr symtab_shdr;
|
51
|
+
Elf_Data *symtab_data;
|
52
|
+
|
53
|
+
const char *filename;
|
54
|
+
};
|
55
|
+
|
56
|
+
|
57
|
+
static void *
|
58
|
+
do_bin_allocate_page(void *cookie)
|
21
59
|
{
|
22
|
-
void * ret = NULL;
|
23
|
-
|
60
|
+
void * ret = NULL, *addr = NULL;
|
61
|
+
struct elf_info *info = cookie;
|
62
|
+
uint16_t max = ~0, count = 0;
|
63
|
+
|
64
|
+
if (!info)
|
65
|
+
return NULL;
|
24
66
|
|
25
|
-
if (
|
26
|
-
|
67
|
+
if (has_libruby) {
|
68
|
+
addr = info->text_segment + info->text_segment_len;
|
69
|
+
for (; count < max;addr += pagesize, count += pagesize) {
|
70
|
+
ret = mmap(addr, pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE, -1, 0);
|
71
|
+
if (ret != MAP_FAILED) {
|
72
|
+
memset(ret, 0x90, pagesize);
|
73
|
+
return ret;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
} else {
|
77
|
+
return mmap(NULL, pagesize, PROT_WRITE|PROT_READ|PROT_EXEC, MAP_ANON|MAP_PRIVATE|MAP_32BIT, -1, 0);
|
27
78
|
}
|
28
79
|
|
29
|
-
return
|
80
|
+
return NULL;
|
30
81
|
}
|
31
82
|
|
32
|
-
void
|
33
|
-
|
83
|
+
void *
|
84
|
+
bin_allocate_page()
|
34
85
|
{
|
35
|
-
|
86
|
+
return do_bin_allocate_page(ruby_info);
|
36
87
|
}
|
37
88
|
|
38
|
-
|
39
|
-
|
89
|
+
static inline GElf_Addr
|
90
|
+
arch_plt_sym_val(struct elf_info *info, size_t ndx) {
|
91
|
+
return info->base_addr + info->plt_addr + (ndx + 1) * 16;
|
92
|
+
}
|
93
|
+
|
94
|
+
static void *
|
95
|
+
find_got_addr(char *symname, void *cookie)
|
96
|
+
{
|
97
|
+
size_t i = 0;
|
98
|
+
struct elf_info *info = cookie;
|
99
|
+
|
100
|
+
if (cookie == NULL) {
|
101
|
+
info = ruby_info;
|
102
|
+
}
|
103
|
+
|
104
|
+
for (i = 0; i < info->relplt_count; ++i) {
|
105
|
+
GElf_Rel rel;
|
106
|
+
GElf_Rela rela;
|
107
|
+
GElf_Sym sym;
|
108
|
+
GElf_Addr addr;
|
109
|
+
void *ret;
|
110
|
+
const char *name;
|
111
|
+
|
112
|
+
if (info->relplt->d_type == ELF_T_RELA) {
|
113
|
+
ret = gelf_getrela(info->relplt, i, &rela);
|
114
|
+
|
115
|
+
if (ret == NULL
|
116
|
+
|| ELF64_R_SYM(rela.r_info) >= info->dynsym_count
|
117
|
+
|| gelf_getsym(info->dynsym, ELF64_R_SYM(rela.r_info), &sym) == NULL)
|
118
|
+
return NULL;
|
119
|
+
|
120
|
+
name = info->dynstr + sym.st_name;
|
121
|
+
if (strcmp(symname, name) == 0) {
|
122
|
+
addr = arch_plt_sym_val(info, i);
|
123
|
+
return (void *)addr;
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
return NULL;
|
129
|
+
}
|
130
|
+
|
131
|
+
static void *
|
132
|
+
do_bin_find_symbol(char *sym, size_t *size, struct elf_info *elf)
|
40
133
|
{
|
41
134
|
char *name = NULL;
|
42
135
|
|
43
|
-
ElfW(Sym) *esym = (ElfW(Sym)*) symtab_data->d_buf;
|
44
|
-
ElfW(Sym) *lastsym = (ElfW(Sym)*) ((char*) symtab_data->d_buf + symtab_data->d_size);
|
136
|
+
ElfW(Sym) *esym = (ElfW(Sym)*) elf->symtab_data->d_buf;
|
137
|
+
ElfW(Sym) *lastsym = (ElfW(Sym)*) ((char*) elf->symtab_data->d_buf + elf->symtab_data->d_size);
|
45
138
|
|
46
139
|
for (; esym < lastsym; esym++){
|
47
140
|
/* ignore weak/numeric/empty symbols */
|
@@ -50,72 +143,413 @@ bin_find_symbol(char *sym, size_t *size)
|
|
50
143
|
(ELF32_ST_BIND(esym->st_info)== STB_NUM))
|
51
144
|
continue;
|
52
145
|
|
53
|
-
|
54
|
-
name = elf_strptr(elf, symtab_shdr.sh_link, (size_t)esym->st_name);
|
146
|
+
name = elf_strptr(elf->elf, elf->symtab_shdr.sh_link, (size_t)esym->st_name);
|
55
147
|
if (strcmp(name, sym) == 0) {
|
56
148
|
if (size) {
|
57
149
|
*size = esym->st_size;
|
58
150
|
}
|
59
|
-
return (void *)esym->st_value;
|
151
|
+
return elf->base_addr + (void *)esym->st_value;
|
60
152
|
}
|
61
153
|
}
|
62
154
|
return NULL;
|
63
155
|
}
|
64
156
|
|
157
|
+
void *
|
158
|
+
bin_find_symbol(char *sym, size_t *size)
|
159
|
+
{
|
160
|
+
return do_bin_find_symbol(sym, size, ruby_info);
|
161
|
+
}
|
65
162
|
|
66
163
|
void
|
67
|
-
|
164
|
+
bin_update_image(int entry, char *trampee, struct tramp_st2_entry *tramp)
|
68
165
|
{
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
166
|
+
void *trampee_addr = NULL;
|
167
|
+
|
168
|
+
if (!has_libruby) {
|
169
|
+
unsigned char *byte = ruby_info->text_segment;
|
170
|
+
trampee_addr = bin_find_symbol(trampee, NULL);
|
171
|
+
size_t count = 0;
|
172
|
+
|
173
|
+
for(; count < ruby_info->text_segment_len; byte++, count++) {
|
174
|
+
arch_insert_st1_tramp(byte, trampee_addr, tramp);
|
175
|
+
}
|
176
|
+
} else {
|
177
|
+
trampee_addr = find_got_addr(trampee, NULL);
|
178
|
+
arch_overwrite_got(trampee_addr, tramp->addr);
|
179
|
+
}
|
180
|
+
}
|
181
|
+
|
182
|
+
|
183
|
+
static Dwarf_Die
|
184
|
+
check_die(Dwarf_Die die, char *search, Dwarf_Half type)
|
185
|
+
{
|
186
|
+
char *name = 0;
|
187
|
+
Dwarf_Error error = 0;
|
188
|
+
Dwarf_Half tag = 0;
|
189
|
+
int ret = 0;
|
190
|
+
int res = dwarf_diename(die,&name,&error);
|
191
|
+
if (res == DW_DLV_ERROR) {
|
192
|
+
printf("Error in dwarf_diename\n");
|
193
|
+
exit(1);
|
194
|
+
}
|
195
|
+
if (res == DW_DLV_NO_ENTRY) {
|
196
|
+
return 0;
|
197
|
+
}
|
198
|
+
|
199
|
+
res = dwarf_tag(die,&tag,&error);
|
200
|
+
if (res != DW_DLV_OK) {
|
201
|
+
printf("Error in dwarf_tag\n");
|
202
|
+
exit(1);
|
203
|
+
}
|
204
|
+
|
205
|
+
if (tag == type && strcmp(name, search) == 0){
|
206
|
+
//printf("tag: %d name: '%s' die: %p\n",tag,name,die);
|
207
|
+
ret = 1;
|
208
|
+
}
|
209
|
+
|
210
|
+
dwarf_dealloc(dwrf,name,DW_DLA_STRING);
|
211
|
+
|
212
|
+
return ret ? die : 0;
|
213
|
+
}
|
214
|
+
|
215
|
+
static Dwarf_Die
|
216
|
+
search_dies(Dwarf_Die die, char *name, Dwarf_Half type)
|
217
|
+
{
|
218
|
+
int res = DW_DLV_ERROR;
|
219
|
+
Dwarf_Die cur_die=die;
|
220
|
+
Dwarf_Die child = 0;
|
221
|
+
Dwarf_Error error;
|
222
|
+
Dwarf_Die ret = 0;
|
223
|
+
|
224
|
+
ret = check_die(cur_die, name, type);
|
225
|
+
if (ret)
|
226
|
+
return ret;
|
227
|
+
|
228
|
+
for(;;) {
|
229
|
+
Dwarf_Die sib_die = 0;
|
230
|
+
res = dwarf_child(cur_die,&child,&error);
|
231
|
+
if (res == DW_DLV_ERROR) {
|
232
|
+
printf("Error in dwarf_child\n");
|
233
|
+
exit(1);
|
234
|
+
}
|
235
|
+
if (res == DW_DLV_OK) {
|
236
|
+
ret = search_dies(child,name,type);
|
237
|
+
if (ret) {
|
238
|
+
if (cur_die != die && cur_die != ret)
|
239
|
+
dwarf_dealloc(dwrf,cur_die,DW_DLA_DIE);
|
240
|
+
return ret;
|
241
|
+
}
|
242
|
+
}
|
243
|
+
/* res == DW_DLV_NO_ENTRY */
|
244
|
+
|
245
|
+
res = dwarf_siblingof(dwrf,cur_die,&sib_die,&error);
|
246
|
+
if (res == DW_DLV_ERROR) {
|
247
|
+
printf("Error in dwarf_siblingof\n");
|
248
|
+
exit(1);
|
249
|
+
}
|
250
|
+
if (res == DW_DLV_NO_ENTRY) {
|
251
|
+
/* Done at this level. */
|
252
|
+
break;
|
253
|
+
}
|
254
|
+
/* res == DW_DLV_OK */
|
255
|
+
|
256
|
+
if (cur_die != die)
|
257
|
+
dwarf_dealloc(dwrf,cur_die,DW_DLA_DIE);
|
258
|
+
|
259
|
+
cur_die = sib_die;
|
260
|
+
ret = check_die(cur_die, name, type);
|
261
|
+
if (ret)
|
262
|
+
return ret;
|
263
|
+
}
|
264
|
+
return 0;
|
265
|
+
}
|
266
|
+
|
267
|
+
static Dwarf_Die
|
268
|
+
find_die(char *name, Dwarf_Half type)
|
269
|
+
{
|
270
|
+
Dwarf_Die ret = 0;
|
271
|
+
Dwarf_Unsigned cu_header_length = 0;
|
272
|
+
Dwarf_Half version_stamp = 0;
|
273
|
+
Dwarf_Unsigned abbrev_offset = 0;
|
274
|
+
Dwarf_Half address_size = 0;
|
275
|
+
Dwarf_Unsigned next_cu_header = 0;
|
276
|
+
Dwarf_Error error;
|
277
|
+
int cu_number = 0;
|
278
|
+
|
279
|
+
Dwarf_Die no_die = 0;
|
280
|
+
Dwarf_Die cu_die = 0;
|
281
|
+
int res = DW_DLV_ERROR;
|
282
|
+
|
283
|
+
for (;;++cu_number) {
|
284
|
+
no_die = 0;
|
285
|
+
cu_die = 0;
|
286
|
+
res = DW_DLV_ERROR;
|
287
|
+
|
288
|
+
res = dwarf_next_cu_header(dwrf, &cu_header_length, &version_stamp, &abbrev_offset, &address_size, &next_cu_header, &error);
|
289
|
+
|
290
|
+
if (res == DW_DLV_ERROR) {
|
291
|
+
printf("Error in dwarf_next_cu_header\n");
|
292
|
+
exit(1);
|
293
|
+
}
|
294
|
+
if (res == DW_DLV_NO_ENTRY) {
|
295
|
+
/* Done. */
|
296
|
+
return 0;
|
297
|
+
}
|
298
|
+
|
299
|
+
/* The CU will have a single sibling, a cu_die. */
|
300
|
+
res = dwarf_siblingof(dwrf,no_die,&cu_die,&error);
|
301
|
+
|
302
|
+
if (res == DW_DLV_ERROR) {
|
303
|
+
printf("Error in dwarf_siblingof on CU die \n");
|
304
|
+
exit(1);
|
305
|
+
}
|
306
|
+
if (res == DW_DLV_NO_ENTRY) {
|
307
|
+
/* Impossible case. */
|
308
|
+
printf("no entry! in dwarf_siblingof on CU die \n");
|
309
|
+
exit(1);
|
310
|
+
}
|
311
|
+
|
312
|
+
ret = search_dies(cu_die,name,type);
|
313
|
+
|
314
|
+
if (cu_die != ret)
|
315
|
+
dwarf_dealloc(dwrf,cu_die,DW_DLA_DIE);
|
316
|
+
|
317
|
+
if (ret)
|
318
|
+
break;
|
319
|
+
}
|
320
|
+
|
321
|
+
/* traverse to the end to reset */
|
322
|
+
while ((dwarf_next_cu_header(dwrf, &cu_header_length, &version_stamp, &abbrev_offset, &address_size, &next_cu_header, &error)) != DW_DLV_NO_ENTRY);
|
323
|
+
|
324
|
+
return ret ? ret : 0;
|
325
|
+
}
|
326
|
+
|
327
|
+
static int
|
328
|
+
bin_has_libruby(struct elf_info *cookie)
|
329
|
+
{
|
330
|
+
struct link_map *map = _r_debug.r_map;
|
331
|
+
struct elf_info *lib = cookie;
|
332
|
+
|
333
|
+
if (has_libruby != -1) {
|
334
|
+
has_libruby = 0;
|
335
|
+
while (map) {
|
336
|
+
if (strstr(map->l_name, "libruby.so")) {
|
337
|
+
if (lib) {
|
338
|
+
lib->base_addr = (GElf_Addr)map->l_addr;
|
339
|
+
lib->filename = strdup(map->l_name);
|
340
|
+
}
|
341
|
+
has_libruby = 1;
|
342
|
+
break;
|
343
|
+
}
|
344
|
+
map = map->l_next;
|
345
|
+
}
|
346
|
+
}
|
347
|
+
|
348
|
+
return has_libruby;
|
349
|
+
}
|
350
|
+
|
351
|
+
int
|
352
|
+
bin_type_size(char *name)
|
353
|
+
{
|
354
|
+
Dwarf_Unsigned size = 0;
|
355
|
+
Dwarf_Error error;
|
356
|
+
int res = DW_DLV_ERROR;
|
357
|
+
Dwarf_Die die = 0;
|
358
|
+
|
359
|
+
die = find_die(name, DW_TAG_structure_type);
|
360
|
+
|
361
|
+
if (die) {
|
362
|
+
res = dwarf_bytesize(die, &size, &error);
|
363
|
+
dwarf_dealloc(dwrf,die,DW_DLA_DIE);
|
364
|
+
if (res == DW_DLV_OK)
|
365
|
+
return (int)size;
|
366
|
+
}
|
367
|
+
|
368
|
+
return -1;
|
369
|
+
}
|
370
|
+
|
371
|
+
int
|
372
|
+
bin_type_member_offset(char *type, char *member)
|
373
|
+
{
|
374
|
+
Dwarf_Error error;
|
375
|
+
int res = DW_DLV_ERROR;
|
376
|
+
Dwarf_Die die = 0, child = 0;
|
377
|
+
Dwarf_Attribute attr = 0;
|
378
|
+
|
379
|
+
die = find_die(type, DW_TAG_structure_type);
|
380
|
+
|
381
|
+
if (die) {
|
382
|
+
child = search_dies(die, member, DW_TAG_member);
|
383
|
+
dwarf_dealloc(dwrf,die,DW_DLA_DIE);
|
384
|
+
|
385
|
+
if (child) {
|
386
|
+
res = dwarf_attr(child, DW_AT_data_member_location, &attr, &error);
|
387
|
+
if (res == DW_DLV_OK) {
|
388
|
+
Dwarf_Locdesc *locs = 0;
|
389
|
+
Dwarf_Signed num = 0;
|
390
|
+
|
391
|
+
res = dwarf_loclist(attr, &locs, &num, &error);
|
392
|
+
if (res == DW_DLV_OK && num > 0) {
|
393
|
+
return locs[0].ld_s[0].lr_number;
|
394
|
+
}
|
395
|
+
}
|
396
|
+
}
|
397
|
+
}
|
398
|
+
|
399
|
+
return -1;
|
400
|
+
}
|
401
|
+
|
402
|
+
static Elf *
|
403
|
+
open_elf(const char *filename)
|
404
|
+
{
|
405
|
+
Elf *ret = NULL;
|
406
|
+
int fd = 0;
|
74
407
|
|
75
408
|
if (elf_version(EV_CURRENT) == EV_NONE)
|
76
409
|
errx(EX_SOFTWARE, "ELF library initialization failed: %s",
|
77
410
|
elf_errmsg(-1));
|
78
411
|
|
79
|
-
asprintf(&filename, "/proc/%ld/exe", (long)getpid());
|
80
|
-
|
81
412
|
if ((fd = open(filename, O_RDONLY, 0)) < 0)
|
82
413
|
err(EX_NOINPUT, "open \%s\" failed", filename);
|
83
414
|
|
84
|
-
if ((
|
415
|
+
if ((ret = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
|
85
416
|
errx(EX_SOFTWARE, "elf_begin() failed: %s.",
|
86
417
|
elf_errmsg(-1));
|
87
418
|
|
88
|
-
if (elf_kind(
|
419
|
+
if (elf_kind(ret) != ELF_K_ELF)
|
89
420
|
errx(EX_DATAERR, "%s is not an ELF object.", filename);
|
90
421
|
|
422
|
+
return ret;
|
423
|
+
}
|
424
|
+
|
425
|
+
static void
|
426
|
+
dissect_elf(struct elf_info *info)
|
427
|
+
{
|
428
|
+
size_t shstrndx = 0;
|
429
|
+
Elf *elf = info->elf;
|
430
|
+
ElfW(Dyn) *dyn;
|
431
|
+
Elf_Scn *scn = NULL;
|
432
|
+
GElf_Shdr shdr;
|
433
|
+
size_t j = 0;
|
434
|
+
|
91
435
|
if (elf_getshdrstrndx(elf, &shstrndx) == -1)
|
92
|
-
errx(EX_SOFTWARE, "getshstrndx() failed: %s.",
|
93
|
-
elf_errmsg(-1));
|
436
|
+
errx(EX_SOFTWARE, "getshstrndx() failed: %s.", elf_errmsg(-1));
|
94
437
|
|
95
|
-
|
438
|
+
if (gelf_getehdr(elf, &(info->ehdr)) == NULL)
|
439
|
+
errx(EX_SOFTWARE, "Couldn't get elf header.");
|
96
440
|
|
97
441
|
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
98
442
|
if (gelf_getshdr(scn, &shdr) != &shdr)
|
99
443
|
errx(EX_SOFTWARE, "getshdr() failed: %s.",
|
100
444
|
elf_errmsg(-1));
|
101
445
|
|
102
|
-
if
|
446
|
+
/* if there is a dynamic section ... */
|
447
|
+
if (shdr.sh_type == SHT_DYNAMIC) {
|
448
|
+
Elf_Data *data;
|
449
|
+
data = elf_getdata(scn, NULL);
|
450
|
+
/* for each entry in the dyn section... */
|
451
|
+
for (j = 0; j < shdr.sh_size / shdr.sh_entsize; ++j) {
|
452
|
+
GElf_Dyn dyn;
|
453
|
+
if (gelf_getdyn(data, j, &dyn) == NULL) {
|
454
|
+
error(EXIT_FAILURE, 0,
|
455
|
+
"Couldn't get .dynamic data from loaded library.");
|
456
|
+
}
|
457
|
+
|
458
|
+
if (dyn.d_tag == DT_JMPREL) {
|
459
|
+
info->relplt_addr = dyn.d_un.d_ptr;
|
460
|
+
}
|
461
|
+
else if (dyn.d_tag == DT_PLTRELSZ) {
|
462
|
+
info->plt_size = dyn.d_un.d_val;
|
463
|
+
}
|
464
|
+
}
|
465
|
+
} else if (shdr.sh_type == SHT_DYNSYM) {
|
466
|
+
Elf_Data *data;
|
467
|
+
|
468
|
+
info->dynsym = elf_getdata(scn, NULL);
|
469
|
+
info->dynsym_count = shdr.sh_size / shdr.sh_entsize;
|
470
|
+
if (info->dynsym == NULL
|
471
|
+
|| elf_getdata(scn, info->dynsym) != NULL)
|
472
|
+
error(EXIT_FAILURE, 0,
|
473
|
+
"Couldn't get .dynsym data ");
|
474
|
+
|
475
|
+
scn = elf_getscn(elf, shdr.sh_link);
|
476
|
+
if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
|
477
|
+
error(EXIT_FAILURE, 0,
|
478
|
+
"Couldn't get section header from");
|
479
|
+
|
480
|
+
data = elf_getdata(scn, NULL);
|
481
|
+
if (data == NULL || elf_getdata(scn, data) != NULL
|
482
|
+
|| shdr.sh_size != data->d_size || data->d_off)
|
483
|
+
error(EXIT_FAILURE, 0,
|
484
|
+
"Couldn't get .dynstr data");
|
485
|
+
|
486
|
+
info->dynstr = data->d_buf;
|
487
|
+
} else if (shdr.sh_type == SHT_PROGBITS &&
|
103
488
|
(shdr.sh_flags == (SHF_ALLOC | SHF_EXECINSTR)) &&
|
104
489
|
strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".text") == 0) {
|
105
490
|
|
106
|
-
|
107
|
-
|
491
|
+
info->text_segment = (void *)shdr.sh_addr + info->base_addr;
|
492
|
+
info->text_segment_len = shdr.sh_size;
|
493
|
+
} else if (shdr.sh_type == SHT_PROGBITS) {
|
494
|
+
if (strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".plt") == 0) {
|
495
|
+
info->plt_addr = shdr.sh_addr;
|
496
|
+
}
|
108
497
|
} else if (shdr.sh_type == SHT_SYMTAB) {
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
498
|
+
info->symtab_shdr = shdr;
|
499
|
+
if ((info->symtab_data = elf_getdata(scn, info->symtab_data)) == NULL ||
|
500
|
+
info->symtab_data->d_size == 0) {
|
501
|
+
errx(EX_DATAERR, "shared lib has a broken symbol table. Is it stripped? "
|
502
|
+
"memprof only works on shared libs that are not stripped!");
|
503
|
+
}
|
504
|
+
}
|
505
|
+
}
|
506
|
+
|
507
|
+
if (!info->symtab_data) {
|
508
|
+
errx(EX_DATAERR, "binary is stripped. memprof only works on binaries that are not stripped!");
|
509
|
+
}
|
510
|
+
|
511
|
+
|
512
|
+
for (j = 1; j < info->ehdr.e_shnum; j++) {
|
513
|
+
scn = elf_getscn(elf, j);
|
514
|
+
if (scn == NULL || gelf_getshdr(scn, &shdr) == NULL)
|
515
|
+
error(EXIT_FAILURE, 0,
|
516
|
+
"Couldn't get section header from library.");
|
517
|
+
|
518
|
+
if (shdr.sh_addr == info->relplt_addr
|
519
|
+
&& shdr.sh_size == info->plt_size) {
|
520
|
+
info->relplt = elf_getdata(scn, NULL);
|
521
|
+
info->relplt_count = shdr.sh_size / shdr.sh_entsize;
|
522
|
+
if (info->relplt == NULL
|
523
|
+
|| elf_getdata(scn, info->relplt) != NULL)
|
524
|
+
error(EXIT_FAILURE, 0,
|
525
|
+
"Couldn't get .rel*.plt data from");
|
526
|
+
break;
|
114
527
|
}
|
115
528
|
}
|
116
529
|
|
117
|
-
|
118
|
-
|
530
|
+
return;
|
531
|
+
}
|
532
|
+
|
533
|
+
void
|
534
|
+
bin_init()
|
535
|
+
{
|
536
|
+
Dwarf_Error dwrf_err;
|
537
|
+
|
538
|
+
ruby_info = malloc(sizeof(*ruby_info));
|
539
|
+
|
540
|
+
if (!ruby_info)
|
541
|
+
return;
|
542
|
+
|
543
|
+
if (!bin_has_libruby(ruby_info)) {
|
544
|
+
ruby_info->filename = strdup("/proc/self/exe");
|
545
|
+
ruby_info->base_addr = 0;
|
546
|
+
}
|
547
|
+
|
548
|
+
ruby_info->elf = open_elf(ruby_info->filename);
|
549
|
+
dissect_elf(ruby_info);
|
550
|
+
|
551
|
+
if (dwarf_elf_init(ruby_info->elf, DW_DLC_READ, NULL, NULL, &dwrf, &dwrf_err) != DW_DLV_OK) {
|
552
|
+
errx(EX_DATAERR, "unable to read debugging data from binary. was it compiled with -g? is it unstripped?");
|
119
553
|
}
|
120
554
|
}
|
121
555
|
#endif
|