Unserialize data serialized with :func:`marshal.dump`. This function
    works across python versions. Marshalled code objects are returned as
    instances of :class:`~pwnypack.bytecode.CodeObject`.

    Arguments:
        fp(file): A file or file-like object that contains the serialized
            data.
        origin(dict): The opcode specification of the python version that
            generated the data. If you provide ``None``, the specs for the
            currently running python version will be used.

    Returns:
        The unserialized data.

def marshal_load(fp, origin=None):
    """
    Unserialize data serialized with :func:`marshal.dump`. This function
    works across python versions. Marshalled code objects are returned as
    instances of :class:`~pwnypack.bytecode.CodeObject`.

    Arguments:
        fp(file): A file or file-like object that contains the serialized
            data.
        origin(dict): The opcode specification of the python version that
            generated the data. If you provide ``None``, the specs for the
            currently running python version will be used.

    Returns:
        The unserialized data.
    """

    origin = get_py_internals(origin)
    version = origin['version']

    refs = []

    def ref(o, flags):
        if flags & FLAG_REF:
            refs.append(o)
        return o

    def read_byte():
        return six.byte2int(fp.read(1))

    def read_short():
        return u16(fp.read(2), target=MARSHAL_TARGET)

    def read_long():
        return u32(fp.read(4), target=MARSHAL_TARGET)

    def read_int64():
        return u64(fp.read(8), target=MARSHAL_TARGET)

    def read_float_binary():
        return unpack('d', fp.read(8), target=MARSHAL_TARGET)[0]

    def read_bytes():
        return fp.read(read_long())

    def read_bytes_short():
        return fp.read(read_byte())

    def read_float_text():
        return float(read_bytes_short())

    def read_object():
        c = six.byte2int(fp.read(1))
        flags = c & FLAG_REF
        c = ObjectType(c & ~FLAG_REF)

        if c is ObjectType.null:
            return NULL
        elif c is ObjectType.none:
            return None
        elif c is ObjectType.stopiter:
            return StopIteration
        elif c is ObjectType.ellipsis:
            return Ellipsis
        elif c is ObjectType.false:
            return False
        elif c is ObjectType.true:
            return True
        elif c is ObjectType.int:
            return ref(read_long(), flags)
        elif c is ObjectType.int64:
            return ref(read_int64(), flags)
        elif c is ObjectType.long:
            n = read_long()
            r = sum(
                read_short() << (i * PyLong_MARSHAL_SHIFT)
                for i in range(abs(n))
            )
            return ref(-r if n < 0 else r, flags)
        elif c is ObjectType.float:
            return ref(read_float_text(), flags)
        elif c is ObjectType.binary_float:
            return ref(read_float_binary(), flags)
        elif c is ObjectType.complex:
            real = read_float_text()
            imag = read_float_text()
            return ref(complex(real, imag), flags)
        elif c is ObjectType.binary_complex:
            real = read_float_binary()
            imag = read_float_binary()
            return ref(complex(real, imag), flags)
        elif c is ObjectType.string:
            return ref(read_bytes(), flags)
        elif c is ObjectType.unicode:
            return ref(read_bytes().decode('utf-8'), flags)
        elif c is ObjectType.interned:
            if version < 30:
                return ref(read_bytes(), FLAG_REF)
            else:
                return ref(read_bytes().decode('utf-8'), flags)
        elif c is ObjectType.ascii:
            return ref(read_bytes().decode('ascii'), flags)
        elif c is ObjectType.ascii_interned:
            return ref(read_bytes().decode('ascii'), flags)
        elif c is ObjectType.short_ascii:
            return ref(read_bytes_short().decode('ascii'), flags)
        elif c is ObjectType.short_ascii_interned:
            return ref(read_bytes_short().decode('ascii'), flags)
        elif c in (ObjectType.tuple, ObjectType.small_tuple, ObjectType.frozenset):
            ref_index = len(refs)
            ref(NULL, flags)
            r_type = frozenset if c is ObjectType.frozenset else tuple
            n = read_byte() if c is ObjectType.small_tuple else read_long()
            r = r_type(read_object() for _ in range(n))
            if flags & FLAG_REF:
                refs[ref_index] = r
            return r
        elif c is ObjectType.list:
            r = ref([], flags)
            for _ in range(read_long()):
                r.append(read_object())
            return r
        elif c is ObjectType.set:
            r = ref(set(), flags)
            for _ in range(read_long()):
                r.add(read_object())
            return r
        elif c is ObjectType.dict:
            r = ref({}, flags)
            while True:
                k = read_object()
                if k is NULL:
                    break
                r[k] = read_object()
            return r
        elif c in (ObjectType.stringref, ObjectType.ref):
            return refs[read_long()]
        elif c is ObjectType.code:
            ref_index = len(refs)
            ref(NULL, flags)

            co_argcount = read_long()
            if version < 30:
                co_kwonlyargcount = 0
            else:
                co_kwonlyargcount = read_long()
            co_nlocals = read_long()
            co_stacksize = read_long()
            co_flags = read_long()
            co_code = read_object()
            co_consts = read_object()
            co_names = read_object()
            co_varnames = read_object()
            co_freevars = read_object()
            co_cellvars = read_object()
            co_filename = read_object()
            co_name = read_object()
            co_firstlineno = read_long()
            co_lnotab = read_object()

            r = CodeObject(
                co_argcount,
                co_kwonlyargcount,
                co_nlocals,
                co_stacksize,
                co_flags,
                co_code,
                co_consts,
                co_names,
                co_varnames,
                co_filename,
                co_name,
                co_firstlineno,
                co_lnotab,
                co_freevars,
                co_cellvars,
                origin,
            )
            if flags & FLAG_REF:
                refs[ref_index] = r
            return r
        else:
            raise ValueError('Unexpected object type %s.' % c)

    return read_object()