/*
* Nastroj pro vypis vsech udaju ulozenych v constant poolu v souborech .class
*
* Pavel Tisnovsky 2011
*
* Preklad;
* gcc -ansi -Wall decompiler.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/* Odkomentujte pro zakaz tisku retezcovych konstant */
/*#define DONT_PRINT_STRING_ENTRIES*/
/*
* Typy zaznamu ulozenych v constant poolu.
* Viz dokumentace k JVM.
*/
enum
{
CONSTANT_Class = 7,
CONSTANT_Fieldref = 9,
CONSTANT_Methodref = 10,
CONSTANT_InterfaceMethodref = 11,
CONSTANT_String = 8,
CONSTANT_Integer = 3,
CONSTANT_Float = 4,
CONSTANT_Long = 5,
CONSTANT_Double = 6,
CONSTANT_NameAndType = 12,
CONSTANT_Utf8 = 1
};
/*
* Struktura reprezentujici jeden zaznam v constant poolu.
* (nektere polozky by bylo mozne sloucit a zmensit tak pametove naroky za cenu vetsi necitelnosti)
*/
typedef struct
{
uint8_t tag; /* typ zaznamu */
uint16_t class_index; /* odkaz na jmeno tridy */
uint16_t name_type_index; /* odkaz na typ metody/atributu... */
uint16_t name_index; /* odkaz na jmeno metody/atributu... */
uint16_t descriptor_index; /* odkaz na identifikaci typu */
uint16_t string_length; /* delka retezcoveho literalu */
char *string; /* retezec (v programu se ukoncuje nulou, v constant poolu ma zadanou delku) */
union /* konstanta o velikosti 4 bajty */
{ /* zjednoduseni pres unii, aby bylo mozne jednoduse provadet prevody */
uint32_t read_value; /* platne pro little endian procesory!!! */
int32_t int_value;
float float_value;
} four_bytes;
union /* konstanta o velikosti 8 bajtu */
{ /* zjednoduseni pres unii, aby bylo mozne jednoduse provadet prevody */
struct {
uint32_t lower_word; /* platne pro little endian procesory */
uint32_t higher_word;
} read_values;
int64_t long_value;
double double_value;
} eight_bytes;
} PoolEntry;
/*
* Pole obsahujici cely constant pool.
*/
PoolEntry* pool_entries = NULL;
/*
* Funkce zavolana v pripade, ze pri cteni ze souboru dojde k chybe.
*/
void read_error(FILE *fin)
{
printf("Chyba pri cteni bajtu na offsetu %ld\n", ftell(fin));
if (fclose(fin))
{
puts("... a uzavreni souboru taky selhalo!");
}
exit(1);
}
/*
* Precteni jednoho bajtu ze souboru s kontrolou, zda nedoslo k chybe.
*/
uint8_t read_byte(FILE *fin)
{
int i = fgetc(fin);
if (i == EOF)
{
read_error(fin);
}
return i;
}
/*
* Precteni dvou bajtu ze souboru s kontrolou, zda nedoslo k chybe.
* V souborech .class se pouziva poradi bajtu typu big endian.
*/
uint16_t read_two_bytes(FILE *fin)
{
int i1 = read_byte(fin);
int i2 = read_byte(fin);
return (i1 << 8) | (i2);
}
/*
* Precteni ctyr bajtu ze souboru s kontrolou, zda nedoslo k chybe.
* V souborech .class se pouziva poradi bajtu typu big endian.
*/
uint32_t read_four_bytes(FILE *fin)
{
int i1 = read_byte(fin);
int i2 = read_byte(fin);
int i3 = read_byte(fin);
int i4 = read_byte(fin);
return (i1 << 24) | (i2 << 16) | (i3 << 8) | (i4);
}
/*
* Otevreni souboru .class se zakladni kontrolou, zda nedoslo k chybe.
*/
FILE* open_class_file(const char *file_name)
{
FILE *fin;
fin = fopen(file_name, "rb");
if (!fin)
{
puts("Chyba pri otevirani souboru");
exit(1);
}
return fin;
}
/*
* Nacteni a zobrazeni magicke konstanty.
*/
void process_magic_number(FILE *fin)
{
uint32_t magic = read_four_bytes(fin);
printf("Magicka konstanta: %08x\n", magic);
if (magic != 0xcafebabe)
{
puts("Spatne magicka konstanta (jedna se skutecne o .class?)");
fclose(fin);
exit(1);
}
}
/*
* Nacteni a zobrazeni verze .class souboru.
*/
void process_class_version(FILE *fin)
{
uint16_t minor_version;
uint16_t major_version;
minor_version = read_two_bytes(fin);
major_version = read_two_bytes(fin);
printf("Cislo verze: %d\n", major_version);
printf("Cislo podverze: %d\n", minor_version);
}
/*
* Nacteni jednoho zaznamu z constant poolu. Funkce vraci 1 nebo 2
* podle toho, zda ma byt dalsi zaznam umisten ihned za prectenym
* zaznamem, nebo ma byt za prectenym zaznamem "mezera" (to kvuli
* osmibajtovym konstantam, ktere "genialne" obsazuji dva sloty)
*/
int read_constant_pool_entry(FILE *fin, PoolEntry *pool_entry)
{
uint8_t tag;
uint32_t length;
uint32_t i;
tag = read_byte(fin); /* nejdulezitejsi informace - typ zaznamu */
pool_entry->tag = tag;
switch (tag)
{
case CONSTANT_Class: /* tento zaznam obsahuje jednu referenci */
pool_entry->class_index = read_two_bytes(fin);
break;
case CONSTANT_Fieldref: /* tento zaznam obsahuje dve reference */
case CONSTANT_Methodref:
case CONSTANT_InterfaceMethodref:
pool_entry->class_index = read_two_bytes(fin);
pool_entry->name_type_index = read_two_bytes(fin);
break;
case CONSTANT_String: /* tento tento zaznam obsahuje jednu referenci */
pool_entry->class_index = read_two_bytes(fin);
break;
case CONSTANT_Integer: /* tento zaznam obsahuje ctyrbajtovou konstantu */
case CONSTANT_Float:
pool_entry->four_bytes.read_value = read_four_bytes(fin);
break;
case CONSTANT_Long: /* tento zaznam obsahuje osmibajtovou konstantu */
case CONSTANT_Double: /* navic musi byt za zaznamem jedno volne misto */
pool_entry->eight_bytes.read_values.higher_word = read_four_bytes(fin);
pool_entry->eight_bytes.read_values.lower_word = read_four_bytes(fin);
return 2;
break;
case CONSTANT_NameAndType: /* tento zaznam obsahuje dve reference */
pool_entry->name_index = read_two_bytes(fin);
pool_entry->descriptor_index = read_two_bytes(fin);
break;
case CONSTANT_Utf8: /* string v kodovani UTF-8 */
length = read_two_bytes(fin);
pool_entry->string_length = length;
pool_entry->string = (char*)malloc(sizeof(char) * (length+1));
for (i = 0; i < length; i++) /* nacteme cely retezec... */
{
pool_entry->string[i] = read_byte(fin);
}
pool_entry->string[i] = 0; /* ... a ukoncime ho nulou pro snadny tisk v printf */
break;
default:
printf("Neznamy tag precteny z const. poolu: %d\n", tag);
break;
}
return 1;
}
/*
* Tisk zaznamu typu "Class".
*/
void print_class_info(PoolEntry *pool_entry)
{
/* pouziti reference na jmeno tridy */
char *class_name = pool_entries[pool_entry->class_index-1].string;
printf("Class %3d %s", pool_entry->class_index, class_name);
}
/*
* Tisk zaznamu typu "Field reference"
*/
void print_field_ref(PoolEntry *pool_entry)
{
int class_index = pool_entry->class_index;
int name_type_index = pool_entry->name_type_index;
/* ziskani jmena tridy pres jednu referenci */
char *class_name = pool_entries[pool_entries[class_index - 1].class_index-1].string;
/* ziskani typu a jmena atributu pres dve reference */
char *name = pool_entries[pool_entries[name_type_index - 1].name_index-1].string;
char *descriptor = pool_entries[pool_entries[name_type_index - 1].descriptor_index-1].string;
printf("FieldRef %3d %3d %s.%s:%s", class_index, name_type_index, class_name, name, descriptor);
}
/*
* Tisk zaznamu typu "Method reference"
*/
void print_method_ref(PoolEntry *pool_entry)
{
int class_index = pool_entry->class_index;
int name_type_index = pool_entry->name_type_index;
/* ziskani jmena tridy pres jednu referenci */
char *class_name = pool_entries[pool_entries[class_index - 1].class_index-1].string;
/* ziskani typu a jmena atributu pres dve reference */
char *name = pool_entries[pool_entries[name_type_index - 1].name_index-1].string;
char *descriptor = pool_entries[pool_entries[name_type_index - 1].descriptor_index-1].string;
printf("MethodRef %3d %3d %s.%s%s", class_index, name_type_index, class_name, name, descriptor);
}
/*
* Tisk zaznamu typu "Interface method reference"
*/
void print_interface_method_ref(PoolEntry *pool_entry)
{
int class_index = pool_entry->class_index;
int name_type_index = pool_entry->name_type_index;
/* ziskani jmena tridy pres jednu referenci */
char *class_name = pool_entries[pool_entries[class_index - 1].class_index-1].string;
/* ziskani typu a jmena atributu pres dve reference */
char *name = pool_entries[pool_entries[name_type_index - 1].name_index-1].string;
char *descriptor = pool_entries[pool_entries[name_type_index - 1].descriptor_index-1].string;
printf("IfaceMethodRef %3d %3d %s.%s%s", class_index, name_type_index, class_name, name, descriptor);
}
/*
* Tisk zaznamu typu "String"
*/
void print_string_const(PoolEntry *pool_entry)
{
/* pouziti reference na obsah retezce */
char *string = pool_entries[pool_entry->class_index-1].string;
printf("String const %3d \"%s\"", pool_entry->class_index, string);
}
/*
* Tisk zaznamu typu "Integer constant"
*/
void print_integer_const(PoolEntry *pool_entry)
{
printf("%-26s %d", "Integer", pool_entry->four_bytes.int_value);
}
/*
* Tisk zaznamu typu "Float constant"
*/
void print_float_const(PoolEntry *pool_entry)
{
printf("%-26s %f", "Float", pool_entry->four_bytes.float_value);
}
/*
* Tisk zaznamu typu "Long constant"
*/
void print_long_const(PoolEntry *pool_entry)
{
printf("%-26s %lld", "Long", pool_entry->eight_bytes.long_value);
}
/*
* Tisk zaznamu typu "Double constant"
*/
void print_double_const(PoolEntry *pool_entry)
{
printf("%-26s %f", "Double", pool_entry->eight_bytes.double_value);
}
/*
* Tisk zaznamu typu "Type and name"
*/
void print_type_and_name(PoolEntry *pool_entry)
{
/* zjistit oba odkazy */
int name_index = pool_entry->name_index;
int descriptor_index = pool_entry->descriptor_index;
/* z odkazu ziskat oba retezce */
char *name_string = pool_entries[name_index-1].string;
char *descriptor_string = pool_entries[descriptor_index-1].string;
printf("Name and type %3d %3d %s %s", name_index, descriptor_index, name_string, descriptor_string);
}
/*
* Tisk zaznamu typu "UTF-8 string"
*/
void print_utf8_string(PoolEntry *pool_entry)
{
printf("String \"%s\"", pool_entry->string);
}
/*
* Tisk hodnoty jednoho zaznamu precteneho z constant poolu.
*/
void print_constant_pool_entry(int item_index, PoolEntry *pool_entry)
{
#ifdef DONT_PRINT_STRING_ENTRIES
if (pool_entry->tag != CONSTANT_Utf8) {
#endif
printf("%3d %3d ", item_index+1, pool_entry->tag);
#ifdef DONT_PRINT_STRING_ENTRIES
}
#endif
switch (pool_entry->tag)
{
case CONSTANT_Class:
print_class_info(pool_entry);
break;
case CONSTANT_Fieldref:
print_field_ref(pool_entry);
break;
case CONSTANT_Methodref:
print_method_ref(pool_entry);
break;
case CONSTANT_InterfaceMethodref:
print_interface_method_ref(pool_entry);
break;
case CONSTANT_String:
print_string_const(pool_entry);
break;
case CONSTANT_Integer:
print_integer_const(pool_entry);
break;
case CONSTANT_Float:
print_float_const(pool_entry);
break;
case CONSTANT_Long:
print_long_const(pool_entry);
break;
case CONSTANT_Double:
print_double_const(pool_entry);
break;
case CONSTANT_NameAndType:
print_type_and_name(pool_entry);
break;
case CONSTANT_Utf8:
#ifndef DONT_PRINT_STRING_ENTRIES
print_utf8_string(pool_entry);
#endif
break;
default:
printf("Neznamy tag");
break;
}
#ifdef DONT_PRINT_STRING_ENTRIES
if (pool_entry->tag != CONSTANT_Utf8) {
#endif
putchar('\n');
#ifdef DONT_PRINT_STRING_ENTRIES
}
#endif
}
/*
* Alokace pameti pro ulozeni informaci prectenych z constant poolu s inicializaci.
*/
void create_constant_pool(int length)
{
int i;
/* alokace pameti pro cele dynamicke pole */
pool_entries = (PoolEntry*) malloc(length * sizeof(PoolEntry));
/* inicializace vsech dulezitych polozek ve strukture */
for (i = 0; i < length; i++)
{
PoolEntry *pool_entry = pool_entries + i;
pool_entry->tag = 0;
pool_entry->class_index = 0;
pool_entry->name_type_index = 0;
pool_entry->name_index = 0;
pool_entry->descriptor_index = 0;
pool_entry->string_length = 0;
pool_entry->string = NULL;
}
}
/*
* Dealokace pameti s informacemi prectenymi z constant poolu.
*/
void delete_constant_pool(int length)
{
int i;
/* dulezite je dealokovat vsechny retezce */
for (i = 0; i < length; i++)
{
PoolEntry *pool_entry = pool_entries + i;
if (pool_entry->string)
{
free(pool_entry->string);
}
}
/* a nasledne vlastni pole */
free(pool_entries);
}
/*
* Nacteni vsech zaznamu z constant poolu a nasledne tisk jeho obsahu.
* Tuto operaci je nutne rozdelit na dve casti, jinak by nebylo mozne
* provadet "referencovani" mezi jednotlivymi polozkami (jedna se mnohdy
* o dopredne reference).
*/
void process_constant_pool(FILE *fin)
{
uint16_t constant_pool_count;
int i;
/* pocet polozek v constant poolu */
constant_pool_count = read_two_bytes(fin);
constant_pool_count--;
printf("\nVelikost const. poolu: %d prvku\n", constant_pool_count);
/* alokace pameti + inicializace */
create_constant_pool(constant_pool_count);
/* nacteni prvku je nutne provadet ve while, kvuli ne zcela
* inteligentnimu zpusobu ulozeni konstant typu long a double.
*/
i = 0;
while (i < constant_pool_count)
{
i += read_constant_pool_entry(fin, pool_entries+i);
}
/* tisk polozek */
for (i = 0; i < constant_pool_count; i++)
{
print_constant_pool_entry(i, pool_entries+i);
}
/* rucni GC :-) */
delete_constant_pool(constant_pool_count);
}
/*
* Zpracovani constant poolu ulozeneho v souboru .class
*/
void process_whole_class(FILE *fin)
{
process_magic_number(fin);
process_class_version(fin);
process_constant_pool(fin);
}
/*
* Vstup do programu.
* V prvnim argumentu je ocekavano jmeno .class souboru
*/
int main(int argc, char** argv)
{
FILE *fin;
if (argc != 2)
{
puts("Pouziti: a.out trida.class");
return 1;
}
fin = open_class_file(argv[1]);
process_whole_class(fin);
if (fclose(fin))
{
puts("Chyba pri zavirani souboru");
return 1;
}
return 0;
}
/* finito */