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 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.
@@ -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 *trampee_addr, struct tramp_st2_entry *tramp);
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
- uint16_t count = 0;
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 < UINT_MAX; addr += pagesize, count += pagesize) {
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
- return info->base_addr + info->plt_addr + (ndx + 1) * 16;
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
- assert(name != NULL);
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
- * has_libruby - check if this ruby binary is linked against libruby.so
660
+ * find_libruby_cb - a callback which attempts to locate libruby in the linkmap
546
661
  *
547
- * This function checks if the curreny binary is linked against libruby. If
548
- * so, it sets libruby = 1, and fill internal state in the elf_info structure.
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
- * Returns 1 if this binary is linked to libruby.so, 0 if not.
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 int
553
- has_libruby(struct elf_info *lib)
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
- libruby = 0;
709
+ assert(map);
710
+
558
711
  while (map) {
559
- if (strstr(map->l_name, "libruby.so")) {
560
- if (lib) {
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 Elf object.
794
+ * Returns an elf_info object that must be freed by the caller.
631
795
  */
632
- static Elf *
633
- open_elf(const char *filename)
796
+ static void
797
+ open_elf(struct elf_info *info)
634
798
  {
635
- Elf *ret = NULL;
636
- int fd = 0;
637
799
 
638
- assert(filename != NULL);
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 ((ret = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
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(ret) != ELF_K_ELF)
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
- assert(ret != NULL);
654
- return ret;
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 void
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
- size_t j = 0;
942
+ int ret = 0;
673
943
 
674
944
  if (elf_getshdrstrndx(elf, &shstrndx) == -1) {
675
- errx(EX_SOFTWARE, "getshstrndx() failed: %s.", elf_errmsg(-1));
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
- errx(EX_SOFTWARE, "Couldn't get elf header.");
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
- errx(EX_SOFTWARE, "getshdr() failed: %s.",
686
- elf_errmsg(-1));
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
- error(EXIT_FAILURE, 0,
703
- "Couldn't get .dynamic data from loaded library.");
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
- || elf_getdata(scn, info->dynsym) != NULL)
725
- error(EXIT_FAILURE, 0,
726
- "Couldn't get .dynsym data ");
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
- error(EXIT_FAILURE, 0,
731
- "Couldn't get section header from");
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
- error(EXIT_FAILURE, 0,
737
- "Couldn't get .dynstr data");
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
- if (strcmp(elf_strptr(elf, shstrndx, shdr.sh_name), ".plt") == 0) {
756
- info->plt_addr = shdr.sh_addr;
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
- errx(EX_DATAERR, "shared lib has a broken symbol table. Is it stripped? "
767
- "memprof only works on shared libs that are not stripped!");
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
- errx(EX_DATAERR, "binary is stripped. memprof only works on binaries that are not stripped!");
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
- error(EXIT_FAILURE, 0,
785
- "Couldn't get section header from library.");
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
- || elf_getdata(scn, info->relplt) != NULL)
793
- error(EXIT_FAILURE, 0,
794
- "Couldn't get .rel*.plt data from");
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
- return;
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
- ruby_info->filename = "/proc/self/exe";
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
- ruby_info->elf = open_elf(ruby_info->filename);
824
- dissect_elf(ruby_info);
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