550 lines
18 KiB
C
550 lines
18 KiB
C
/*
|
|
* Copyright (c) 2002, 2008, 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. Oracle designates this
|
|
* particular file as subject to the "Classpath" exception as provided
|
|
* by Oracle in the LICENSE file that accompanied this code.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
// Global Structures
|
|
struct jar;
|
|
struct gunzip;
|
|
struct band;
|
|
struct constant_pool;
|
|
struct entry;
|
|
struct cpindex;
|
|
struct inner_class;
|
|
struct value_stream;
|
|
|
|
typedef int64_t (*read_input_fn_t)(unpacker *self, void *buf, int64_t minlen, int64_t maxlen);
|
|
|
|
struct cpindex
|
|
{
|
|
uint32_t len;
|
|
entry *base1; // base of primary index
|
|
entry **base2; // base of secondary index
|
|
byte ixTag; // type of entries (!= CONSTANT_None), plus 64 if sub-index
|
|
enum
|
|
{
|
|
SUB_TAG = 64
|
|
};
|
|
|
|
entry *get(uint32_t i);
|
|
|
|
void init(int len_, entry *base1_, int ixTag_)
|
|
{
|
|
len = len_;
|
|
base1 = base1_;
|
|
base2 = nullptr;
|
|
ixTag = ixTag_;
|
|
}
|
|
void init(int len_, entry **base2_, int ixTag_)
|
|
{
|
|
len = len_;
|
|
base1 = nullptr;
|
|
base2 = base2_;
|
|
ixTag = ixTag_;
|
|
}
|
|
};
|
|
|
|
struct constant_pool
|
|
{
|
|
uint32_t nentries;
|
|
entry *entries;
|
|
entry *first_extra_entry;
|
|
uint32_t maxentries; // total allocated size of entries
|
|
|
|
// Position and size of each homogeneous subrange:
|
|
int tag_count[CONSTANT_Limit];
|
|
int tag_base[CONSTANT_Limit];
|
|
cpindex tag_index[CONSTANT_Limit];
|
|
ptrlist tag_extras[CONSTANT_Limit];
|
|
|
|
cpindex *member_indexes; // indexed by 2*CONSTANT_Class.inord
|
|
cpindex *getFieldIndex(entry *classRef);
|
|
cpindex *getMethodIndex(entry *classRef);
|
|
|
|
inner_class **ic_index;
|
|
inner_class **ic_child_index;
|
|
inner_class *getIC(entry *inner);
|
|
inner_class *getFirstChildIC(entry *outer);
|
|
inner_class *getNextChildIC(inner_class *child);
|
|
|
|
int outputIndexLimit; // index limit after renumbering
|
|
ptrlist outputEntries; // list of entry* needing output idx assigned
|
|
|
|
entry **hashTab;
|
|
uint32_t hashTabLength;
|
|
entry *&hashTabRef(byte tag, bytes &b);
|
|
entry *ensureUtf8(bytes &b);
|
|
entry *ensureClass(bytes &b);
|
|
|
|
// Well-known Utf8 symbols.
|
|
enum
|
|
{
|
|
#define SNAME(n, s) s_##s,
|
|
ALL_ATTR_DO(SNAME)
|
|
#undef SNAME
|
|
s_lt_init_gt, // <init>
|
|
s_LIMIT
|
|
};
|
|
entry *sym[s_LIMIT];
|
|
|
|
// read counts from hdr, allocate main arrays
|
|
enum
|
|
{
|
|
NUM_COUNTS = 12
|
|
};
|
|
void init(unpacker *u, int counts[NUM_COUNTS]);
|
|
|
|
// pointer to outer unpacker, for error checks etc.
|
|
unpacker *u;
|
|
|
|
int getCount(byte tag)
|
|
{
|
|
assert((uint32_t)tag < CONSTANT_Limit);
|
|
return tag_count[tag];
|
|
}
|
|
cpindex *getIndex(byte tag)
|
|
{
|
|
assert((uint32_t)tag < CONSTANT_Limit);
|
|
return &tag_index[tag];
|
|
}
|
|
cpindex *getKQIndex(); // uses cur_descr
|
|
|
|
void expandSignatures();
|
|
void initMemberIndexes();
|
|
|
|
void computeOutputOrder();
|
|
void computeOutputIndexes();
|
|
void resetOutputIndexes();
|
|
};
|
|
|
|
/*
|
|
* The unpacker provides the entry points to the unpack engine,
|
|
* as well as maintains the state of the engine.
|
|
*/
|
|
struct unpacker
|
|
{
|
|
// One element of the resulting JAR.
|
|
struct file
|
|
{
|
|
const char *name;
|
|
uint64_t size;
|
|
int modtime;
|
|
int options;
|
|
bytes data[2];
|
|
// Note: If Sum(data[*].len) < size,
|
|
// remaining bytes must be read directly from the input stream.
|
|
bool deflate_hint()
|
|
{
|
|
return ((options & FO_DEFLATE_HINT) != 0);
|
|
}
|
|
};
|
|
|
|
// if running Unix-style, here are the inputs and outputs
|
|
FILE *infileptr; // buffered
|
|
bytes inbytes; // direct
|
|
gunzip *gzin; // gunzip filter, if any
|
|
jar *jarout; // output JAR file
|
|
|
|
// pointer to self, for U_NEW macro
|
|
unpacker *u;
|
|
|
|
ptrlist mallocs; // list of guys to free when we are all done
|
|
ptrlist tmallocs; // list of guys to free on next client request
|
|
fillbytes smallbuf; // supplies small alloc requests
|
|
fillbytes tsmallbuf; // supplies temporary small alloc requests
|
|
|
|
// option management members
|
|
int verbose; // verbose level, 0 means no output
|
|
int deflate_hint_or_zero; // ==0 means not set, otherwise -1 or 1
|
|
int modification_time_or_zero;
|
|
|
|
// input stream
|
|
fillbytes input; // the whole block (size is predicted, has slop too)
|
|
bool live_input; // is the data in this block live?
|
|
bool free_input; // must the input buffer be freed?
|
|
byte *rp; // read pointer (< rplimit <= input.limit())
|
|
byte *rplimit; // how much of the input block has been read?
|
|
uint64_t bytes_read;
|
|
int unsized_bytes_read;
|
|
|
|
// callback to read at least one byte, up to available input
|
|
read_input_fn_t read_input_fn;
|
|
|
|
// archive header fields
|
|
int magic, minver, majver;
|
|
size_t archive_size;
|
|
int archive_next_count, archive_options, archive_modtime;
|
|
int band_headers_size;
|
|
int file_count, attr_definition_count, ic_count, class_count;
|
|
int default_class_minver, default_class_majver;
|
|
int default_file_options, suppress_file_options; // not header fields
|
|
int default_archive_modtime, default_file_modtime; // not header fields
|
|
int code_count; // not a header field
|
|
int files_remaining; // not a header field
|
|
|
|
// engine state
|
|
band *all_bands; // indexed by band_number
|
|
byte *meta_rp; // read-pointer into (copy of) band_headers
|
|
constant_pool cp; // all constant pool information
|
|
inner_class *ics; // InnerClasses
|
|
|
|
// output stream
|
|
bytes output; // output block (either classfile head or tail)
|
|
byte *wp; // write pointer (< wplimit == output.limit())
|
|
byte *wpbase; // write pointer starting address (<= wp)
|
|
byte *wplimit; // how much of the output block has been written?
|
|
|
|
// output state
|
|
file cur_file;
|
|
entry *cur_class; // CONSTANT_Class entry
|
|
entry *cur_super; // CONSTANT_Class entry or nullptr
|
|
entry *cur_descr; // CONSTANT_NameandType entry
|
|
int cur_descr_flags; // flags corresponding to cur_descr
|
|
int cur_class_minver, cur_class_majver;
|
|
bool cur_class_has_local_ics;
|
|
fillbytes cur_classfile_head;
|
|
fillbytes cur_classfile_tail;
|
|
int files_written; // also tells which file we're working on
|
|
int classes_written; // also tells which class we're working on
|
|
uint64_t bytes_written;
|
|
intlist bcimap;
|
|
fillbytes class_fixup_type;
|
|
intlist class_fixup_offset;
|
|
ptrlist class_fixup_ref;
|
|
fillbytes code_fixup_type; // which format of branch operand?
|
|
intlist code_fixup_offset; // location of operand needing fixup
|
|
intlist code_fixup_source; // encoded ID of branch insn
|
|
ptrlist requested_ics; // which ics need output?
|
|
|
|
// stats pertaining to multiple segments (updated on reset)
|
|
uint64_t bytes_read_before_reset;
|
|
uint64_t bytes_written_before_reset;
|
|
int files_written_before_reset;
|
|
int classes_written_before_reset;
|
|
int segments_read_before_reset;
|
|
|
|
// attribute state
|
|
struct layout_definition
|
|
{
|
|
uint32_t idx; // index (0..31...) which identifies this layout
|
|
const char *name; // name of layout
|
|
entry *nameEntry;
|
|
const char *layout; // string of layout (not yet parsed)
|
|
band **elems; // array of top-level layout elems (or callables)
|
|
|
|
bool hasCallables()
|
|
{
|
|
return layout[0] == '[';
|
|
}
|
|
band **bands()
|
|
{
|
|
assert(elems != nullptr);
|
|
return elems;
|
|
}
|
|
};
|
|
struct attr_definitions
|
|
{
|
|
unpacker *u; // pointer to self, for U_NEW macro
|
|
int xxx_flags_hi_bn; // locator for flags, count, indexes, calls bands
|
|
int attrc; // ATTR_CONTEXT_CLASS, etc.
|
|
uint32_t flag_limit; // 32 or 63, depending on archive_options bit
|
|
uint64_t predef; // mask of built-in definitions
|
|
uint64_t redef; // mask of local flag definitions or redefinitions
|
|
ptrlist layouts; // local (compressor-defined) defs, in index order
|
|
int flag_count[X_ATTR_LIMIT_FLAGS_HI];
|
|
intlist overflow_count;
|
|
ptrlist strip_names; // what attribute names are being stripped?
|
|
ptrlist band_stack; // Temp., used during layout parsing.
|
|
ptrlist calls_to_link; // (ditto)
|
|
int bands_made; // (ditto)
|
|
|
|
void free()
|
|
{
|
|
layouts.free();
|
|
overflow_count.free();
|
|
strip_names.free();
|
|
band_stack.free();
|
|
calls_to_link.free();
|
|
}
|
|
|
|
// Locate the five fixed bands.
|
|
band &xxx_flags_hi();
|
|
band &xxx_flags_lo();
|
|
band &xxx_attr_count();
|
|
band &xxx_attr_indexes();
|
|
band &xxx_attr_calls();
|
|
band &fixed_band(int e_class_xxx);
|
|
|
|
// Register a new layout, and make bands for it.
|
|
layout_definition *defineLayout(int idx, const char *name, const char *layout);
|
|
layout_definition *defineLayout(int idx, entry *nameEntry, const char *layout);
|
|
band **buildBands(layout_definition *lo);
|
|
|
|
// Parse a layout string or part of one, recursively if necessary.
|
|
const char *parseLayout(const char *lp, band **&res, int curCble);
|
|
const char *parseNumeral(const char *lp, int &res);
|
|
const char *parseIntLayout(const char *lp, band *&res, byte le_kind,
|
|
bool can_be_signed = false);
|
|
band **popBody(int band_stack_base); // pops a body off band_stack
|
|
|
|
// Read data into the bands of the idx-th layout.
|
|
void readBandData(int idx); // parse layout, make bands, read data
|
|
void readBandData(band **body, uint32_t count); // recursive helper
|
|
|
|
layout_definition *getLayout(uint32_t idx)
|
|
{
|
|
if (idx >= (uint32_t)layouts.length())
|
|
return nullptr;
|
|
return (layout_definition *)layouts.get(idx);
|
|
}
|
|
|
|
void setHaveLongFlags(bool z)
|
|
{
|
|
assert(flag_limit == 0); // not set up yet
|
|
flag_limit = (z ? X_ATTR_LIMIT_FLAGS_HI : X_ATTR_LIMIT_NO_FLAGS_HI);
|
|
}
|
|
bool haveLongFlags()
|
|
{
|
|
assert(flag_limit == X_ATTR_LIMIT_NO_FLAGS_HI ||
|
|
flag_limit == X_ATTR_LIMIT_FLAGS_HI);
|
|
return flag_limit == X_ATTR_LIMIT_FLAGS_HI;
|
|
}
|
|
|
|
// Return flag_count if idx is predef and not redef, else zero.
|
|
int predefCount(uint32_t idx);
|
|
|
|
bool isRedefined(uint32_t idx)
|
|
{
|
|
if (idx >= flag_limit)
|
|
return false;
|
|
return (bool)((redef >> idx) & 1);
|
|
}
|
|
bool isPredefined(uint32_t idx)
|
|
{
|
|
if (idx >= flag_limit)
|
|
return false;
|
|
return (bool)(((predef & ~redef) >> idx) & 1);
|
|
}
|
|
uint64_t flagIndexMask()
|
|
{
|
|
return (predef | redef);
|
|
}
|
|
bool isIndex(uint32_t idx)
|
|
{
|
|
assert(flag_limit != 0); // must be set up already
|
|
if (idx < flag_limit)
|
|
return (bool)(((predef | redef) >> idx) & 1);
|
|
else
|
|
return (idx - flag_limit < (uint32_t)overflow_count.length());
|
|
}
|
|
int &getCount(uint32_t idx)
|
|
{
|
|
assert(isIndex(idx));
|
|
if (idx < flag_limit)
|
|
return flag_count[idx];
|
|
else
|
|
return overflow_count.get(idx - flag_limit);
|
|
}
|
|
};
|
|
|
|
attr_definitions attr_defs[ATTR_CONTEXT_LIMIT];
|
|
|
|
// Initialization
|
|
void init(read_input_fn_t input_fn = nullptr);
|
|
// Resets to a known sane state
|
|
void reset();
|
|
// Deallocates all storage.
|
|
void free();
|
|
// Deallocates temporary storage (volatile after next client call).
|
|
void free_temps()
|
|
{
|
|
tsmallbuf.init();
|
|
tmallocs.freeAll();
|
|
}
|
|
|
|
// Option management methods
|
|
bool set_option(const char *option, const char *value);
|
|
const char *get_option(const char *option);
|
|
|
|
// Fetching input.
|
|
bool ensure_input(int64_t more);
|
|
byte *input_scan()
|
|
{
|
|
return rp;
|
|
}
|
|
size_t input_remaining()
|
|
{
|
|
return rplimit - rp;
|
|
}
|
|
size_t input_consumed()
|
|
{
|
|
return rp - input.base();
|
|
}
|
|
|
|
// Entry points to the unpack engine
|
|
static int run(int argc, char **argv); // Unix-style entry point.
|
|
void check_options();
|
|
void start(void *packptr = nullptr, size_t len = 0);
|
|
void write_file_to_jar(file *f);
|
|
void finish();
|
|
|
|
// Public post unpack methods
|
|
int get_files_remaining()
|
|
{
|
|
return files_remaining;
|
|
}
|
|
int get_segments_remaining()
|
|
{
|
|
return archive_next_count;
|
|
}
|
|
file *get_next_file(); // returns nullptr on last file
|
|
|
|
// General purpose methods
|
|
void *alloc(size_t size)
|
|
{
|
|
return alloc_heap(size, true);
|
|
}
|
|
void *temp_alloc(size_t size)
|
|
{
|
|
return alloc_heap(size, true, true);
|
|
}
|
|
void *alloc_heap(size_t size, bool smallOK = false, bool temp = false);
|
|
void saveTo(bytes &b, const char *str)
|
|
{
|
|
saveTo(b, (byte *)str, strlen(str));
|
|
}
|
|
void saveTo(bytes &b, bytes &data)
|
|
{
|
|
saveTo(b, data.ptr, data.len);
|
|
}
|
|
void saveTo(bytes &b, byte *ptr, size_t len); //{ b.ptr = U_NEW...}
|
|
const char *saveStr(const char *str)
|
|
{
|
|
bytes buf;
|
|
saveTo(buf, str);
|
|
return buf.strval();
|
|
}
|
|
const char *saveIntStr(int num)
|
|
{
|
|
char buf[30];
|
|
sprintf(buf, "%d", num);
|
|
return saveStr(buf);
|
|
}
|
|
static unpacker *current(); // find current instance
|
|
|
|
// Output management
|
|
void set_output(fillbytes *which)
|
|
{
|
|
assert(wp == nullptr);
|
|
which->ensureSize(1 << 12); // covers the average classfile
|
|
wpbase = which->base();
|
|
wp = which->limit();
|
|
wplimit = which->end();
|
|
}
|
|
fillbytes *close_output(fillbytes *which = nullptr); // inverse of set_output
|
|
|
|
// These take an implicit parameter of wp/wplimit, and resize as necessary:
|
|
byte *put_space(size_t len); // allocates space at wp, returns pointer
|
|
size_t put_empty(size_t s)
|
|
{
|
|
byte *p = put_space(s);
|
|
return p - wpbase;
|
|
}
|
|
void ensure_put_space(size_t len);
|
|
void put_bytes(bytes &b)
|
|
{
|
|
b.writeTo(put_space(b.len));
|
|
}
|
|
void putu1(int n)
|
|
{
|
|
putu1_at(put_space(1), n);
|
|
}
|
|
void putu1_fast(int n)
|
|
{
|
|
putu1_at(wp++, n);
|
|
}
|
|
void putu2(int n); // { putu2_at(put_space(2), n); }
|
|
void putu4(int n); // { putu4_at(put_space(4), n); }
|
|
void putu8(int64_t n); // { putu8_at(put_space(8), n); }
|
|
void putref(entry *e); // { putu2_at(put_space(2), putref_index(e, 2)); }
|
|
void putu1ref(entry *e); // { putu1_at(put_space(1), putref_index(e, 1)); }
|
|
int putref_index(entry *e, int size); // size in [1..2]
|
|
void put_label(int curIP, int size); // size in {2,4}
|
|
void putlayout(band **body);
|
|
void put_stackmap_type();
|
|
|
|
size_t wpoffset()
|
|
{
|
|
return (size_t)(wp - wpbase);
|
|
} // (unvariant across overflow)
|
|
byte *wp_at(size_t offset)
|
|
{
|
|
return wpbase + offset;
|
|
}
|
|
uint32_t to_bci(uint32_t bii);
|
|
void get_code_header(int &max_stack, int &max_na_locals, int &handler_count, int &cflags);
|
|
band *ref_band_for_self_op(int bc, bool &isAloadVar, int &origBCVar);
|
|
band *ref_band_for_op(int bc);
|
|
|
|
// Definitions of standard classfile int formats:
|
|
static void putu1_at(byte *wp, int n)
|
|
{
|
|
assert(n == (n & 0xFF));
|
|
wp[0] = n;
|
|
}
|
|
static void putu2_at(byte *wp, int n);
|
|
static void putu4_at(byte *wp, int n);
|
|
static void putu8_at(byte *wp, int64_t n);
|
|
|
|
// Private stuff
|
|
void reset_cur_classfile();
|
|
void write_classfile_tail();
|
|
void write_classfile_head();
|
|
void write_code();
|
|
void write_bc_ops();
|
|
void write_members(int num, int attrc); // attrc=ATTR_CONTEXT_FIELD/METHOD
|
|
int write_attrs(int attrc, uint64_t indexBits);
|
|
|
|
// The readers
|
|
void read_bands();
|
|
void read_file_header();
|
|
void read_cp();
|
|
void read_cp_counts(value_stream &hdr);
|
|
void read_attr_defs();
|
|
void read_ics();
|
|
void read_attrs(int attrc, int obj_count);
|
|
void read_classes();
|
|
void read_code_headers();
|
|
void read_bcs();
|
|
void read_bc_ops();
|
|
void read_files();
|
|
void read_Utf8_values(entry *cpMap, int len);
|
|
void read_single_words(band &cp_band, entry *cpMap, int len);
|
|
void read_double_words(band &cp_bands, entry *cpMap, int len);
|
|
void read_single_refs(band &cp_band, byte refTag, entry *cpMap, int len);
|
|
void read_double_refs(band &cp_band, byte ref1Tag, byte ref2Tag, entry *cpMap, int len);
|
|
void read_signature_values(entry *cpMap, int len);
|
|
};
|