/* * Copyright (c) 2008, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ #include "precompiled.hpp" #include "asm/assembler.inline.hpp" #include "ci/ciUtilities.hpp" #include "classfile/javaClasses.hpp" #include "code/codeCache.hpp" #include "compiler/disassembler.hpp" #include "gc/shared/cardTable.hpp" #include "gc/shared/cardTableBarrierSet.hpp" #include "gc/shared/collectedHeap.hpp" #include "memory/resourceArea.hpp" #include "oops/oop.inline.hpp" #include "runtime/handles.inline.hpp" #include "runtime/os.inline.hpp" #include "runtime/stubCodeGenerator.hpp" #include "runtime/stubRoutines.hpp" #include "utilities/resourceHash.hpp" #include CPU_HEADER(depChecker) void* Disassembler::_library = NULL; bool Disassembler::_tried_to_load_library = false; bool Disassembler::_library_usable = false; // This routine is in the shared library: Disassembler::decode_func_virtual Disassembler::_decode_instructions_virtual = NULL; Disassembler::decode_func Disassembler::_decode_instructions = NULL; static const char hsdis_library_name[] = "hsdis-" HOTSPOT_LIB_ARCH; static const char decode_instructions_virtual_name[] = "decode_instructions_virtual"; static const char decode_instructions_name[] = "decode_instructions"; static bool use_new_version = true; #define COMMENT_COLUMN 52 LP64_ONLY(+8) /*could be an option*/ #define BYTES_COMMENT ";..." /* funky byte display comment */ class decode_env { private: outputStream* _output; // where the disassembly is directed to CodeBuffer* _codeBuffer; // != NULL only when decoding a CodeBuffer CodeBlob* _codeBlob; // != NULL only when decoding a CodeBlob nmethod* _nm; // != NULL only when decoding a nmethod CodeStrings _strings; address _start; // != NULL when decoding a range of unknown type address _end; // != NULL when decoding a range of unknown type // ptrdiff_t _offset; char _option_buf[512]; char _print_raw; address _cur_insn; // address of instruction currently being decoded int _bytes_per_line; // arch-specific formatting option int _pre_decode_alignment; int _post_decode_alignment; bool _print_file_name; bool _print_help; bool _helpPrinted; static bool _optionsParsed; enum { tabspacing = 8 }; // Check if the event matches the expected tag // The tag must be a substring of the event, and // the tag must be a token in the event, i.e. separated by delimiters static bool match(const char* event, const char* tag) { size_t eventlen = strlen(event); size_t taglen = strlen(tag); if (eventlen < taglen) // size mismatch return false; if (strncmp(event, tag, taglen) != 0) // string mismatch return false; char delim = event[taglen]; return delim == '\0' || delim == ' ' || delim == '/' || delim == '='; } // Merge new option string with previously recorded options void collect_options(const char* p) { if (p == NULL || p[0] == '\0') return; size_t opt_so_far = strlen(_option_buf); if (opt_so_far + 1 + strlen(p) + 1 > sizeof(_option_buf)) return; char* fillp = &_option_buf[opt_so_far]; if (opt_so_far > 0) *fillp++ = ','; strcat(fillp, p); // replace white space by commas: char* q = fillp; while ((q = strpbrk(q, " \t\n")) != NULL) *q++ = ','; // Note that multiple PrintAssemblyOptions flags accumulate with \n, // which we want to be changed to a comma... } void process_options(outputStream* ost); void print_insn_labels(); void print_insn_prefix(); void print_address(address value); // Properly initializes _start/_end. Overwritten too often if // printing of instructions is called for each instruction. void set_start(address s) { _start = s; } void set_end (address e) { _end = e; } void set_nm (nmethod* nm) { _nm = nm; } void set_output(outputStream* st) { _output = st; } #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) // The disassembler library (sometimes) uses tabs to nicely align the instruction operands. // Depending on the mnemonic length and the column position where the // mnemonic is printed, alignment may turn out to be not so nice. // To improve, we assume 8-character tab spacing and left-align the mnemonic on a tab position. // Instruction comments are aligned 4 tab positions to the right of the mnemonic. void calculate_alignment() { _pre_decode_alignment = ((output()->position()+tabspacing-1)/tabspacing)*tabspacing; _post_decode_alignment = _pre_decode_alignment + 4*tabspacing; } void start_insn(address pc) { _cur_insn = pc; output()->bol(); print_insn_labels(); print_insn_prefix(); } void end_insn(address pc) { address pc0 = cur_insn(); outputStream* st = output(); if (AbstractDisassembler::show_comment()) { if ((_nm != NULL) && _nm->has_code_comment(pc0, pc)) { _nm->print_code_comment_on(st, _post_decode_alignment, pc0, pc); // this calls reloc_string_for which calls oop::print_value_on } print_hook_comments(pc0, _nm != NULL); } Disassembler::annotate(pc0, output()); // follow each complete insn by a nice newline st->bol(); } #endif struct SourceFileInfo { struct Link : public CHeapObj { const char* file; int line; Link* next; Link(const char* f, int l) : file(f), line(l), next(NULL) {} }; Link *head, *tail; static unsigned hash(const address& a) { return primitive_hash
(a); } static bool equals(const address& a0, const address& a1) { return primitive_equals
(a0, a1); } void append(const char* file, int line) { if (tail != NULL && tail->file == file && tail->line == line) { // Don't print duplicated lines at the same address. This could happen with C // macros that end up having multiple "__" tokens on the same __LINE__. return; } Link *link = new Link(file, line); if (head == NULL) { head = tail = link; } else { tail->next = link; tail = link; } } SourceFileInfo(const char* file, int line) : head(NULL), tail(NULL) { append(file, line); } }; typedef ResourceHashtable< address, SourceFileInfo, SourceFileInfo::hash, SourceFileInfo::equals, 15889, // prime number ResourceObj::C_HEAP> SourceFileInfoTable; static SourceFileInfoTable _src_table; static const char* _cached_src; static GrowableArray* _cached_src_lines; public: decode_env(CodeBuffer* code, outputStream* output); decode_env(CodeBlob* code, outputStream* output, CodeStrings c = CodeStrings() /* , ptrdiff_t offset */); decode_env(nmethod* code, outputStream* output, CodeStrings c = CodeStrings()); // Constructor for a 'decode_env' to decode an arbitrary // piece of memory, hopefully containing code. decode_env(address start, address end, outputStream* output); #if defined(LUCY_OBSOLETE) // Used in SAPJVM only // Constructor for a simple 'decode_env' which can be used // to decode and print a single instruction to an output stream. decode_env(address start, outputStream* output); #endif // Add 'original_start' argument which is the the original address // the instructions were located at (if this is not equal to 'start'). address decode_instructions(address start, address end, address original_start = NULL); address handle_event(const char* event, address arg); outputStream* output() { return _output; } address cur_insn() { return _cur_insn; } const char* options() { return _option_buf; } static void hook(const char* file, int line, address pc); void print_hook_comments(address pc, bool newline); }; bool decode_env::_optionsParsed = false; decode_env::SourceFileInfoTable decode_env::_src_table; const char* decode_env::_cached_src = NULL; GrowableArray* decode_env::_cached_src_lines = NULL; void decode_env::hook(const char* file, int line, address pc) { // For simplication, we never free from this table. It's really not // necessary as we add to the table only when PrintInterpreter is true, // which means we are debugging the VM and a little bit of extra // memory usage doesn't matter. SourceFileInfo* found = _src_table.get(pc); if (found != NULL) { found->append(file, line); } else { SourceFileInfo sfi(file, line); _src_table.put(pc, sfi); // sfi is copied by value } } void decode_env::print_hook_comments(address pc, bool newline) { SourceFileInfo* found = _src_table.get(pc); outputStream* st = output(); if (found != NULL) { for (SourceFileInfo::Link *link = found->head; link; link = link->next) { const char* file = link->file; int line = link->line; if (_cached_src == NULL || strcmp(_cached_src, file) != 0) { FILE* fp; // _cached_src_lines is a single cache of the lines of a source file, and we refill this cache // every time we need to print a line from a different source file. It's not the fastest, // but seems bearable. if (_cached_src_lines != NULL) { for (int i=0; i<_cached_src_lines->length(); i++) { os::free((void*)_cached_src_lines->at(i)); } _cached_src_lines->clear(); } else { _cached_src_lines = new (ResourceObj::C_HEAP, mtCode)GrowableArray(0, true); } if ((fp = fopen(file, "r")) == NULL) { _cached_src = NULL; return; } _cached_src = file; char line[500]; // don't write lines that are too long in your source files! while (fgets(line, sizeof(line), fp) != NULL) { size_t len = strlen(line); if (len > 0 && line[len-1] == '\n') { line[len-1] = '\0'; } _cached_src_lines->append(os::strdup(line)); } fclose(fp); _print_file_name = true; } if (_print_file_name) { // We print the file name whenever we switch to a new file, or when // Disassembler::decode is called to disassemble a new block of code. _print_file_name = false; if (newline) { st->cr(); } st->move_to(COMMENT_COLUMN); st->print(";;@FILE: %s", file); newline = true; } int index = line - 1; // 1-based line number -> 0-based index. if (index >= _cached_src_lines->length()) { // This could happen if source file is mismatched. } else { const char* source_line = _cached_src_lines->at(index); if (newline) { st->cr(); } st->move_to(COMMENT_COLUMN); st->print(";;%5d: %s", line, source_line); newline = true; } } } } decode_env::decode_env(CodeBuffer* code, outputStream* output) { memset(this, 0, sizeof(*this)); _output = output ? output : tty; _codeBlob = NULL; _codeBuffer = code; _helpPrinted = false; process_options(_output); } decode_env::decode_env(CodeBlob* code, outputStream* output, CodeStrings c) { memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields. _output = output ? output : tty; _codeBlob = code; _codeBuffer = NULL; _helpPrinted = false; if (_codeBlob != NULL && _codeBlob->is_nmethod()) { _nm = (nmethod*) code; } _strings.copy(c); process_options(_output); } decode_env::decode_env(nmethod* code, outputStream* output, CodeStrings c) { memset(this, 0, sizeof(*this)); // Beware, this zeroes bits of fields. _output = output ? output : tty; _codeBlob = NULL; _codeBuffer = NULL; _nm = code; _start = _nm->code_begin(); _end = _nm->code_end(); _helpPrinted = false; _strings.copy(c); process_options(_output); } // Constructor for a 'decode_env' to decode a memory range [start, end) // of unknown origin, assuming it contains code. decode_env::decode_env(address start, address end, outputStream* output) { assert(start < end, "Range must have a positive size, [" PTR_FORMAT ".." PTR_FORMAT ").", p2i(start), p2i(end)); memset(this, 0, sizeof(*this)); _output = output ? output : tty; _codeBlob = NULL; _codeBuffer = NULL; _start = start; _end = end; _helpPrinted = false; process_options(_output); } #if defined(LUCY_OBSOLETE) // Used in SAPJVM only // Constructor for a simple 'decode_env' which can be used // to decode and print a single instruction to an outputStream decode_env::decode_env(address start, outputStream* output) { memset(this, 0, sizeof(*this)); _output = output ? output : tty; _codeBlob = NULL; _codeBuffer = NULL; _start = start; _end = start + Assembler::instr_len(_start); _helpPrinted = false; process_options(_output); } #endif void decode_env::process_options(outputStream* ost) { // by default, output pc but not bytes: _print_help = false; _bytes_per_line = Disassembler::pd_instruction_alignment(); _print_file_name = true; if (_optionsParsed) return; // parse only once // parse the global option string: collect_options(Disassembler::pd_cpu_opts()); collect_options(PrintAssemblyOptions); // ost->print_cr("PrintAssemblyOptions='%s'", options()); if (strstr(options(), "hsdis-")) { if (strstr(options(), "hsdis-print-raw")) { _print_raw = (strstr(options(), "xml") ? 2 : 1); } if (strstr(options(), "hsdis-print-pc")) { // deprecated AbstractDisassembler::toggle_show_pc(); } if (strstr(options(), "hsdis-print-bytes")) { // deprecated AbstractDisassembler::toggle_show_bytes(); } if (strstr(options(), "hsdis-help")) { _print_help = true; } if (strstr(options(), "hsdis-align-instr")) { AbstractDisassembler::toggle_align_instr(); } if (strstr(options(), "hsdis-show-pc")) { AbstractDisassembler::toggle_show_pc(); } if (strstr(options(), "hsdis-show-offset")) { AbstractDisassembler::toggle_show_offset(); } if (strstr(options(), "hsdis-show-bytes")) { AbstractDisassembler::toggle_show_bytes(); } if (strstr(options(), "hsdis-show-data-hex")) { AbstractDisassembler::toggle_show_data_hex(); } if (strstr(options(), "hsdis-show-data-int")) { AbstractDisassembler::toggle_show_data_int(); } if (strstr(options(), "hsdis-show-data-float")) { AbstractDisassembler::toggle_show_data_float(); } if (strstr(options(), "hsdis-show-structs")) { AbstractDisassembler::toggle_show_structs(); } if (strstr(options(), "hsdis-show-comment")) { AbstractDisassembler::toggle_show_comment(); } if (strstr(options(), "hsdis-show-block-comment")) { AbstractDisassembler::toggle_show_block_comment(); } _optionsParsed = true; } if (_print_help && ! _helpPrinted) { _helpPrinted = true; ost->print_cr("PrintAssemblyOptions help:"); ost->print_cr(" hsdis-print-raw test plugin by requesting raw output"); ost->print_cr(" hsdis-print-raw-xml test plugin by requesting raw xml"); ost->print_cr(" hsdis-print-pc turn off PC printing (on by default)"); ost->print_cr(" hsdis-print-bytes turn on instruction byte output"); ost->cr(); ost->print_cr(" hsdis-show-pc toggle printing current pc, currently %s", AbstractDisassembler::show_pc() ? "ON" : "OFF"); ost->print_cr(" hsdis-show-offset toggle printing current offset, currently %s", AbstractDisassembler::show_offset() ? "ON" : "OFF"); ost->print_cr(" hsdis-show-bytes toggle printing instruction bytes, currently %s", AbstractDisassembler::show_bytes() ? "ON" : "OFF"); ost->print_cr(" hsdis-show-data-hex toggle formatting data as hex, currently %s", AbstractDisassembler::show_data_hex() ? "ON" : "OFF"); ost->print_cr(" hsdis-show-data-int toggle formatting data as int, currently %s", AbstractDisassembler::show_data_int() ? "ON" : "OFF"); ost->print_cr(" hsdis-show-data-float toggle formatting data as float, currently %s", AbstractDisassembler::show_data_float() ? "ON" : "OFF"); ost->print_cr(" hsdis-show-structs toggle compiler data structures, currently %s", AbstractDisassembler::show_structs() ? "ON" : "OFF"); ost->print_cr(" hsdis-show-comment toggle instruction comments, currently %s", AbstractDisassembler::show_comment() ? "ON" : "OFF"); ost->print_cr(" hsdis-show-block-comment toggle block comments, currently %s", AbstractDisassembler::show_block_comment() ? "ON" : "OFF"); ost->print_cr(" hsdis-align-instr toggle instruction alignment, currently %s", AbstractDisassembler::align_instr() ? "ON" : "OFF"); ost->print_cr("combined options: %s", options()); } } // Disassembly Event Handler. // This method receives events from the disassembler library hsdis // via event_to_env for each decoding step (installed by // Disassembler::decode_instructions(), replacing the default // callback method). This enables dumping additional info // and custom line formatting. // In a future extension, calling a custom decode method will be // supported. We can use such a method to decode instructions the // binutils decoder does not handle to our liking (suboptimal // formatting, incomplete information, ...). // Returns: // - NULL for all standard invocations. The function result is not // examined (as of now, 20190409) by the hsdis decoder loop. // - next for 'insn0' invocations. // next == arg: the custom decoder didn't do anything. // next > arg: the custom decoder did decode the instruction. // next points to the next undecoded instruction // (continuation point for decoder loop). // // "Normal" sequence of events: // insns - start of instruction stream decoding // mach - display architecture // format - display bytes-per-line // for each instruction: // insn - start of instruction decoding // insn0 - custom decoder invocation (if any) // addr - print address value // /insn - end of instruction decoding // /insns - premature end of instruction stream due to no progress // address decode_env::handle_event(const char* event, address arg) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) //---< Event: end decoding loop (error, no progress) >--- if (decode_env::match(event, "/insns")) { // Nothing to be done here. return NULL; } //---< Event: start decoding loop >--- if (decode_env::match(event, "insns")) { // Nothing to be done here. return NULL; } //---< Event: finish decoding an instruction >--- if (decode_env::match(event, "/insn")) { output()->fill_to(_post_decode_alignment); end_insn(arg); return NULL; } //---< Event: start decoding an instruction >--- if (decode_env::match(event, "insn")) { start_insn(arg); calculate_alignment(); output()->fill_to(_pre_decode_alignment); return NULL; } //---< Event: call custom decoder (platform specific) >--- if (decode_env::match(event, "insn0")) { return Disassembler::decode_instruction0(arg, output(), arg); } //---< Event: Print address >--- if (decode_env::match(event, "addr")) { print_address(arg); return arg; } //---< Event: mach (inform about machine architecture) >--- // This event is problematic because it messes up the output. // The event is fired after the instruction address has already // been printed. The decoded instruction (event "insn") is // printed afterwards. That doesn't look nice. if (decode_env::match(event, "mach")) { guarantee(arg != NULL, "event_to_env - arg must not be NULL for event 'mach'"); static char buffer[64] = { 0, }; if (strcmp(buffer, (const char*)arg) != 0 || strlen((const char*)arg) > sizeof(buffer) - 1) { // Only print this when the mach changes strncpy(buffer, (const char*)arg, sizeof(buffer) - 1); buffer[sizeof(buffer) - 1] = '\0'; // Output suppressed because it messes up disassembly. // output()->print_cr("[Disassembling for mach='%s']", (const char*)arg); } return NULL; } //---< Event: format bytes-per-line >--- if (decode_env::match(event, "format bytes-per-line")) { _bytes_per_line = (int) (intptr_t) arg; return NULL; } #endif return NULL; } static void* event_to_env(void* env_pv, const char* event, void* arg) { decode_env* env = (decode_env*) env_pv; return env->handle_event(event, (address) arg); } // called by the disassembler to print out jump targets and data addresses void decode_env::print_address(address adr) { outputStream* st = output(); if (adr == NULL) { st->print("NULL"); return; } int small_num = (int)(intptr_t)adr; if ((intptr_t)adr == (intptr_t)small_num && -1 <= small_num && small_num <= 9) { st->print("%d", small_num); return; } if (Universe::is_fully_initialized()) { if (StubRoutines::contains(adr)) { StubCodeDesc* desc = StubCodeDesc::desc_for(adr); if (desc == NULL) { desc = StubCodeDesc::desc_for(adr + frame::pc_return_offset); } if (desc != NULL) { st->print("Stub::%s", desc->name()); if (desc->begin() != adr) { st->print(INTX_FORMAT_W(+) " " PTR_FORMAT, adr - desc->begin(), p2i(adr)); } else if (WizardMode) { st->print(" " PTR_FORMAT, p2i(adr)); } return; } st->print("Stub:: " PTR_FORMAT, p2i(adr)); return; } BarrierSet* bs = BarrierSet::barrier_set(); if (bs->is_a(BarrierSet::CardTableBarrierSet) && adr == ci_card_table_address_as
()) { st->print("word_map_base"); if (WizardMode) st->print(" " INTPTR_FORMAT, p2i(adr)); return; } } if (_nm == NULL) { // Don't do this for native methods, as the function name will be printed in // nmethod::reloc_string_for(). // Allocate the buffer on the stack instead of as RESOURCE array. // In case we do DecodeErrorFile, Thread will not be initialized, // causing a "assert(current != __null) failed" failure. const int buflen = 1024; char buf[buflen]; int offset; if (os::dll_address_to_function_name(adr, buf, buflen, &offset)) { st->print(PTR_FORMAT " = %s", p2i(adr), buf); if (offset != 0) { st->print("+%d", offset); } return; } } // Fall through to a simple (hexadecimal) numeral. st->print(PTR_FORMAT, p2i(adr)); } void decode_env::print_insn_labels() { if (AbstractDisassembler::show_block_comment()) { address p = cur_insn(); outputStream* st = output(); //---< Block comments for nmethod >--- // Outputs a bol() before and a cr() after, but only if a comment is printed. // Prints nmethod_section_label as well. if (_nm != NULL) { _nm->print_block_comment(st, p); } if (_codeBlob != NULL) { _codeBlob->print_block_comment(st, p); } if (_codeBuffer != NULL) { _codeBuffer->print_block_comment(st, p); } _strings.print_block_comment(st, (intptr_t)(p - _start)); } } void decode_env::print_insn_prefix() { address p = cur_insn(); outputStream* st = output(); AbstractDisassembler::print_location(p, _start, _end, st, false, false); AbstractDisassembler::print_instruction(p, Assembler::instr_len(p), Assembler::instr_maxlen(), st, true, false); } ATTRIBUTE_PRINTF(2, 3) static int printf_to_env(void* env_pv, const char* format, ...) { decode_env* env = (decode_env*) env_pv; outputStream* st = env->output(); size_t flen = strlen(format); const char* raw = NULL; if (flen == 0) return 0; if (flen == 1 && format[0] == '\n') { st->bol(); return 1; } if (flen < 2 || strchr(format, '%') == NULL) { raw = format; } else if (format[0] == '%' && format[1] == '%' && strchr(format+2, '%') == NULL) { // happens a lot on machines with names like %foo flen--; raw = format+1; } if (raw != NULL) { st->print_raw(raw, (int) flen); return (int) flen; } va_list ap; va_start(ap, format); julong cnt0 = st->count(); st->vprint(format, ap); julong cnt1 = st->count(); va_end(ap); return (int)(cnt1 - cnt0); } // The 'original_start' argument holds the the original address where // the instructions were located in the originating system. If zero (NULL) // is passed in, there is no original address. address decode_env::decode_instructions(address start, address end, address original_start /* = 0*/) { // CodeComment in Stubs. // Properly initialize _start/_end. Overwritten too often if // printing of instructions is called for each instruction. assert((_start == NULL) || (start == NULL) || (_start == start), "don't overwrite CTOR values"); assert((_end == NULL) || (end == NULL) || (_end == end ), "don't overwrite CTOR values"); if (start != NULL) set_start(start); if (end != NULL) set_end(end); if (original_start == NULL) { original_start = start; } //---< Check (and correct) alignment >--- // Don't check alignment of end, it is not aligned. if (((uint64_t)start & ((uint64_t)Disassembler::pd_instruction_alignment() - 1)) != 0) { output()->print_cr("Decode range start:" PTR_FORMAT ": ... (unaligned)", p2i(start)); start = (address)((uint64_t)start & ~((uint64_t)Disassembler::pd_instruction_alignment() - 1)); } // Trying to decode instructions doesn't make sense if we // couldn't load the disassembler library. if (Disassembler::is_abstract()) { return NULL; } // decode a series of instructions and return the end of the last instruction if (_print_raw) { // Print whatever the library wants to print, w/o fancy callbacks. // This is mainly for debugging the library itself. FILE* out = stdout; FILE* xmlout = (_print_raw > 1 ? out : NULL); return use_new_version ? (address) (*Disassembler::_decode_instructions_virtual)((uintptr_t)start, (uintptr_t)end, start, end - start, NULL, (void*) xmlout, NULL, (void*) out, options(), 0/*nice new line*/) : (address) (*Disassembler::_decode_instructions)(start, end, NULL, (void*) xmlout, NULL, (void*) out, options()); } return use_new_version ? (address) (*Disassembler::_decode_instructions_virtual)((uintptr_t)start, (uintptr_t)end, start, end - start, &event_to_env, (void*) this, &printf_to_env, (void*) this, options(), 0/*nice new line*/) : (address) (*Disassembler::_decode_instructions)(start, end, &event_to_env, (void*) this, &printf_to_env, (void*) this, options()); } // ---------------------------------------------------------------------------- // Disassembler // Used as a static wrapper for decode_env. // Each method will create a decode_env before decoding. // You can call the decode_env methods directly if you already have one. bool Disassembler::load_library(outputStream* st) { // Do not try to load multiple times. Failed once -> fails always. // To force retry in debugger: assign _tried_to_load_library=0 if (_tried_to_load_library) { return _library_usable; } #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) // Print to given stream, if any. // Print to tty if Verbose is on and no stream given. st = ((st == NULL) && Verbose) ? tty : st; // Compute fully qualified library name. char ebuf[1024]; char buf[JVM_MAXPATHLEN]; os::jvm_path(buf, sizeof(buf)); int jvm_offset = -1; int lib_offset = -1; #ifdef STATIC_BUILD char* p = strrchr(buf, '/'); *p = '\0'; strcat(p, "/lib/"); lib_offset = jvm_offset = strlen(buf); #else { // Match "libjvm" instead of "jvm" on *nix platforms. Creates better matches. // Match "[lib]jvm[^/]*" in jvm_path. const char* base = buf; const char* p = strrchr(buf, *os::file_separator()); #ifdef _WIN32 p = strstr(p ? p : base, "jvm"); #else p = strstr(p ? p : base, "libjvm"); #endif if (p != NULL) lib_offset = p - base + 1; if (p != NULL) jvm_offset = p - base; } #endif // Find the disassembler shared library. // Search for several paths derived from libjvm, in this order: // 1. /jre/lib///libhsdis-.so (for compatibility) // 2. /jre/lib///hsdis-.so // 3. /jre/lib//hsdis-.so // 4. hsdis-.so (using LD_LIBRARY_PATH) if (jvm_offset >= 0) { // 1. /jre/lib///libhsdis-.so strcpy(&buf[jvm_offset], hsdis_library_name); strcat(&buf[jvm_offset], os::dll_file_extension()); _library = os::dll_load(buf, ebuf, sizeof ebuf); if (_library == NULL && lib_offset >= 0) { // 2. /jre/lib///hsdis-.so strcpy(&buf[lib_offset], hsdis_library_name); strcat(&buf[lib_offset], os::dll_file_extension()); _library = os::dll_load(buf, ebuf, sizeof ebuf); } if (_library == NULL && lib_offset > 0) { // 3. /jre/lib//hsdis-.so buf[lib_offset - 1] = '\0'; const char* p = strrchr(buf, *os::file_separator()); if (p != NULL) { lib_offset = p - buf + 1; strcpy(&buf[lib_offset], hsdis_library_name); strcat(&buf[lib_offset], os::dll_file_extension()); _library = os::dll_load(buf, ebuf, sizeof ebuf); } } } if (_library == NULL) { // 4. hsdis-.so (using LD_LIBRARY_PATH) strcpy(&buf[0], hsdis_library_name); strcat(&buf[0], os::dll_file_extension()); _library = os::dll_load(buf, ebuf, sizeof ebuf); } // load the decoder function to use (new or old version). if (_library != NULL) { _decode_instructions_virtual = CAST_TO_FN_PTR(Disassembler::decode_func_virtual, os::dll_lookup(_library, decode_instructions_virtual_name)); } if (_decode_instructions_virtual == NULL && _library != NULL) { // could not spot in new version, try old version _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func, os::dll_lookup(_library, decode_instructions_name)); use_new_version = false; } else { use_new_version = true; } _tried_to_load_library = true; _library_usable = _decode_instructions_virtual != NULL || _decode_instructions != NULL; // Create a dummy environment to initialize PrintAssemblyOptions. // The PrintAssemblyOptions must be known for abstract disassemblies as well. decode_env dummy((unsigned char*)(&buf[0]), (unsigned char*)(&buf[1]), st); // Report problems during dll_load or dll_lookup, if any. if (st != NULL) { // Success. if (_library_usable) { st->print_cr("Loaded disassembler from %s", buf); } else { st->print_cr("Could not load %s; %s; %s", buf, ((_library != NULL) ? "entry point is missing" : ((WizardMode || PrintMiscellaneous) ? (const char*)ebuf : "library not loadable")), "PrintAssembly defaults to abstract disassembly."); } } #endif return _library_usable; } // Directly disassemble code buffer. void Disassembler::decode(CodeBuffer* cb, address start, address end, outputStream* st) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) //---< Test memory before decoding >--- if (!(cb->contains(start) && cb->contains(end))) { //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- if (st != NULL) { st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not contained in CodeBuffer", p2i(start), p2i(end)); } return; } if (!os::is_readable_range(start, end)) { //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- if (st != NULL) { st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end)); } return; } decode_env env(cb, st); env.output()->print_cr("--------------------------------------------------------------------------------"); env.output()->print("Decoding CodeBuffer (" PTR_FORMAT ")", p2i(cb)); if (cb->name() != NULL) { env.output()->print(", name: %s,", cb->name()); } env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(start), p2i(end), ((jlong)(end - start))); if (is_abstract()) { AbstractDisassembler::decode_abstract(start, end, env.output(), Assembler::instr_maxlen()); } else { env.decode_instructions(start, end); } env.output()->print_cr("--------------------------------------------------------------------------------"); #endif } // Directly disassemble code blob. void Disassembler::decode(CodeBlob* cb, outputStream* st, CodeStrings c) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) if (cb->is_nmethod()) { // If we have an nmethod at hand, // call the specialized decoder directly. decode((nmethod*)cb, st, c); return; } decode_env env(cb, st); env.output()->print_cr("--------------------------------------------------------------------------------"); if (cb->is_aot()) { env.output()->print("A "); if (cb->is_compiled()) { CompiledMethod* cm = (CompiledMethod*)cb; env.output()->print("%d ",cm->compile_id()); cm->method()->method_holder()->name()->print_symbol_on(env.output()); env.output()->print("."); cm->method()->name()->print_symbol_on(env.output()); cm->method()->signature()->print_symbol_on(env.output()); } else { env.output()->print_cr("%s", cb->name()); } } else { env.output()->print("Decoding CodeBlob"); if (cb->name() != NULL) { env.output()->print(", name: %s,", cb->name()); } } env.output()->print_cr(" at [" PTR_FORMAT ", " PTR_FORMAT "] " JLONG_FORMAT " bytes", p2i(cb->code_begin()), p2i(cb->code_end()), ((jlong)(cb->code_end() - cb->code_begin()))); if (is_abstract()) { AbstractDisassembler::decode_abstract(cb->code_begin(), cb->code_end(), env.output(), Assembler::instr_maxlen()); } else { env.decode_instructions(cb->code_begin(), cb->code_end()); } env.output()->print_cr("--------------------------------------------------------------------------------"); #endif } // Decode a nmethod. // This includes printing the constant pool and all code segments. // The nmethod data structures (oop maps, relocations and the like) are not printed. void Disassembler::decode(nmethod* nm, outputStream* st, CodeStrings c) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) ttyLocker ttyl; decode_env env(nm, st); env.output()->print_cr("--------------------------------------------------------------------------------"); nm->print_constant_pool(env.output()); env.output()->print_cr("--------------------------------------------------------------------------------"); env.output()->cr(); if (is_abstract()) { AbstractDisassembler::decode_abstract(nm->code_begin(), nm->code_end(), env.output(), Assembler::instr_maxlen()); } else { env.decode_instructions(nm->code_begin(), nm->code_end()); } env.output()->print_cr("--------------------------------------------------------------------------------"); #endif } // Decode a range, given as [start address, end address) void Disassembler::decode(address start, address end, outputStream* st, CodeStrings c /*, ptrdiff_t offset */) { #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) //---< Test memory before decoding >--- if (!os::is_readable_range(start, end)) { //---< Allow output suppression, but prevent writing to a NULL stream. Could happen with +PrintStubCode. >--- if (st != NULL) { st->print("Memory range [" PTR_FORMAT ".." PTR_FORMAT "] not readable", p2i(start), p2i(end)); } return; } if (is_abstract()) { AbstractDisassembler::decode_abstract(start, end, st, Assembler::instr_maxlen()); return; } // Don't do that fancy stuff. If we just have two addresses, live with it // and treat the memory contents as "amorphic" piece of code. #if 0 CodeBlob* cb = CodeCache::find_blob_unsafe(start); if (cb != NULL) { // If we have an CodeBlob at hand, // call the specialized decoder directly. decode(cb, st, c); } else #endif { // This seems to be just a chunk of memory. decode_env env(start, end, st); env.output()->print_cr("--------------------------------------------------------------------------------"); env.decode_instructions(start, end); env.output()->print_cr("--------------------------------------------------------------------------------"); } #endif } #if defined(LUCY_OBSOLETE) // Used in SAPJVM only // Decodes at pc with given #instructions before and after. // Marks the instruction at pc with '=>'. void Disassembler::decode(address pc, int num_instr_before, int num_instr_after, outputStream* st) { //---< Don't decode the zero page. >--- if ((uint64_t)pc < 0x1000UL) { st->print_cr("Decode address:" PTR_FORMAT ": ... (this is the zero page)", p2i(pc)); return; } //---< test memory before decoding >--- if (!os::is_readable_pointer(pc)) { st->print_cr("", p2i(pc)); return; } //---< Check (and correct) alignment >--- if (((uint64_t)pc & ((uint64_t)pd_instruction_alignment() - 1)) != 0) { st->print_cr("Decode address:" PTR_FORMAT ": ... (unaligned)", p2i(pc)); pc = (address)((uint64_t)pc & ~((uint64_t)pd_instruction_alignment() - 1)); } //---< find correct begin of range >--- address begin = pc; address prev = find_prev_instr(begin, num_instr_before); if (prev != NULL) { // Cull the range if part of the range is invalid. // Usually happens if pc points right at the start of a blob starting at a page. if (os::is_readable_pointer(prev)) begin = prev; } else { // If stepping backwards in instruction stream was // not successful, just start at current position (pc) and only // disassemble num_instr_after instructions. num_instr_before = 0; } if (is_abstract()) { // Just estimate instruction size of 4 for this dump. AbstractDisassembler::decode_abstract(begin, pc + 4*num_instr_after, st, Assembler::instr_maxlen()); return; } //---< decode one instruction at a time >--- address p = begin; address end = begin + Assembler::instr_maxlen()*(num_instr_before+num_instr_after); // safe estimate only for (int i = 0; (i < num_instr_before + num_instr_after) && (p":" ", st); } } #endif #if defined(LUCY_OBSOLETE) // Used in SAPJVM only // Decode a range (#bytes) around a given pc. // Mark the instruction at pc with '=>'. // Helpful to print the surroundings of a SIG* location. void Disassembler::decode(address pc, unsigned int range, outputStream* st) { st = st ? st : tty; //---< Don't decode the zero page. >--- if ((uint64_t)pc < 0x1000UL) { st->print_cr("Decode address:" PTR_FORMAT ": ... (this is the zero page)", p2i(pc)); return; } //---< test memory before decoding >--- if (!os::is_readable_pointer(pc)) { st->print_cr("", p2i(pc)); return; } if (!os::is_readable_pointer(pc-range)) { st->print_cr("", p2i(pc-range)); return; } if (!os::is_readable_pointer(pc+range-1)) { st->print_cr("", p2i(pc+range-1)); return; } //---< Check (and correct) alignment >--- if (((uint64_t)pc & ((uint64_t)pd_instruction_alignment() - 1)) != 0) { st->print_cr("Decode address:" PTR_FORMAT ": ... (unaligned)", p2i(pc)); pc = (address)((uint64_t)pc & ~((uint64_t)pd_instruction_alignment() - 1)); } if ((range & (pd_instruction_alignment() - 1)) != 0) { st->print_cr("Decode range: %d: ... (unaligned)", range); range &= ~(pd_instruction_alignment() - 1); } //---< find correct begin and end of range >--- unsigned int shift = 0; #ifdef S390 int ninstr = 0; while ((shift < Assembler::instr_maxlen()) && ((ninstr = count_instr(pc-range+shift, pc)) < 0)) shift += pd_instruction_alignment(); if (ninstr <= 0) { st->print_cr(" *** INFO no program text found @ range start. Scanned up to range center @ " PTR_FORMAT, p2i(pc)); } while ((shift < range) && ((ninstr = count_instr(pc-range+shift, pc)) < 0)) { shift += pd_instruction_alignment(); } if (ninstr <= 0) { st->print_cr(" *** range disassembly failed. No starting point found."); return; } #endif address begin = pc-range+shift; address end = pc+range; if (is_abstract()) { // Just estimate instruction size of 4 for this dump. AbstractDisassembler::decode_abstract(begin, end, st, Assembler::instr_maxlen()); return; } //---< decode one instruction at a time >--- for (address p = begin; p < end;) { p = decode_one(p, begin, end, (p == pc)?"=>":" ", st); } } #endif #if defined(LUCY_OBSOLETE) // Used in SAPJVM only // One central method for printing one disassembly line, // potentially with a marker. address Disassembler::decode_one(address pc, address begin, address end, const char* marker, outputStream* st) { st->print("%s", marker); // print marker address new_pc = decode_instruction(pc, st); st->cr(); return new_pc; } #endif #if defined(LUCY_OBSOLETE) // Used in SAPJVM only // Decode one instruction and return a pointer to the next instruction. address Disassembler::decode_instruction(address start, outputStream* st, address original_start /* = 0*/) { if (is_abstract()) return start + Assembler::instr_len(start); decode_env env(start, st); return env.decode_instructions(start, start + Assembler::instr_len(start), original_start); } #endif // To prevent excessive code expansion in the interpreter generator, we // do not inline this function into Disassembler::hook(). void Disassembler::_hook(const char* file, int line, MacroAssembler* masm) { decode_env::hook(file, line, masm->code_section()->end()); }