static RList *classes(RBinFile *bf) {
	RDyldCache *cache = (RDyldCache*) bf->o->bin_obj;
	if (!cache) {
		return NULL;
	}

	RList *ret = r_list_newf (free);
	if (!ret) {
		return NULL;
	}

	if (!cache->objc_opt_info_loaded) {
		cache->oi = get_objc_opt_info (bf, cache);
		cache->objc_opt_info_loaded = true;
	}

	RListIter *iter;
	RDyldBinImage *bin;
	ut64 slide = rebase_infos_get_slide (cache);

	RBuffer *orig_buf = bf->buf;
	ut32 num_of_unnamed_class = 0;
	r_list_foreach (cache->bins, iter, bin) {
		struct MACH0_(obj_t) *mach0 = bin_to_mach0 (bf, bin);
		if (!mach0) {
			goto beach;
		}

		struct section_t *sections = NULL;
		if (!(sections = MACH0_(get_sections) (mach0))) {
			MACH0_(mach0_free) (mach0);
			goto beach;
		}

		int i;
		for (i = 0; !sections[i].last; i++) {
			if (sections[i].size == 0) {
				continue;
			}

			bool is_classlist = strstr (sections[i].name, "__objc_classlist");
			bool is_catlist = strstr (sections[i].name, "__objc_catlist");

			if (!is_classlist && !is_catlist) {
				continue;
			}

			ut8 *pointers = malloc (sections[i].size);
			if (!pointers) {
				continue;
			}

			ut64 offset = va2pa (sections[i].addr, cache->n_maps, cache->maps, cache->buf, slide, NULL, NULL);
			if (r_buf_read_at (cache->buf, offset, pointers, sections[i].size) < sections[i].size) {
				R_FREE (pointers);
				continue;
			}
			ut8 *cursor = pointers;
			ut8 *pointers_end = pointers + sections[i].size;

			for (; cursor < pointers_end; cursor += 8) {
				ut64 pointer_to_class = r_read_le64 (cursor);

				RBinClass *klass;
				if (!(klass = R_NEW0 (RBinClass)) ||
					!(klass->methods = r_list_new ()) ||
					!(klass->fields = r_list_new ())) {
					R_FREE (klass);
					R_FREE (pointers);
					R_FREE (sections);
					MACH0_(mach0_free) (mach0);
					goto beach;
				}

				bf->o->bin_obj = mach0;
				bf->buf = cache->buf;
				if (is_classlist) {
					MACH0_(get_class_t) (pointer_to_class, bf, klass, false, NULL, cache->oi);
				} else {
					MACH0_(get_category_t) (pointer_to_class, bf, klass, NULL, cache->oi);
				}
				bf->o->bin_obj = cache;
				bf->buf = orig_buf;

				if (!klass->name) {
					eprintf ("KLASS ERROR AT 0x%"PFMT64x", is_classlist %d\n", pointer_to_class, is_classlist);
					klass->name = r_str_newf ("UnnamedClass%u", num_of_unnamed_class);
					if (!klass->name) {
						R_FREE (klass);
						R_FREE (pointers);
						R_FREE (sections);
						MACH0_(mach0_free) (mach0);
						goto beach;
					}
					num_of_unnamed_class++;
				}
				r_list_append (ret, klass);
			}

			R_FREE (pointers);
		}

		R_FREE (sections);
		MACH0_(mach0_free) (mach0);
	}

	return ret;

beach:
	r_list_free (ret);
	return NULL;
}
static RList *classes(RBinFile *bf) {
	RDyldCache *cache = (RDyldCache*) bf->o->bin_obj;
	if (!cache) {
		return NULL;
	}

	RList *ret = r_list_newf (free);
	if (!ret) {
		return NULL;
	}

	if (!cache->objc_opt_info_loaded) {
		cache->oi = get_objc_opt_info (bf, cache);
		cache->objc_opt_info_loaded = true;
	}

	RListIter *iter;
	RDyldBinImage *bin;
	ut64 slide = rebase_infos_get_slide (cache);

	RBuffer *orig_buf = bf->buf;
	ut32 num_of_unnamed_class = 0;
	r_list_foreach (cache->bins, iter, bin) {
		struct MACH0_(obj_t) *mach0 = bin_to_mach0 (bf, bin);
		if (!mach0) {
			goto beach;
		}

		struct section_t *sections = NULL;
		if (!(sections = MACH0_(get_sections) (mach0))) {
			MACH0_(mach0_free) (mach0);
			goto beach;
		}

		int i;
		for (i = 0; !sections[i].last; i++) {
			if (sections[i].size == 0) {
				continue;
			}

			bool is_classlist = strstr (sections[i].name, "__objc_classlist");
			bool is_catlist = strstr (sections[i].name, "__objc_catlist");

			if (!is_classlist && !is_catlist) {
				continue;
			}

			ut8 *pointers = malloc (sections[i].size);
			if (!pointers) {
				continue;
			}

			ut64 offset = va2pa (sections[i].addr, cache->n_maps, cache->maps, cache->buf, slide, NULL, NULL);
			if (r_buf_read_at (cache->buf, offset, pointers, sections[i].size) < sections[i].size) {
				R_FREE (pointers);
				continue;
			}
			ut8 *cursor = pointers;
			ut8 *pointers_end = pointers + sections[i].size;

			for (; cursor < pointers_end; cursor += 8) {
				ut64 pointer_to_class = r_read_le64 (cursor);

				RBinClass *klass;
				if (!(klass = R_NEW0 (RBinClass)) ||
					!(klass->methods = r_list_new ()) ||
					!(klass->fields = r_list_new ())) {
					R_FREE (klass);
					R_FREE (pointers);
					R_FREE (sections);
					MACH0_(mach0_free) (mach0);
					goto beach;
				}

				bf->o->bin_obj = mach0;
				bf->buf = cache->buf;
				if (is_classlist) {
					MACH0_(get_class_t) (pointer_to_class, bf, klass, false, NULL, cache->oi);
				} else {
					MACH0_(get_category_t) (pointer_to_class, bf, klass, NULL, cache->oi);
				}
				bf->o->bin_obj = cache;
				bf->buf = orig_buf;

				if (!klass->name) {
					eprintf ("KLASS ERROR AT 0x%"PFMT64x", is_classlist %d\n", pointer_to_class, is_classlist);
					klass->name = r_str_newf ("UnnamedClass%u", num_of_unnamed_class);
					if (!klass->name) {
						R_FREE (klass);
						R_FREE (pointers);
						R_FREE (sections);
						MACH0_(mach0_free) (mach0);
						goto beach;
					}
					num_of_unnamed_class++;
				}
				r_list_append (ret, klass);
			}

			R_FREE (pointers);
		}

		R_FREE (sections);
		MACH0_(mach0_free) (mach0);
	}

	return ret;

beach:
	r_list_free (ret);
	return NULL;
}