struct r_bin_pe_export_t* PE_(r_bin_pe_get_exports)(RBinPEObj* pe) {
	r_return_val_if_fail (pe, NULL);
	struct r_bin_pe_export_t* exp, * exports = NULL;
	PE_Word function_ordinal = 0;
	PE_VWord functions_paddr, names_paddr, ordinals_paddr, function_rva, name_vaddr, name_paddr;
	char function_name[PE_NAME_LENGTH + 1], forwarder_name[PE_NAME_LENGTH + 1];
	char dll_name[PE_NAME_LENGTH + 1];
	PE_(image_data_directory) * data_dir_export;
	PE_VWord export_dir_rva;
	int n,i, export_dir_size;
	st64 exports_sz = 0;

	if (!pe->data_directory) {
		return NULL;
	}
	data_dir_export = &pe->data_directory[PE_IMAGE_DIRECTORY_ENTRY_EXPORT];
	export_dir_rva = data_dir_export->VirtualAddress;
	export_dir_size = data_dir_export->Size;
	PE_VWord *func_rvas = NULL;
	PE_Word *ordinals = NULL;
	if (pe->export_directory) {
		if (pe->export_directory->NumberOfFunctions + 1 <
		pe->export_directory->NumberOfFunctions) {
			// avoid integer overflow
			return NULL;
		}
		exports_sz = (pe->export_directory->NumberOfFunctions + 1) * sizeof (struct r_bin_pe_export_t);
		// we cant exit with export_sz > pe->size, us r_bin_pe_export_t is 256+256+8+8+8+4 bytes is easy get over file size
		// to avoid fuzzing we can abort on export_directory->NumberOfFunctions>0xffff
		if (exports_sz < 0 || pe->export_directory->NumberOfFunctions + 1 > 0xffff) {
			return NULL;
		}
		if (!(exports = malloc (exports_sz))) {
			return NULL;
		}
		if (r_buf_read_at (pe->b, PE_(va2pa) (pe, pe->export_directory->Name), (ut8*) dll_name, PE_NAME_LENGTH) < 1) {
			// we dont stop if dll name cant be read, we set dllname to null and continue
			pe_printf ("Warning: read (dll name)\n");
			dll_name[0] = '\0';
		}
		functions_paddr = PE_(va2pa) (pe, pe->export_directory->AddressOfFunctions);
		names_paddr = PE_(va2pa) (pe, pe->export_directory->AddressOfNames);
		ordinals_paddr = PE_(va2pa) (pe, pe->export_directory->AddressOfOrdinals);

		const size_t names_sz = pe->export_directory->NumberOfNames * sizeof (PE_Word);
		const size_t funcs_sz = pe->export_directory->NumberOfFunctions * sizeof (PE_VWord);
		ordinals = malloc (names_sz);
		func_rvas = malloc (funcs_sz);
		if (!ordinals || !func_rvas) {
			goto beach;
		}
		int r = r_buf_read_at (pe->b, ordinals_paddr, (ut8 *)ordinals, names_sz);
		if (r != names_sz) {
			goto beach;
		}
		r = r_buf_read_at (pe->b, functions_paddr, (ut8 *)func_rvas, funcs_sz);
		if (r != funcs_sz) {
			goto beach;
		}
		for (i = 0; i < pe->export_directory->NumberOfFunctions; i++) {
			// get vaddr from AddressOfFunctions array
			function_rva = r_read_at_ble32 ((ut8 *)func_rvas, i * sizeof (PE_VWord), pe->endian);
			// have exports by name?
			if (pe->export_directory->NumberOfNames > 0) {
				// search for value of i into AddressOfOrdinals
				name_vaddr = 0;
				for (n = 0; n < pe->export_directory->NumberOfNames; n++) {
					PE_Word fo = r_read_at_ble16 ((ut8 *)ordinals, n * sizeof (PE_Word), pe->endian);
					// if exist this index into AddressOfOrdinals
					if (i == fo) {
						function_ordinal = fo;
						// get the VA of export name  from AddressOfNames
						name_vaddr = r_buf_read_le32_at (pe->b, names_paddr + n * sizeof (PE_VWord));
						break;
					}
				}
				// have an address into name_vaddr?
				if (name_vaddr) {
					// get the name of the Export
					name_paddr = PE_(va2pa) (pe, name_vaddr);
					if (r_buf_read_at (pe->b, name_paddr, (ut8*) function_name, PE_NAME_LENGTH) < 1) {
						pe_printf ("Warning: read (function name)\n");
						exports[i].last = 1;
						return exports;
					}
				} else { // No name export, get the ordinal
					function_ordinal = i;
					snprintf (function_name, PE_NAME_LENGTH, "Ordinal_%i", i + pe->export_directory->Base);
				}
			} else { // if export by name dont exist, get the ordinal taking in mind the Base value.
				snprintf (function_name, PE_NAME_LENGTH, "Ordinal_%i", i + pe->export_directory->Base);
			}
			// check if VA are into export directory, this mean a forwarder export
			if (function_rva >= export_dir_rva && function_rva < (export_dir_rva + export_dir_size)) {
				// if forwarder, the VA point to Forwarded name
				if (r_buf_read_at (pe->b, PE_(va2pa) (pe, function_rva), (ut8*) forwarder_name, PE_NAME_LENGTH) < 1) {
					exports[i].last = 1;
					return exports;
				}
			} else { // no forwarder export
				snprintf (forwarder_name, PE_NAME_LENGTH, "NONE");
			}
			dll_name[PE_NAME_LENGTH] = '\0';
			function_name[PE_NAME_LENGTH] = '\0';
			exports[i].vaddr = bin_pe_rva_to_va (pe, function_rva);
			exports[i].paddr = PE_(va2pa) (pe, function_rva);
			exports[i].ordinal = function_ordinal + pe->export_directory->Base;
			memcpy (exports[i].forwarder, forwarder_name, PE_NAME_LENGTH);
			exports[i].forwarder[PE_NAME_LENGTH] = '\0';
			memcpy (exports[i].name, function_name, PE_NAME_LENGTH);
			exports[i].name[PE_NAME_LENGTH] = '\0';
			memcpy (exports[i].libname, dll_name, PE_NAME_LENGTH);
			exports[i].libname[PE_NAME_LENGTH] = '\0';
			exports[i].last = 0;
		}
		exports[i].last = 1;
		free (ordinals);
		free (func_rvas);
	}
	exp = parse_symbol_table (pe, exports, exports_sz - sizeof (struct r_bin_pe_export_t));
	if (exp) {
		exports = exp;
	}
	return exports;
beach:
	free (exports);
	free (ordinals);
	free (func_rvas);
	return NULL;
}