memprof 0.2.7 → 0.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -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