def parse_export_directory(self, rva, size, forwarded_only=False):
        """Parse the export directory.

        Given the RVA of the export directory, it will process all
        its entries.

        The exports will be made available as a list of ExportData
        instances in the 'IMAGE_DIRECTORY_ENTRY_EXPORT' PE attribute.
        """

        try:
            export_dir = self.__unpack_data__(
                self.__IMAGE_EXPORT_DIRECTORY_format__,
                self.get_data(
                    rva, Structure(self.__IMAGE_EXPORT_DIRECTORY_format__).sizeof()
                ),
                file_offset=self.get_offset_from_rva(rva),
            )
        except PEFormatError:
            self.__warnings.append(
                "Error parsing export directory at RVA: 0x%x" % (rva)
            )
            return

        if not export_dir:
            return

        # We keep track of the bytes left in the file and use it to set a upper
        # bound in the number of items that can be read from the different
        # arrays.
        def length_until_eof(rva):
            return len(self.__data__) - self.get_offset_from_rva(rva)

        try:
            address_of_names = self.get_data(
                export_dir.AddressOfNames,
                min(
                    length_until_eof(export_dir.AddressOfNames),
                    export_dir.NumberOfNames * 4,
                ),
            )
            address_of_name_ordinals = self.get_data(
                export_dir.AddressOfNameOrdinals,
                min(
                    length_until_eof(export_dir.AddressOfNameOrdinals),
                    export_dir.NumberOfNames * 4,
                ),
            )
            address_of_functions = self.get_data(
                export_dir.AddressOfFunctions,
                min(
                    length_until_eof(export_dir.AddressOfFunctions),
                    export_dir.NumberOfFunctions * 4,
                ),
            )
        except PEFormatError:
            self.__warnings.append(
                "Error parsing export directory at RVA: 0x%x" % (rva)
            )
            return

        exports = []

        max_failed_entries_before_giving_up = 10

        section = self.get_section_by_rva(export_dir.AddressOfNames)
        # Overly generous upper bound
        safety_boundary = len(self.__data__)
        if section:
            safety_boundary = (
                section.VirtualAddress
                + len(section.get_data())
                - export_dir.AddressOfNames
            )

        symbol_counts = collections.defaultdict(int)
        export_parsing_loop_completed_normally = True
        for i in range(min(export_dir.NumberOfNames, int(safety_boundary / 4))):
            symbol_ordinal = self.get_word_from_data(address_of_name_ordinals, i)

            if symbol_ordinal is not None and symbol_ordinal * 4 < len(
                address_of_functions
            ):
                symbol_address = self.get_dword_from_data(
                    address_of_functions, symbol_ordinal
                )
            else:
                # Corrupt? a bad pointer... we assume it's all
                # useless, no exports
                return None
            if symbol_address is None or symbol_address == 0:
                continue

            # If the function's RVA points within the export directory
            # it will point to a string with the forwarded symbol's string
            # instead of pointing to the function start address.
            if symbol_address >= rva and symbol_address < rva + size:
                forwarder_str = self.get_string_at_rva(symbol_address)
                try:
                    forwarder_offset = self.get_offset_from_rva(symbol_address)
                except PEFormatError:
                    continue
            else:
                if forwarded_only:
                    continue
                forwarder_str = None
                forwarder_offset = None

            symbol_name_address = self.get_dword_from_data(address_of_names, i)
            if symbol_name_address is None:
                max_failed_entries_before_giving_up -= 1
                if max_failed_entries_before_giving_up <= 0:
                    export_parsing_loop_completed_normally = False
                    break

            symbol_name = self.get_string_at_rva(
                symbol_name_address, MAX_SYMBOL_NAME_LENGTH
            )
            if not is_valid_function_name(symbol_name, relax_allowed_characters=True):
                export_parsing_loop_completed_normally = False
                break
            try:
                symbol_name_offset = self.get_offset_from_rva(symbol_name_address)
            except PEFormatError:
                max_failed_entries_before_giving_up -= 1
                if max_failed_entries_before_giving_up <= 0:
                    export_parsing_loop_completed_normally = False
                    break
                try:
                    symbol_name_offset = self.get_offset_from_rva(symbol_name_address)
                except PEFormatError:
                    max_failed_entries_before_giving_up -= 1
                    if max_failed_entries_before_giving_up <= 0:
                        export_parsing_loop_completed_normally = False
                        break
                    continue

            # File 0b1d3d3664915577ab9a32188d29bbf3542b86c7b9ce333e245496c3018819f1
            # was being parsed as potentially containing millions of exports.
            # Checking for duplicates addresses the issue.
            symbol_counts[(symbol_name, symbol_address)] += 1
            if symbol_counts[(symbol_name, symbol_address)] > 10:
                self.__warnings.append(
                    f"Export directory contains more than 10 repeated entries "
                    f"({symbol_name}, {symbol_address:#02x}). Assuming corrupt."
                )
                break
            elif len(symbol_counts) > self.max_symbol_exports:
                self.__warnings.append(
                    "Export directory contains more than {} symbol entries. "
                    "Assuming corrupt.".format(self.max_symbol_exports)
                )
                break

            exports.append(
                ExportData(
                    pe=self,
                    ordinal=export_dir.Base + symbol_ordinal,
                    ordinal_offset=self.get_offset_from_rva(
                        export_dir.AddressOfNameOrdinals + 2 * i
                    ),
                    address=symbol_address,
                    address_offset=self.get_offset_from_rva(
                        export_dir.AddressOfFunctions + 4 * symbol_ordinal
                    ),
                    name=symbol_name,
                    name_offset=symbol_name_offset,
                    forwarder=forwarder_str,
                    forwarder_offset=forwarder_offset,
                )
            )

        if not export_parsing_loop_completed_normally:
            self.__warnings.append(
                f"RVA AddressOfNames in the export directory points to an invalid "
                f"address: {export_dir.AddressOfNames:x}"
            )

        ordinals = {exp.ordinal for exp in exports}

        max_failed_entries_before_giving_up = 10

        section = self.get_section_by_rva(export_dir.AddressOfFunctions)
        # Overly generous upper bound
        safety_boundary = len(self.__data__)
        if section:
            safety_boundary = (
                section.VirtualAddress
                + len(section.get_data())
                - export_dir.AddressOfFunctions
            )

        symbol_counts = collections.defaultdict(int)
        export_parsing_loop_completed_normally = True
        for idx in range(min(export_dir.NumberOfFunctions, int(safety_boundary / 4))):
            if idx + export_dir.Base not in ordinals:
                try:
                    symbol_address = self.get_dword_from_data(address_of_functions, idx)
                except PEFormatError:
                    symbol_address = None

                if symbol_address is None:
                    max_failed_entries_before_giving_up -= 1
                    if max_failed_entries_before_giving_up <= 0:
                        export_parsing_loop_completed_normally = False
                        break

                if symbol_address == 0:
                    continue

                # Checking for forwarder again.
                if (
                    symbol_address is not None
                    and symbol_address >= rva
                    and symbol_address < rva + size
                ):
                    forwarder_str = self.get_string_at_rva(symbol_address)
                else:
                    forwarder_str = None

                # File 0b1d3d3664915577ab9a32188d29bbf3542b86c7b9ce333e245496c3018819f1
                # was being parsed as potentially containing millions of exports.
                # Checking for duplicates addresses the issue.
                symbol_counts[symbol_address] += 1
                if symbol_counts[symbol_address] > self.max_repeated_symbol:
                    # if most_common and most_common[0][1] > 10:
                    self.__warnings.append(
                        "Export directory contains more than {} repeated "
                        "ordinal entries (0x{:x}). Assuming corrupt.".format(
                            self.max_repeated_symbol, symbol_address
                        )
                    )
                    break
                elif len(symbol_counts) > self.max_symbol_exports:
                    self.__warnings.append(
                        "Export directory contains more than "
                        f"{self.max_symbol_exports} ordinal entries. Assuming corrupt."
                    )
                    break

                exports.append(
                    ExportData(
                        ordinal=export_dir.Base + idx,
                        address=symbol_address,
                        name=None,
                        forwarder=forwarder_str,
                    )
                )

        if not export_parsing_loop_completed_normally:
            self.__warnings.append(
                "RVA AddressOfFunctions in the export directory points to an invalid "
                f"address: {export_dir.AddressOfFunctions:x}"
            )
            return

        if not exports and export_dir.all_zeroes():
            return None
        return ExportDirData(
            struct=export_dir,
            symbols=exports,
            name=self.get_string_at_rva(export_dir.Name),
        )
Parse the export directory.

        Given the RVA of the export directory, it will process all
        its entries.

        The exports will be made available as a list of ExportData
        instances in the 'IMAGE_DIRECTORY_ENTRY_EXPORT' PE attribute.
        
pefile.py