/*
* 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
*
* 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 <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <inttypes.h>
#include <string.h>
#include <llvm-c/Disassembler.h>
#include <llvm-c/DisassemblerTypes.h>
#include <llvm-c/Target.h>
#include <llvm-c/TargetMachine.h>
#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, "" NS_PFX "%s>", event);
} else {
/* split out the closing attributes as <dis:foo_done attr='val'/> */
size_t event_prefix =(argp - event);
fprintf(fp, "<" NS_PFX "%.*s_done", (int) event_prefix, event);
fprintf(fp, argp, arg);
fprintf(fp, "/>" NS_PFX "%.*s>", (int) 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);
}
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet)
¤
|
Haftungshinweis
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.
|