Mercurial > hg > truffle
comparison src/share/tools/hsdis/hsdis.c @ 100:c7c777385a15
6667042: PrintAssembly option does not work without special plugin
Summary: remove old private plugin interface, simplify, rework old plugin to use unchanged Gnu sources
Reviewed-by: kvn, rasbold
author | jrose |
---|---|
date | Wed, 02 Apr 2008 12:09:59 -0700 |
parents | |
children | 67a2f5ba5582 |
comparison
equal
deleted
inserted
replaced
99:8a4ef4e001d3 | 100:c7c777385a15 |
---|---|
1 /* | |
2 * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. | |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
4 * | |
5 * This code is free software; you can redistribute it and/or modify it | |
6 * under the terms of the GNU General Public License version 2 only, as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, | |
20 * CA 95054 USA or visit www.sun.com if you need additional information or | |
21 * have any questions. | |
22 * | |
23 */ | |
24 | |
25 /* hsdis.c -- dump a range of addresses as native instructions | |
26 This implements the plugin protocol required by the | |
27 HotSpot PrintAssembly option. | |
28 */ | |
29 | |
30 #include "hsdis.h" | |
31 | |
32 #include <sysdep.h> | |
33 #include <libiberty.h> | |
34 #include <bfd.h> | |
35 #include <dis-asm.h> | |
36 | |
37 #ifndef bool | |
38 #define bool int | |
39 #define true 1 | |
40 #define false 0 | |
41 #endif /*bool*/ | |
42 | |
43 /* short names for stuff in hsdis.h */ | |
44 typedef decode_instructions_event_callback_ftype event_callback_t; | |
45 typedef decode_instructions_printf_callback_ftype printf_callback_t; | |
46 | |
47 /* disassemble_info.application_data object */ | |
48 struct hsdis_app_data { | |
49 /* the arguments to decode_instructions */ | |
50 uintptr_t start; uintptr_t end; | |
51 event_callback_t event_callback; void* event_stream; | |
52 printf_callback_t printf_callback; void* printf_stream; | |
53 bool losing; | |
54 | |
55 /* the architecture being disassembled */ | |
56 const char* arch_name; | |
57 const bfd_arch_info_type* arch_info; | |
58 | |
59 /* the disassembler we are going to use: */ | |
60 disassembler_ftype dfn; | |
61 struct disassemble_info dinfo; /* the actual struct! */ | |
62 | |
63 char mach_option[64]; | |
64 char insn_options[256]; | |
65 }; | |
66 | |
67 #define DECL_APP_DATA(dinfo) \ | |
68 struct hsdis_app_data* app_data = (struct hsdis_app_data*) (dinfo)->application_data | |
69 | |
70 #define DECL_EVENT_CALLBACK(app_data) \ | |
71 event_callback_t event_callback = (app_data)->event_callback; \ | |
72 void* event_stream = (app_data)->event_stream | |
73 | |
74 #define DECL_PRINTF_CALLBACK(app_data) \ | |
75 printf_callback_t printf_callback = (app_data)->printf_callback; \ | |
76 void* printf_stream = (app_data)->printf_stream | |
77 | |
78 | |
79 static void print_help(struct hsdis_app_data* app_data, | |
80 const char* msg, const char* arg); | |
81 static void setup_app_data(struct hsdis_app_data* app_data, | |
82 const char* options); | |
83 static const char* format_insn_close(const char* close, | |
84 disassemble_info* dinfo, | |
85 char* buf, size_t bufsize); | |
86 | |
87 void* | |
88 #ifdef DLL_ENTRY | |
89 DLL_ENTRY | |
90 #endif | |
91 decode_instructions(void* start_pv, void* end_pv, | |
92 event_callback_t event_callback_arg, void* event_stream_arg, | |
93 printf_callback_t printf_callback_arg, void* printf_stream_arg, | |
94 const char* options) { | |
95 struct hsdis_app_data app_data; | |
96 memset(&app_data, 0, sizeof(app_data)); | |
97 app_data.start = (uintptr_t) start_pv; | |
98 app_data.end = (uintptr_t) end_pv; | |
99 app_data.event_callback = event_callback_arg; | |
100 app_data.event_stream = event_stream_arg; | |
101 app_data.printf_callback = printf_callback_arg; | |
102 app_data.printf_stream = printf_stream_arg; | |
103 | |
104 setup_app_data(&app_data, options); | |
105 char buf[128]; | |
106 | |
107 { | |
108 /* now reload everything from app_data: */ | |
109 DECL_EVENT_CALLBACK(&app_data); | |
110 DECL_PRINTF_CALLBACK(&app_data); | |
111 uintptr_t start = app_data.start; | |
112 uintptr_t end = app_data.end; | |
113 uintptr_t p = start; | |
114 | |
115 (*event_callback)(event_stream, "insns", (void*)start); | |
116 | |
117 (*event_callback)(event_stream, "mach name='%s'", | |
118 (void*) app_data.arch_info->printable_name); | |
119 if (app_data.dinfo.bytes_per_line != 0) { | |
120 (*event_callback)(event_stream, "format bytes-per-line='%p'/", | |
121 (void*)(intptr_t) app_data.dinfo.bytes_per_line); | |
122 } | |
123 | |
124 while (p < end && !app_data.losing) { | |
125 (*event_callback)(event_stream, "insn", (void*) p); | |
126 | |
127 /* reset certain state, so we can read it with confidence */ | |
128 app_data.dinfo.insn_info_valid = 0; | |
129 app_data.dinfo.branch_delay_insns = 0; | |
130 app_data.dinfo.data_size = 0; | |
131 app_data.dinfo.insn_type = 0; | |
132 | |
133 int size = (*app_data.dfn)((bfd_vma) p, &app_data.dinfo); | |
134 | |
135 if (size > 0) p += size; | |
136 else app_data.losing = true; | |
137 | |
138 const char* insn_close = format_insn_close("/insn", &app_data.dinfo, | |
139 buf, sizeof(buf)); | |
140 (*event_callback)(event_stream, insn_close, (void*) p); | |
141 | |
142 /* follow each complete insn by a nice newline */ | |
143 (*printf_callback)(printf_stream, "\n"); | |
144 } | |
145 | |
146 (*event_callback)(event_stream, "/insns", (void*) p); | |
147 return (void*) p; | |
148 } | |
149 } | |
150 | |
151 /* take the address of the function, for luck, and also test the typedef: */ | |
152 const decode_instructions_ftype decode_instructions_address = &decode_instructions; | |
153 | |
154 static const char* format_insn_close(const char* close, | |
155 disassemble_info* dinfo, | |
156 char* buf, size_t bufsize) { | |
157 if (!dinfo->insn_info_valid) | |
158 return close; | |
159 enum dis_insn_type itype = dinfo->insn_type; | |
160 int dsize = dinfo->data_size, delays = dinfo->branch_delay_insns; | |
161 if ((itype == dis_nonbranch && (dsize | delays) == 0) | |
162 || (strlen(close) + 3*20 > bufsize)) | |
163 return close; | |
164 | |
165 const char* type = "unknown"; | |
166 switch (itype) { | |
167 case dis_nonbranch: type = NULL; break; | |
168 case dis_branch: type = "branch"; break; | |
169 case dis_condbranch: type = "condbranch"; break; | |
170 case dis_jsr: type = "jsr"; break; | |
171 case dis_condjsr: type = "condjsr"; break; | |
172 case dis_dref: type = "dref"; break; | |
173 case dis_dref2: type = "dref2"; break; | |
174 } | |
175 | |
176 strcpy(buf, close); | |
177 char* p = buf; | |
178 if (type) sprintf(p += strlen(p), " type='%s'", type); | |
179 if (dsize) sprintf(p += strlen(p), " dsize='%d'", dsize); | |
180 if (delays) sprintf(p += strlen(p), " delay='%d'", delays); | |
181 return buf; | |
182 } | |
183 | |
184 /* handler functions */ | |
185 | |
186 static int | |
187 hsdis_read_memory_func(bfd_vma memaddr, | |
188 bfd_byte* myaddr, | |
189 unsigned int length, | |
190 struct disassemble_info* dinfo) { | |
191 uintptr_t memaddr_p = (uintptr_t) memaddr; | |
192 DECL_APP_DATA(dinfo); | |
193 if (memaddr_p + length > app_data->end) { | |
194 /* read is out of bounds */ | |
195 return EIO; | |
196 } else { | |
197 memcpy(myaddr, (bfd_byte*) memaddr_p, length); | |
198 return 0; | |
199 } | |
200 } | |
201 | |
202 static void | |
203 hsdis_print_address_func(bfd_vma vma, struct disassemble_info* dinfo) { | |
204 /* the actual value to print: */ | |
205 void* addr_value = (void*) (uintptr_t) vma; | |
206 DECL_APP_DATA(dinfo); | |
207 DECL_EVENT_CALLBACK(app_data); | |
208 | |
209 /* issue the event: */ | |
210 void* result = | |
211 (*event_callback)(event_stream, "addr/", addr_value); | |
212 if (result == NULL) { | |
213 /* event declined */ | |
214 generic_print_address(vma, dinfo); | |
215 } | |
216 } | |
217 | |
218 | |
219 /* configuration */ | |
220 | |
221 static void set_optional_callbacks(struct hsdis_app_data* app_data); | |
222 static void parse_caller_options(struct hsdis_app_data* app_data, | |
223 const char* caller_options); | |
224 static const char* native_arch_name(); | |
225 static enum bfd_endian native_endian(); | |
226 static const bfd_arch_info_type* find_arch_info(const char* arch_nane); | |
227 static bfd* get_native_bfd(const bfd_arch_info_type* arch_info, | |
228 /* to avoid malloc: */ | |
229 bfd* empty_bfd, bfd_target* empty_xvec); | |
230 static void init_disassemble_info_from_bfd(struct disassemble_info* dinfo, | |
231 void *stream, | |
232 fprintf_ftype fprintf_func, | |
233 bfd* bfd, | |
234 char* disassembler_options); | |
235 static void parse_fake_insn(disassembler_ftype dfn, | |
236 struct disassemble_info* dinfo); | |
237 | |
238 static void setup_app_data(struct hsdis_app_data* app_data, | |
239 const char* caller_options) { | |
240 /* Make reasonable defaults for null callbacks. | |
241 A non-null stream for a null callback is assumed to be a FILE* for output. | |
242 Events are rendered as XML. | |
243 */ | |
244 set_optional_callbacks(app_data); | |
245 | |
246 /* Look into caller_options for anything interesting. */ | |
247 if (caller_options != NULL) | |
248 parse_caller_options(app_data, caller_options); | |
249 | |
250 /* Discover which architecture we are going to disassemble. */ | |
251 app_data->arch_name = &app_data->mach_option[0]; | |
252 if (app_data->arch_name[0] == '\0') | |
253 app_data->arch_name = native_arch_name(); | |
254 app_data->arch_info = find_arch_info(app_data->arch_name); | |
255 | |
256 /* Make a fake bfd to hold the arch. and byteorder info. */ | |
257 struct { | |
258 bfd_target empty_xvec; | |
259 bfd empty_bfd; | |
260 } buf; | |
261 bfd* native_bfd = get_native_bfd(app_data->arch_info, | |
262 /* to avoid malloc: */ | |
263 &buf.empty_bfd, &buf.empty_xvec); | |
264 init_disassemble_info_from_bfd(&app_data->dinfo, | |
265 app_data->printf_stream, | |
266 app_data->printf_callback, | |
267 native_bfd, | |
268 app_data->insn_options); | |
269 | |
270 /* Finish linking together the various callback blocks. */ | |
271 app_data->dinfo.application_data = (void*) app_data; | |
272 app_data->dfn = disassembler(native_bfd); | |
273 app_data->dinfo.print_address_func = hsdis_print_address_func; | |
274 app_data->dinfo.read_memory_func = hsdis_read_memory_func; | |
275 | |
276 if (app_data->dfn == NULL) { | |
277 const char* bad = app_data->arch_name; | |
278 static bool complained; | |
279 if (bad == &app_data->mach_option[0]) | |
280 print_help(app_data, "bad mach=%s", bad); | |
281 else if (!complained) | |
282 print_help(app_data, "bad native mach=%s; please port hsdis to this platform", bad); | |
283 complained = true; | |
284 /* must bail out */ | |
285 app_data->losing = true; | |
286 return; | |
287 } | |
288 | |
289 parse_fake_insn(app_data->dfn, &app_data->dinfo); | |
290 } | |
291 | |
292 | |
293 /* ignore all events, return a null */ | |
294 static void* null_event_callback(void* ignore_stream, const char* ignore_event, void* arg) { | |
295 return NULL; | |
296 } | |
297 | |
298 /* print all events as XML markup */ | |
299 static void* xml_event_callback(void* stream, const char* event, void* arg) { | |
300 FILE* fp = (FILE*) stream; | |
301 #define NS_PFX "dis:" | |
302 if (event[0] != '/') { | |
303 /* issue the tag, with or without a formatted argument */ | |
304 fprintf(fp, "<"NS_PFX); | |
305 fprintf(fp, event, arg); | |
306 fprintf(fp, ">"); | |
307 } else { | |
308 ++event; /* skip slash */ | |
309 const char* argp = strchr(event, ' '); | |
310 if (argp == NULL) { | |
311 /* no arguments; just issue the closing tag */ | |
312 fprintf(fp, "</"NS_PFX"%s>", event); | |
313 } else { | |
314 /* split out the closing attributes as <dis:foo_done attr='val'/> */ | |
315 int event_prefix = (argp - event); | |
316 fprintf(fp, "<"NS_PFX"%.*s_done", event_prefix, event); | |
317 fprintf(fp, argp, arg); | |
318 fprintf(fp, "/></"NS_PFX"%.*s>", event_prefix, event); | |
319 } | |
320 } | |
321 return NULL; | |
322 } | |
323 | |
324 static void set_optional_callbacks(struct hsdis_app_data* app_data) { | |
325 if (app_data->printf_callback == NULL) { | |
326 int (*fprintf_callback)(FILE*, const char*, ...) = &fprintf; | |
327 FILE* fprintf_stream = stdout; | |
328 app_data->printf_callback = (printf_callback_t) fprintf_callback; | |
329 if (app_data->printf_stream == NULL) | |
330 app_data->printf_stream = (void*) fprintf_stream; | |
331 } | |
332 if (app_data->event_callback == NULL) { | |
333 if (app_data->event_stream == NULL) | |
334 app_data->event_callback = &null_event_callback; | |
335 else | |
336 app_data->event_callback = &xml_event_callback; | |
337 } | |
338 | |
339 } | |
340 | |
341 static void parse_caller_options(struct hsdis_app_data* app_data, const char* caller_options) { | |
342 char* iop_base = app_data->insn_options; | |
343 char* iop_limit = iop_base + sizeof(app_data->insn_options) - 1; | |
344 char* iop = iop_base; | |
345 const char* p; | |
346 for (p = caller_options; p != NULL; ) { | |
347 const char* q = strchr(p, ','); | |
348 size_t plen = (q == NULL) ? strlen(p) : ((q++) - p); | |
349 if (plen == 4 && strncmp(p, "help", plen) == 0) { | |
350 print_help(app_data, NULL, NULL); | |
351 } else if (plen >= 5 && strncmp(p, "mach=", 5) == 0) { | |
352 char* mach_option = app_data->mach_option; | |
353 size_t mach_size = sizeof(app_data->mach_option); | |
354 mach_size -= 1; /*leave room for the null*/ | |
355 if (plen > mach_size) plen = mach_size; | |
356 strncpy(mach_option, p, plen); | |
357 mach_option[plen] = '\0'; | |
358 } else if (plen > 6 && strncmp(p, "hsdis-", 6)) { | |
359 // do not pass these to the next level | |
360 } else { | |
361 /* just copy it; {i386,sparc}-dis.c might like to see it */ | |
362 if (iop > iop_base && iop < iop_limit) (*iop++) = ','; | |
363 if (iop + plen > iop_limit) | |
364 plen = iop_limit - iop; | |
365 strncpy(iop, p, plen); | |
366 iop += plen; | |
367 } | |
368 p = q; | |
369 } | |
370 } | |
371 | |
372 static void print_help(struct hsdis_app_data* app_data, | |
373 const char* msg, const char* arg) { | |
374 DECL_PRINTF_CALLBACK(app_data); | |
375 if (msg != NULL) { | |
376 (*printf_callback)(printf_stream, "hsdis: "); | |
377 (*printf_callback)(printf_stream, msg, arg); | |
378 (*printf_callback)(printf_stream, "\n"); | |
379 } | |
380 (*printf_callback)(printf_stream, "hsdis output options:\n"); | |
381 if (printf_callback == (printf_callback_t) &fprintf) | |
382 disassembler_usage((FILE*) printf_stream); | |
383 else | |
384 disassembler_usage(stderr); /* better than nothing */ | |
385 (*printf_callback)(printf_stream, " mach=<arch> select disassembly mode\n"); | |
386 #if defined(LIBARCH_i386) || defined(LIBARCH_amd64) | |
387 (*printf_callback)(printf_stream, " mach=i386 select 32-bit mode\n"); | |
388 (*printf_callback)(printf_stream, " mach=x86-64 select 64-bit mode\n"); | |
389 (*printf_callback)(printf_stream, " suffix always print instruction suffix\n"); | |
390 #endif | |
391 (*printf_callback)(printf_stream, " help print this message\n"); | |
392 } | |
393 | |
394 | |
395 /* low-level bfd and arch stuff that binutils doesn't do for us */ | |
396 | |
397 static const bfd_arch_info_type* find_arch_info(const char* arch_name) { | |
398 const bfd_arch_info_type* arch_info = bfd_scan_arch(arch_name); | |
399 if (arch_info == NULL) { | |
400 extern const bfd_arch_info_type bfd_default_arch_struct; | |
401 arch_info = &bfd_default_arch_struct; | |
402 } | |
403 return arch_info; | |
404 } | |
405 | |
406 static const char* native_arch_name() { | |
407 const char* res = HOTSPOT_LIB_ARCH; | |
408 #ifdef LIBARCH_amd64 | |
409 res = "i386:x86-64"; | |
410 #endif | |
411 #ifdef LIBARCH_sparc | |
412 res = "sparc:v8plusb"; | |
413 #endif | |
414 #ifdef LIBARCH_sparc | |
415 res = "sparc:v8plusb"; | |
416 #endif | |
417 #ifdef LIBARCH_sparcv9 | |
418 res = "sparc:v9b"; | |
419 #endif | |
420 if (res == NULL) | |
421 res = "HOTSPOT_LIB_ARCH is not set in Makefile!"; | |
422 return res; | |
423 } | |
424 | |
425 static enum bfd_endian native_endian() { | |
426 int32_t endian_test = 'x'; | |
427 if (*(const char*) &endian_test == 'x') | |
428 return BFD_ENDIAN_LITTLE; | |
429 else | |
430 return BFD_ENDIAN_BIG; | |
431 } | |
432 | |
433 static bfd* get_native_bfd(const bfd_arch_info_type* arch_info, | |
434 bfd* empty_bfd, bfd_target* empty_xvec) { | |
435 memset(empty_bfd, 0, sizeof(*empty_bfd)); | |
436 memset(empty_xvec, 0, sizeof(*empty_xvec)); | |
437 empty_xvec->flavour = bfd_target_unknown_flavour; | |
438 empty_xvec->byteorder = native_endian(); | |
439 empty_bfd->xvec = empty_xvec; | |
440 empty_bfd->arch_info = arch_info; | |
441 return empty_bfd; | |
442 } | |
443 | |
444 static int read_zero_data_only(bfd_vma ignore_p, | |
445 bfd_byte* myaddr, unsigned int length, | |
446 struct disassemble_info *ignore_info) { | |
447 memset(myaddr, 0, length); | |
448 return 0; | |
449 } | |
450 static int print_to_dev_null(void* ignore_stream, const char* ignore_format, ...) { | |
451 return 0; | |
452 } | |
453 | |
454 /* Prime the pump by running the selected disassembler on a null input. | |
455 This forces the machine-specific disassembler to divulge invariant | |
456 information like bytes_per_line. | |
457 */ | |
458 static void parse_fake_insn(disassembler_ftype dfn, | |
459 struct disassemble_info* dinfo) { | |
460 typedef int (*read_memory_ftype) | |
461 (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, | |
462 struct disassemble_info *info); | |
463 read_memory_ftype read_memory_func = dinfo->read_memory_func; | |
464 fprintf_ftype fprintf_func = dinfo->fprintf_func; | |
465 | |
466 dinfo->read_memory_func = &read_zero_data_only; | |
467 dinfo->fprintf_func = &print_to_dev_null; | |
468 (*dfn)(0, dinfo); | |
469 | |
470 // put it back: | |
471 dinfo->read_memory_func = read_memory_func; | |
472 dinfo->fprintf_func = fprintf_func; | |
473 } | |
474 | |
475 static void init_disassemble_info_from_bfd(struct disassemble_info* dinfo, | |
476 void *stream, | |
477 fprintf_ftype fprintf_func, | |
478 bfd* abfd, | |
479 char* disassembler_options) { | |
480 init_disassemble_info(dinfo, stream, fprintf_func); | |
481 | |
482 dinfo->flavour = bfd_get_flavour(abfd); | |
483 dinfo->arch = bfd_get_arch(abfd); | |
484 dinfo->mach = bfd_get_mach(abfd); | |
485 dinfo->disassembler_options = disassembler_options; | |
486 dinfo->octets_per_byte = bfd_octets_per_byte (abfd); | |
487 dinfo->skip_zeroes = sizeof(void*) * 2; | |
488 dinfo->skip_zeroes_at_end = sizeof(void*)-1; | |
489 dinfo->disassembler_needs_relocs = FALSE; | |
490 | |
491 if (bfd_big_endian(abfd)) | |
492 dinfo->display_endian = dinfo->endian = BFD_ENDIAN_BIG; | |
493 else if (bfd_little_endian(abfd)) | |
494 dinfo->display_endian = dinfo->endian = BFD_ENDIAN_LITTLE; | |
495 else | |
496 dinfo->endian = native_endian(); | |
497 | |
498 disassemble_init_for_target(dinfo); | |
499 } |