diff --git a/make/Hsdis.gmk b/make/Hsdis.gmk index 02f09b320f095..edc629e6bb591 100644 --- a/make/Hsdis.gmk +++ b/make/Hsdis.gmk @@ -38,6 +38,7 @@ HSDIS_OUTPUT_DIR := $(SUPPORT_OUTPUTDIR)/hsdis ifeq ($(call isTargetOs, windows), true) INSTALLED_HSDIS_DIR := $(JDK_OUTPUTDIR)/bin + IMAGE_HSDIS_DIR := $(JDK_IMAGE_DIR)/bin # On windows, we need to "fake" a completely different toolchain using gcc # instead of the normal microsoft toolchain. This is quite hacky... @@ -55,6 +56,7 @@ ifeq ($(call isTargetOs, windows), true) $(eval $(call DefineNativeToolchain, TOOLCHAIN_MINGW, \ CC := $(MINGW_BASE)-gcc, \ + CXX := $(MINGW_BASE)-g++, \ LD := $(MINGW_BASE)-ld, \ OBJCOPY := $(MINGW_BASE)-objcopy, \ RC := $(RC), \ @@ -101,24 +103,46 @@ ifeq ($(call isTargetOs, windows), true) -lmingwex -lmsvcrt -lpthread -ladvapi32 -lshell32 -luser32 -lkernel32 else INSTALLED_HSDIS_DIR := $(JDK_OUTPUTDIR)/lib + IMAGE_HSDIS_DIR := $(JDK_IMAGE_DIR)/lib HSDIS_TOOLCHAIN := TOOLCHAIN_DEFAULT HSDIS_TOOLCHAIN_CFLAGS := $(CFLAGS_JDKLIB) HSDIS_TOOLCHAIN_LDFLAGS := $(LDFLAGS_JDKLIB) HSDIS_TOOLCHAIN_LIBS := -ldl + + ifeq ($(HSDIS_BACKEND), llvm) + # Use C++ instead of C + HSDIS_TOOLCHAIN_CFLAGS := $(CXXFLAGS_JDKLIB) + HSDIS_TOOLCHAIN := TOOLCHAIN_LINK_CXX + endif +endif + +ifeq ($(HSDIS_BACKEND), llvm) + ifeq ($(call isTargetOs, linux), true) + LLVM_OS := pc-linux-gnu + else ifeq ($(call isTargetOs, macosx), true) + LLVM_OS := apple-darwin + else ifeq ($(call isTargetOs, windows), true) + LLVM_OS := pc-windows-msvc + else + $(error No support for LLVM on this platform) + endif + + HSDIS_CFLAGS += -DLLVM_DEFAULT_TRIPLET='"$(OPENJDK_TARGET_CPU)-$(LLVM_OS)"' endif $(eval $(call SetupJdkLibrary, BUILD_HSDIS, \ NAME := hsdis, \ - SRC := $(TOPDIR)/src/utils/hsdis, \ + SRC := $(TOPDIR)/src/utils/hsdis/$(HSDIS_BACKEND), \ + EXTRA_HEADER_DIRS := $(TOPDIR)/src/utils/hsdis, \ TOOLCHAIN := $(HSDIS_TOOLCHAIN), \ OUTPUT_DIR := $(HSDIS_OUTPUT_DIR), \ OBJECT_DIR := $(HSDIS_OUTPUT_DIR), \ DISABLED_WARNINGS_gcc := undef format-nonliteral sign-compare, \ DISABLED_WARNINGS_clang := undef format-nonliteral, \ CFLAGS := $(HSDIS_TOOLCHAIN_CFLAGS) $(HSDIS_CFLAGS), \ - LDFLAGS := $(HSDIS_TOOLCHAIN_LDFLAGS) $(SHARED_LIBRARY_FLAGS), \ + LDFLAGS := $(HSDIS_TOOLCHAIN_LDFLAGS) $(HSDIS_LDFLAGS) $(SHARED_LIBRARY_FLAGS), \ LIBS := $(HSDIS_LIBS) $(HSDIS_TOOLCHAIN_LIBS), \ )) @@ -129,13 +153,19 @@ TARGETS += build INSTALLED_HSDIS_NAME := hsdis-$(OPENJDK_TARGET_CPU_LEGACY_LIB)$(SHARED_LIBRARY_SUFFIX) INSTALLED_HSDIS := $(INSTALLED_HSDIS_DIR)/$(INSTALLED_HSDIS_NAME) +INSTALLED_HSDIS_IMAGE := $(IMAGE_HSDIS_DIR)/$(INSTALLED_HSDIS_NAME) $(INSTALLED_HSDIS): $(BUILD_HSDIS_TARGET) - $(call LogWarn, NOTE: The resulting build might not be redistributable. Seek legal advice before distibuting.) +ifeq ($(HSDIS_BACKEND), binutils) + $(call LogWarn, NOTE: The resulting build might not be redistributable. Seek legal advice before distributing.) +endif + $(install-file) + +$(INSTALLED_HSDIS_IMAGE): $(INSTALLED_HSDIS) $(install-file) -install: $(INSTALLED_HSDIS) +install: $(INSTALLED_HSDIS_IMAGE) TARGETS += install diff --git a/make/autoconf/jdk-options.m4 b/make/autoconf/jdk-options.m4 index 0a7145c9116a4..6c483c046775c 100644 --- a/make/autoconf/jdk-options.m4 +++ b/make/autoconf/jdk-options.m4 @@ -811,7 +811,7 @@ AC_DEFUN([JDKOPT_BUILD_BINUTILS], AC_DEFUN_ONCE([JDKOPT_SETUP_HSDIS], [ AC_ARG_WITH([hsdis], [AS_HELP_STRING([--with-hsdis], - [what hsdis backend to use ('none', 'binutils') @<:@none@:>@])]) + [what hsdis backend to use ('none', 'binutils', 'llvm') @<:@none@:>@])]) AC_ARG_WITH([binutils], [AS_HELP_STRING([--with-binutils], [where to find the binutils files needed for hsdis/binutils])]) @@ -819,6 +819,9 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_HSDIS], AC_ARG_WITH([binutils-src], [AS_HELP_STRING([--with-binutils-src], [where to find the binutils source for building])]) + AC_ARG_WITH([llvm], [AS_HELP_STRING([--with-llvm], + [where to find LLVM, needed for hsdis/llvm])]) + AC_MSG_CHECKING([what hsdis backend to use]) if test "x$with_hsdis" = xyes; then @@ -853,6 +856,7 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_HSDIS], test -e $BINUTILS_DIR/opcodes/libopcodes.a && \ test -e $BINUTILS_DIR/libiberty/libiberty.a; then HSDIS_CFLAGS="-I$BINUTILS_DIR/include -I$BINUTILS_DIR/bfd -DLIBARCH_$OPENJDK_TARGET_CPU_LEGACY_LIB" + HSDIS_LDFLAGS="" HSDIS_LIBS="$BINUTILS_DIR/bfd/libbfd.a $BINUTILS_DIR/opcodes/libopcodes.a $BINUTILS_DIR/libiberty/libiberty.a $BINUTILS_DIR/zlib/libz.a" fi fi @@ -888,6 +892,33 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_HSDIS], fi ;; esac + elif test "x$with_hsdis" = xllvm; then + HSDIS_BACKEND=llvm + AC_MSG_RESULT(['llvm']) + + if test "x$with_llvm" != x; then + LLVM_DIR="$with_llvm" + fi + + if test "x$LLVM_DIR" != x; then + UTIL_LOOKUP_PROGS(LLVM_CONFIG, llvm-config, [$LLVM_DIR/bin]) + if test "x$LLVM_CONFIG" = x; then + AC_MSG_NOTICE([Cannot locate llvm-config in $LLVM_DIR. Check your --with-llvm argument.]) + AC_MSG_ERROR([Cannot continue]) + fi + else + # Macs with homebrew can have llvm in different places + UTIL_LOOKUP_PROGS(LLVM_CONFIG, llvm-config, [$PATH:/usr/local/opt/llvm/bin:/opt/homebrew/opt/llvm/bin]) + if test "x$LLVM_CONFIG" = x; then + AC_MSG_NOTICE([Cannot locate llvm-config which is needed for hsdis/llvm. Try using --with-llvm=.]) + AC_MSG_ERROR([Cannot continue]) + fi + fi + + # We need the LLVM flags and libs, and llvm-config provides them for us. + HSDIS_CFLAGS=`$LLVM_CONFIG --cflags` + HSDIS_LDFLAGS=`$LLVM_CONFIG --ldflags` + HSDIS_LIBS=`$LLVM_CONFIG --libs $OPENJDK_TARGET_CPU_ARCH ${OPENJDK_TARGET_CPU_ARCH}disassembler` else AC_MSG_RESULT([invalid]) AC_MSG_ERROR([Incorrect hsdis backend "$with_hsdis"]) @@ -895,5 +926,6 @@ AC_DEFUN_ONCE([JDKOPT_SETUP_HSDIS], AC_SUBST(HSDIS_BACKEND) AC_SUBST(HSDIS_CFLAGS) + AC_SUBST(HSDIS_LDFLAGS) AC_SUBST(HSDIS_LIBS) ]) diff --git a/make/autoconf/spec.gmk.in b/make/autoconf/spec.gmk.in index 3dce730970e7d..9e1bd6d4026ef 100644 --- a/make/autoconf/spec.gmk.in +++ b/make/autoconf/spec.gmk.in @@ -361,6 +361,7 @@ ALLOW_ABSOLUTE_PATHS_IN_OUTPUT := @ALLOW_ABSOLUTE_PATHS_IN_OUTPUT@ HSDIS_BACKEND := @HSDIS_BACKEND@ HSDIS_CFLAGS := @HSDIS_CFLAGS@ +HSDIS_LDFLAGS := @HSDIS_LDFLAGS@ HSDIS_LIBS := @HSDIS_LIBS@ # The boot jdk to use. This is overridden in bootcycle-spec.gmk. Make sure to keep diff --git a/src/utils/hsdis/README b/src/utils/hsdis/README index 64a8ab61a7c90..da710c68559db 100644 --- a/src/utils/hsdis/README +++ b/src/utils/hsdis/README @@ -1,4 +1,4 @@ -Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. +Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. The Universal Permissive License (UPL), Version 1.0 @@ -47,11 +47,41 @@ ________________________________________________________________________ The files in this directory are built independently of the HotSpot JVM. -* Building +hsdis is an interface exposed by Hotspot. There are several backends that +implement this interface, using different disassembly engines. Included in the +JDK is support for building hsdis with LLVM or GNU binutils.The interface is +fairly straightforward and easy to implement using other backends. -To build this project you need a copy of GNU binutils to build against. It is -known to work with binutils 2.37. Building against versions older than 2.29 is -not supported. Download a copy of the software from +* Building and installing + +To compile hsdis, you need to activate hsdis support, and select the proper +backend to use. This is done with the configure switch --with-hsdis=, +where is either "llvm" or "binutils". For details, see the sections on +the respective backends below. + +To build the hsdis library, run "make build-hsdis". This will build the library +in a separate directory, but not make it available to the JDK in the +configuration. To actually install it in the JDK, run "make install-hsdis". + +NOTE: If you do this using the binutils backend, he resulting build may not be +distributable. Please get legal advice if you intend to distribute the result of +your build. + +* Using the library + +The hsdis library will be automatically loaded by Hotspot when you use the +diagnostic option "-XX:+PrintAssembly". Note that since this is a diagnostic +option, you need to unlock these first, so in practice you activate it using +"-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly". + +More information is available at the wiki +[https://wiki.openjdk.java.net/display/HotSpot/PrintAssembly]. + +* Building with binutils + +To build this project using binutils you need a copy of GNU binutils to build +against. It is known to work with binutils 2.37. Building against versions older +than 2.29 is not supported. Download a copy of the software from http://directory.fsf.org/project/binutils or one of its mirrors. To build this library, you must enable building in configure by "bash configure @@ -71,10 +101,7 @@ If you want to build hsdis with binutils provided by system (e.g. binutils-devel from Fedora, binutils-dev from Ubuntu), you can pass "--with-binutils=system". "system" is available on Linux only. -When you have created a proper configuration, you can then build the hsdis -library using "make build-hsdis". - -* Building on Windows +* Building with binutils on Windows On Windows, the normal Microsoft Visual Studio toolchain cannot build binutils. Instead we need to use the mingw compiler. This is available as a cygwin @@ -82,21 +109,18 @@ package. You need to install the "gcc-core" and "mingw64-x86_64-gcc-core" packages (or "mingw64-i686-gcc-core", if you want the 32-bit version) and "mingw64-x86_64-glib2.0". -* Installing +* Building with LLVM -To build the hsdis library, run "make build-hsdis". This will build the library -in a separate directory, but not make it available to the JDK in the -configuration. To actually install it in the JDK, run "make install-hsdis". +To build this project using LLVM you need to have LLVM installed. Typical ways +of installation can be "sudo apt install llvm" (on Debian and derivatives), +or "brew install llvm" (on macOS with Homebrew). -Note: The resulting build may not be distributable. Please get legal advice if -you intend to distribute the result of your build. +This has been tested with LLVM v13.0.0, but earlier (and later) versions are +also likely to work. -* Using the library +To build hsdis using LLVM, you must enable it in configure by "bash configure +--with-hsdis=llvm". -The hsdis library will be automatically loaded by Hotspot when you use the -diagnostic option "-XX:+PrintAssembly". Note that since this is a diagnostic -option, you need to unlock these first, so in practice you activate it using -"-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly". - -More information is available at the wiki -[https://wiki.openjdk.java.net/display/HotSpot/PrintAssembly]. +If llvm-config is not in your path, you will need to specify the LLVM home using +"--with-llvm=". If your llvm-config is in ~/my-llvm/bin, then you +should use "--with-llvm=~/my-llvm". diff --git a/src/utils/hsdis/hsdis.c b/src/utils/hsdis/binutils/hsdis-binutils.c similarity index 100% rename from src/utils/hsdis/hsdis.c rename to src/utils/hsdis/binutils/hsdis-binutils.c diff --git a/src/utils/hsdis/hsdis.h b/src/utils/hsdis/hsdis.h index a6b45a57ba810..3fbc19040d7a5 100644 --- a/src/utils/hsdis/hsdis.h +++ b/src/utils/hsdis/hsdis.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -72,9 +72,14 @@ #ifndef SHARED_TOOLS_HSDIS_H #define SHARED_TOOLS_HSDIS_H +#ifdef __cplusplus +extern "C" +{ +#endif + extern -#ifdef DLL_EXPORT - DLL_EXPORT +#ifdef _WIN32 +__declspec(dllexport) #endif void* decode_instructions_virtual(uintptr_t start_va, uintptr_t end_va, unsigned char* buffer, uintptr_t length, @@ -87,8 +92,8 @@ void* decode_instructions_virtual(uintptr_t start_va, uintptr_t end_va, /* This is the compatability interface for older versions of hotspot */ extern -#ifdef DLL_ENTRY - DLL_ENTRY +#ifdef _WIN32 +__declspec(dllexport) #endif void* decode_instructions(void* start_pv, void* end_pv, void* (*event_callback)(void*, const char*, void*), @@ -115,4 +120,9 @@ typedef void* (*decode_func_stype) (void* start_pv, void* end_pv, decode_instructions_printf_callback_ftype printf_callback, void* printf_stream, const char* options); + +#ifdef __cplusplus +} +#endif + #endif /* SHARED_TOOLS_HSDIS_H */ diff --git a/src/utils/hsdis/llvm/hsdis-llvm.cpp b/src/utils/hsdis/llvm/hsdis-llvm.cpp new file mode 100644 index 0000000000000..179763d4deb61 --- /dev/null +++ b/src/utils/hsdis/llvm/hsdis-llvm.cpp @@ -0,0 +1,358 @@ +/* + * Copyright (c) 2008, 2021, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to + * any person obtaining a copy of this software, associated documentation + * and/or data (collectively the "Software"), free of charge and under any + * and all copyright rights in the Software, and any and all patent rights + * owned or freely licensable by each licensor hereunder covering either (i) + * the unmodified Software as contributed to or provided by such licensor, + * or (ii) the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file + * if one is included with the Software (each a "Larger Work" to which the + * Software is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, + * create derivative works of, display, perform, and distribute the Software + * and make, use, sell, offer for sale, import, export, have made, and have + * sold the Software and the Larger Work(s), and to sublicense the foregoing + * rights on either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or + * at a minimum a reference to the UPL must be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * 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. + * + */ + +/* hsdis.cpp -- dump a range of addresses as native instructions + This implements the plugin protocol required by the + HotSpot PrintAssembly option. +*/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "hsdis.h" + +/* short names for stuff in hsdis.h */ +typedef decode_instructions_event_callback_ftype event_callback_t; +typedef decode_instructions_printf_callback_ftype printf_callback_t; + +class hsdis_backend_base { + protected: + uintptr_t _start_va; + uintptr_t _end_va; + unsigned char* _buffer; + uintptr_t _length; + event_callback_t _event_callback; + void* _event_stream; + printf_callback_t _printf_callback; + void* _printf_stream; + int _do_newline; + + bool _losing; + const char* _arch_name; + + virtual void print_help(const char* msg, const char* arg) = 0; + virtual void print_insns_config() = 0; + virtual size_t decode_instruction(uintptr_t p, uintptr_t start, uintptr_t end) = 0; + virtual const char* format_insn_close(const char* close, char* buf, size_t bufsize) = 0; + + private: + /* ignore all events, return a null */ + static void* null_event_callback(void* ignore_stream, const char* ignore_event, void* arg) { + return NULL; + } + + /* print all events as XML markup */ + static void* xml_event_callback(void* stream, const char* event, void* arg) { + FILE* fp = (FILE*) stream; +#define NS_PFX "dis:" + if (event[0] != '/') { + /* issue the tag, with or without a formatted argument */ + fprintf(fp, "<" NS_PFX); + fprintf(fp, event, arg); + fprintf(fp, ">"); + } else { + ++event; /* skip slash */ + const char* argp = strchr(event, ' '); + if (argp == NULL) { + /* no arguments; just issue the closing tag */ + fprintf(fp, "", event); + } else { + /* split out the closing attributes as */ + int event_prefix = (argp - event); + fprintf(fp, "<" NS_PFX "%.*s_done", event_prefix, event); + fprintf(fp, argp, arg); + fprintf(fp, "/>", event_prefix, event); + } + } +#undef NS_PFX + return NULL; + } + +protected: + hsdis_backend_base(uintptr_t start_va, uintptr_t end_va, + unsigned char* buffer, uintptr_t length, + event_callback_t event_callback, void* event_stream, + printf_callback_t printf_callback, void* printf_stream, + int do_newline) : + _start_va(start_va), _end_va(end_va), + _buffer(buffer), _length(length), + _event_callback(event_callback), _event_stream(event_stream), + _printf_callback(printf_callback), _printf_stream(printf_stream), + _do_newline(do_newline), + _losing(false), _arch_name(NULL) + { + /* Make reasonable defaults for null callbacks. + A non-null stream for a null callback is assumed to be a FILE* for output. + Events are rendered as XML. + */ + if (_printf_callback == NULL) { + int (*fprintf_callback)(FILE*, const char*, ...) = &fprintf; + FILE* fprintf_stream = stdout; + _printf_callback = (printf_callback_t) fprintf_callback; + if (_printf_stream == NULL) + _printf_stream = (void*) fprintf_stream; + } + if (_event_callback == NULL) { + if (_event_stream == NULL) + _event_callback = (event_callback_t)&null_event_callback; + else + _event_callback = (event_callback_t)&xml_event_callback; + } + } + + public: + void* decode() { + uintptr_t start = _start_va; + uintptr_t end = _end_va; + uintptr_t p = start; + + (*_event_callback)(_event_stream, "insns", (void*)start); + + print_insns_config(); + + while (p < end && !_losing) { + (*_event_callback)(_event_stream, "insn", (void*) p); + + size_t size = decode_instruction(p, start, end); + if (size > 0) p += size; + else _losing = true; + + if (!_losing) { + char buf[128]; + const char* insn_close = format_insn_close("/insn", buf, sizeof(buf)); + (*_event_callback)(_event_stream, insn_close, (void*) p); + + if (_do_newline) { + /* follow each complete insn by a nice newline */ + (*_printf_callback)(_printf_stream, "\n"); + } + } + } + + if (_losing) (*_event_callback)(_event_stream, "/insns", (void*) p); + return (void*) p; + } +}; + + +class hsdis_backend : public hsdis_backend_base { + private: + LLVMDisasmContextRef _dcontext; + char _target_triple[128]; + + void parse_caller_options(const char* options) { + memset(&_target_triple, 0, sizeof(_target_triple)); + const char* p; + for (p = options; p != NULL; ) { + const char* q = strchr(p, ','); + size_t plen = (q == NULL) ? strlen(p) : ((q++) - p); + if (plen == 4 && strncmp(p, "help", plen) == 0) { + print_help(NULL, NULL); + } else if (plen > 6 && strncmp(p, "hsdis-", 6) == 0) { + // do not pass these to the next level + } else if (plen >= 14 && strncmp(p, "target_triple=", 14) == 0) { + char* target_triple = _target_triple; + size_t target_triple_size = sizeof(_target_triple); + target_triple_size -= 1; /*leave room for the null*/ + if (plen > target_triple_size) plen = target_triple_size; + strncpy(target_triple, p, plen); + target_triple[plen] = '\0'; + } + p = q; + } + } + + const char* native_target_triple() { + return LLVM_DEFAULT_TRIPLET; + } + + public: + hsdis_backend(uintptr_t start_va, uintptr_t end_va, + unsigned char* buffer, uintptr_t length, + event_callback_t event_callback, void* event_stream, + printf_callback_t printf_callback, void* printf_stream, + const char* options, int newline) + : hsdis_backend_base(start_va, end_va, + buffer, length, + event_callback, event_stream, + printf_callback, printf_stream, + newline), + _dcontext(NULL) { + /* Look into _options for anything interesting. */ + if (options != NULL) + parse_caller_options(options); + + /* Discover which architecture we are going to disassemble. */ + _arch_name = &_target_triple[0]; + if (_arch_name[0] == '\0') + _arch_name = native_target_triple(); + + if (LLVMInitializeNativeTarget() != 0) { + static bool complained = false; + if (!complained) + (*_printf_callback)(_printf_stream, "failed to initialize LLVM native target\n"); + complained = true; + /* must bail out */ + _losing = true; + return; + } + if (LLVMInitializeNativeAsmPrinter() != 0) { + static bool complained = false; + if (!complained) + (*_printf_callback)(_printf_stream, "failed to initialize LLVM native asm printer\n"); + complained = true; + /* must bail out */ + _losing = true; + return; + } + if (LLVMInitializeNativeDisassembler() != 0) { + static bool complained = false; + if (!complained) + (*_printf_callback)(_printf_stream, "failed to initialize LLVM native disassembler\n"); + complained = true; + /* must bail out */ + _losing = true; + return; + } + if ((_dcontext = LLVMCreateDisasm(_arch_name, NULL, 0, NULL, NULL)) == NULL) { + static bool complained = false; + const char* bad = _arch_name; + if (bad == &_target_triple[0]) + print_help("bad target_triple=%s", bad); + else if (!complained) + print_help("bad native target_triple=%s; please port hsdis to this platform", bad); + complained = true; + /* must bail out */ + _losing = true; + return; + } + + LLVMSetDisasmOptions(_dcontext, LLVMDisassembler_Option_PrintImmHex); + } + + ~hsdis_backend() { + if (_dcontext != NULL) { + LLVMDisasmDispose(_dcontext); + } + } + + protected: + virtual void print_help(const char* msg, const char* arg) { + if (msg != NULL) { + (*_printf_callback)(_printf_stream, "hsdis: "); + (*_printf_callback)(_printf_stream, msg, arg); + (*_printf_callback)(_printf_stream, "\n"); + } + (*_printf_callback)(_printf_stream, "hsdis output options:\n"); + (*_printf_callback)(_printf_stream, " target_triple= select disassembly target\n"); + (*_printf_callback)(_printf_stream, " help print this message\n"); + } + + virtual void print_insns_config() { + (*_event_callback)(_event_stream, "target_triple name='%s'", + (void*) _arch_name); + } + + virtual size_t decode_instruction(uintptr_t p, uintptr_t start, uintptr_t end) { + char buf[128]; + size_t size = LLVMDisasmInstruction(_dcontext, (uint8_t*)p, (uint64_t)(end - start), (uint64_t)p, buf, sizeof(buf)); + if (size > 0) { + (*_printf_callback)(_printf_stream, "%s", buf); + } else { + // LLVM encountered an unknown instruction + if (end - start >= 4) { + // Print the following word and skip past it + snprintf(buf, sizeof(buf), "\t.inst\t#0x%08x ; undefined", *(uint32_t*)p); + size = 4; + } else { + snprintf(buf, sizeof(buf), "\t"); + } + } + return size; + } + + virtual const char* format_insn_close(const char* close, char* buf, size_t bufsize) { + return close; + } +}; + + +void* decode_instructions_virtual(uintptr_t start_va, uintptr_t end_va, + unsigned char* buffer, uintptr_t length, + event_callback_t event_callback_arg, void* event_stream_arg, + printf_callback_t printf_callback_arg, void* printf_stream_arg, + const char* options, int newline) { + return hsdis_backend(start_va, end_va, + buffer, length, + event_callback_arg, event_stream_arg, + printf_callback_arg, printf_stream_arg, + options, newline == 0 ? false : true) + .decode(); +} + +/* This is the compatability interface for older version of hotspot */ +void* decode_instructions(void* start_pv, void* end_pv, + event_callback_t event_callback_arg, void* event_stream_arg, + printf_callback_t printf_callback_arg, void* printf_stream_arg, + const char* options) { + return decode_instructions_virtual((uintptr_t)start_pv, + (uintptr_t)end_pv, + (unsigned char*)start_pv, + (uintptr_t)end_pv - (uintptr_t)start_pv, + event_callback_arg, + event_stream_arg, + printf_callback_arg, + printf_stream_arg, + options, false); +}