classparser.c 7.99 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
#include "common.h"
#include "classparser.h"

#include <assert.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>

//

11 12 13 14 15
#ifdef MINGW

#include <ws2tcpip.h>

#else
16

17 18 19 20 21 22 23 24
#include <arpa/inet.h>

#endif

#define read_be16(value) ntohs(value)
#define read_be32(value) ntohl(value)

//
25

26
#define CLASS_MAGIC 0xCAFEBABE
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54

struct java_class {
	// Class bytecode.
	const uint8_t * bytes;

	// Size of the class bytecode.
	size_t size;

	// Constant pool size in bytes.
	size_t cp_size;

	// Constant pool entry offsets.
	size_t cp_offsets [];
};



enum cp_tag {
	CP_TAG_UTF8 = 1,
	CP_TAG_LONG = 5,
	CP_TAG_DOUBLE = 6,
	CP_TAG_CLASS = 7,
	CP_TAG_STRING = 8,
	CP_TAG_METHOD_HANDLE = 15,
	CP_TAG_METHOD_TYPE = 16,
};


55
struct PACKED cp_info {
56 57
	uint8_t tag;

58 59
	union PACKED {
		struct PACKED cp_info_utf8 {
60 61 62 63
			uint16_t length;
			uint8_t bytes [];
		} utf8;

64
		struct PACKED cp_info_class {
65 66 67
			uint16_t name_index;
		} class;

68
		struct PACKED cp_info_method_handle {
69 70 71 72
			uint8_t reference_kind;
			uint16_t reference_index;
		} method_handle;

73
		struct PACKED cp_info_long_double {
74 75 76 77 78 79 80 81
			uint32_t high_bytes;
			uint32_t low_bytes;
		} long_double;
	};
};

//

82
struct PACKED class_file_start {
83 84 85 86 87 88 89
	uint32_t magic;
	uint16_t minor_version;
	uint16_t major_version;
	uint16_t constant_pool_count;
	struct cp_info constant_pool []; // [constant_pool_count - 1]
};

90
struct PACKED class_file_access {
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
	uint16_t access_flags;
	uint16_t this_class;
	uint16_t super_class;
	uint16_t interfaces_count;
	uint16_t interfaces []; // [interfaces_count]
};

//

static inline struct class_file_start *
__class_file_start (const uint8_t * class_bytes) {
	return (struct class_file_start *) class_bytes;
}


static inline struct class_file_access *
__class_file_access (class_t java_class) {
	int offset = sizeof (struct class_file_start) + java_class->cp_size;
	return (struct class_file_access *) (java_class->bytes + offset);
}

//

static inline bool
__class_bytes_valid (const uint8_t * class_bytes) {
116
	return read_be32 (__class_file_start (class_bytes)->magic) == CLASS_MAGIC;
117 118 119 120 121
}


static inline int
__cp_entry_count (const uint8_t * class_bytes) {
122
	return read_be16 (__class_file_start (class_bytes)->constant_pool_count);
123 124 125 126 127 128
}

//

static inline int
__class_this_class_index (class_t java_class) {
129
	return read_be16 (__class_file_access (java_class)->this_class);
130 131 132 133 134
}


static inline int
__class_super_class_index (class_t java_class) {
135
	return read_be16 (__class_file_access (java_class)->super_class);
136 137 138 139 140
}


static inline int
__class_interface_count (class_t java_class) {
141
	return read_be16 (__class_file_access (java_class)->interfaces_count);
142 143 144 145 146
}


static inline int
__class_interface_index (class_t java_class, int index) {
147
	return read_be16 (__class_file_access (java_class)->interfaces [index]);
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
}

//

static inline struct cp_info *
__cp_entry (const uint8_t * class_bytes, int offset) {
	const uint8_t * cp_base = class_bytes + offsetof (struct class_file_start, constant_pool);
	return (struct cp_info *) (cp_base + offset);
}


static struct cp_info *
__class_cp_get_entry (class_t java_class, int index) {
	if (index >= 1 && index < __cp_entry_count (java_class->bytes)) {
		int offset = java_class->cp_offsets [index];
		return __cp_entry (java_class->bytes, offset);

	} else {
		warn ("invalid const pool index: %d\n", index);
		return NULL;
	}
}

//

static int
__cp_scan_entries (const uint8_t * class_bytes, size_t * offsets) {
	uint_fast32_t offset = 0;
	uint_fast16_t count = __cp_entry_count (class_bytes);

	for (uint_fast16_t index = 1; index < count; index++) {
		offsets [index] = offset;

		struct cp_info * entry = __cp_entry (class_bytes, offset);
		switch (entry->tag) {

		case CP_TAG_UTF8: {
185
			uint_fast16_t length = read_be16 (entry->utf8.length);
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
			offset += sizeof (struct cp_info_utf8) + length;
			break;
		}

		case CP_TAG_CLASS:
		case CP_TAG_STRING:
		case CP_TAG_METHOD_TYPE:
			// Indices into constant pool.
			offset += sizeof (uint16_t);
			break;

		case CP_TAG_METHOD_HANDLE:
			offset += sizeof (struct cp_info_method_handle);
			break;

		case CP_TAG_LONG:
		case CP_TAG_DOUBLE:
			// 64-bit values take an extra constant pool slot.
			index++;

			offset += sizeof (struct cp_info_long_double);
			break;

		default:
			// All other entries fit into 4 bytes.
			offset += 2 * sizeof (uint16_t);
		}

		// Account for the type tag.
215
		offset += sizeof_member (struct cp_info, tag);
216 217 218 219 220 221 222 223 224 225 226
	}

	return offset;
}


//

static inline struct java_class *
__class_alloc (const uint8_t * class_bytes) {
	int const_count = 1 + __cp_entry_count (class_bytes);
227
	int offsets_size = const_count * sizeof_member (struct java_class, cp_offsets[0]);
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
	return (struct java_class *) malloc (sizeof (struct java_class) + offsets_size);
}


static inline void
__class_init (struct java_class * java_class, const uint8_t * class_bytes, size_t byte_count) {
	java_class->bytes = class_bytes;
	java_class->size = byte_count;
	java_class->cp_size = __cp_scan_entries (class_bytes, &java_class->cp_offsets[0]);
}


class_t
class_alloc (const uint8_t * class_bytes, size_t byte_count) {
	assert (byte_count > sizeof (struct class_file_start));
	assert (class_bytes != NULL);

	if (__class_bytes_valid (class_bytes)) {
		struct java_class * result = __class_alloc (class_bytes);
		if (result != NULL) {
			__class_init (result, class_bytes, byte_count);
			return result;

		} else {
			warn ("failed to allocate java_class structure");
		}
	} else {
255
		warn ("invalid class bytecode passed to class_alloc is invalid");
256 257 258 259 260 261 262
	}

	return NULL;
}

//

263
static utf8_t
264 265 266 267 268 269 270
class_cp_get_utf8 (class_t java_class, int index, size_t * utf8_length) {
	assert (java_class != NULL);

	struct cp_info * entry = __class_cp_get_entry (java_class, index);
	if (entry != NULL) {
		if (entry->tag == CP_TAG_UTF8) {
			if (utf8_length != NULL) {
271
				*utf8_length = read_be16 (entry->utf8.length);
272 273 274 275 276 277 278 279 280 281 282 283 284
			}

			return &entry->utf8.bytes [0];

		} else {
			warn ("invalid const tag, expected %d, found %d\n", CP_TAG_UTF8, entry->tag);
		}
	}

	return NULL;
}


285
static utf8_t
286 287 288 289 290 291
class_cp_get_class_name (
	class_t java_class, int class_index, size_t * utf8_length
) {
	struct cp_info * entry = __class_cp_get_entry (java_class, class_index);
	if (entry != NULL) {
		if (entry->tag == CP_TAG_CLASS) {
292
			int index = read_be16 (entry->class.name_index);
293 294 295 296 297 298 299 300 301 302 303 304 305 306
			return class_cp_get_utf8 (java_class, index, utf8_length);

		} else {
			warn ("invalid const tag, expected %d, found %d\n", CP_TAG_CLASS, entry->tag);
		}
	}

	return NULL;
}

//

static inline char *
__utf8dup (utf8_t bytes, size_t length) {
307 308 309 310 311 312 313 314
	size_t len = length + 1;
	char * result = (char *) malloc (len);
	if (result != NULL) {
		strncpy (result, (const char *) bytes, length);
		result [length] = '\0';
	}

	return result;
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
}


char *
class_name (class_t java_class) {
	assert (java_class != NULL);

	int class_index = __class_this_class_index (java_class);

	size_t length = 0;
	utf8_t bytes = class_cp_get_class_name (java_class, class_index, &length);
	return __utf8dup (bytes, length);
}


char *
class_super_class_name (class_t java_class) {
	assert (java_class != NULL);

	int class_index = __class_super_class_index (java_class);
	if (class_index != 0) {
		size_t length = 0;
		utf8_t bytes = class_cp_get_class_name (java_class, class_index, &length);
		return __utf8dup (bytes, length);

	} else {
		return NULL;
	}
}


bool
class_has_super_class (class_t java_class) {
	assert (java_class != NULL);
	return __class_super_class_index (java_class) > 1;
}


int
class_interface_count (class_t java_class) {
	assert (java_class != NULL);
	return __class_interface_count (java_class);
}


char *
class_interface_name (class_t java_class, int index) {
	assert (java_class != NULL);

	int count = __class_interface_count (java_class);
	if (0 <= index && index < count) {
		int iface_index = __class_interface_index (java_class, index);

		size_t length = 0;
		utf8_t bytes = class_cp_get_class_name (java_class, iface_index, &length);
		return __utf8dup (bytes, length);

	} else {
		warn ("requested interface index out of range: %d\n", index);
		return NULL;
	}
}


void
class_free (class_t java_class) {
	assert (java_class != NULL);

	free ((void *) java_class);
}