Exemplo n.º 1
0
/* return if instruction */
bool
instr_uses_aflags(instr_t *instr)
{
    uint aflags;

    aflags = instr_get_arith_flags(instr);
    if (TESTANY(EFLAGS_READ_6, aflags) ||
            TESTANY(EFLAGS_WRITE_6, aflags))
        return true;

    return false;
}
Exemplo n.º 2
0
void
init_dr_marker(dr_marker_t *marker)
{
    /* not calling memset b/c windbg_cmds is large */
# ifdef DEBUG
    marker->flags = DR_MARKER_DEBUG_BUILD;
# else
    marker->flags = DR_MARKER_RELEASE_BUILD;
# endif
# ifdef PROFILE
    marker->flags = DR_MARKER_PROFILE_BUILD;
# endif
    /* make sure we set one of the above flags and not more then one */
    ASSERT(TESTANY(DR_MARKER_BUILD_TYPES, marker->flags) &&
           ((DR_MARKER_BUILD_TYPES & marker->flags) &
            ((DR_MARKER_BUILD_TYPES & marker->flags) - 1)) == 0);
    /* TODO : add any additional flags? */
    marker->build_num = BUILD_NUMBER;
    marker->dr_base_addr = get_module_base((app_pc)init_dr_marker);
    marker->dr_generic_nudge_target = (void *)generic_nudge_target;
# ifdef HOT_PATCHING_INTERFACE
    marker->dr_hotp_policy_status_table = (void *)hotp_policy_status_table;
# else
    marker->dr_hotp_policy_status_table = NULL;
# endif
    marker->dr_marker_version = DR_MARKER_VERSION_CURRENT;
    marker->stats = get_dr_stats();
    dr_marker_magic_init(NT_CURRENT_PROCESS, marker);
    marker->windbg_cmds[0] = '\0';
}
Exemplo n.º 3
0
cache_pc
indirect_linkstub_target(dcontext_t *dcontext, fragment_t *f, linkstub_t *l)
{
	// COMPLETEDD #495 indirect_linkstub_target
	printf("Starting indirect_linkstub_target\n");
  ASSERT(LINKSTUB_INDIRECT(l->flags));
  ASSERT(!TESTANY(LINK_NI_SYSCALL_ALL, l->flags));
#ifdef WINDOWS
  if (EXIT_TARGETS_SHARED_SYSCALL(l->flags)) {
      /* currently this is the only way to distinguish shared_syscall
       * exit from other indirect exits and from other exits in
       * a fragment containing ignorable or non-ignorable syscalls
       */
      ASSERT(TEST(FRAG_HAS_SYSCALL, f->flags));
      return shared_syscall_routine_ex(dcontext
                                       _IF_X64(MODE_OVERRIDE(FRAG_IS_32(f->flags))));
  }
#endif
  if (TEST(FRAG_COARSE_GRAIN, f->flags)) {
      /* Need to target the ibl prefix.  Passing in cti works as well as stub,
       * and avoids a circular dependence where linkstub_unlink_entry_offset()
       * call this routine to get the target and then this routine asks for
       * the stub which calls linkstub_unlink_entry_offset()...
       */
      return get_coarse_ibl_prefix(dcontext, EXIT_CTI_PC(f, l),
                                   extract_branchtype(l->flags));
  } else {
      return get_ibl_routine_ex(dcontext,
                                IF_X64(TEST(LINK_TRACE_CMP, l->flags) ? IBL_TRACE_CMP :)
                                IBL_LINKED,
                                get_source_fragment_type(dcontext, f->flags),
                                extract_branchtype(l->flags)
                                _IF_X64(MODE_OVERRIDE(FRAG_IS_32(f->flags))));
  }
}
Exemplo n.º 4
0
DR_EXPORT
bool
drx_aflags_are_dead(instr_t *where)
{
    instr_t *instr;
    uint flags;
    for (instr = where; instr != NULL; instr = instr_get_next(instr)) {
        /* we treat syscall/interrupt as aflags read */
        if (instr_is_syscall(instr) || instr_is_interrupt(instr))
            return false;
        flags = instr_get_arith_flags(instr, DR_QUERY_DEFAULT);
        if (TESTANY(EFLAGS_READ_ARITH, flags))
            return false;
        if (TESTALL(EFLAGS_WRITE_ARITH, flags))
            return true;
        if (instr_is_cti(instr)) {
            if (instr_is_app(instr) &&
                (instr_is_ubr(instr) || instr_is_call_direct(instr))) {
                instr_t *next = instr_get_next(instr);
                opnd_t   tgt  = instr_get_target(instr);
                /* continue on elision */
                if (next != NULL && instr_is_app(next) &&
                    opnd_is_pc(tgt) &&
                    opnd_get_pc(tgt) == instr_get_app_pc(next))
                    continue;
            }
            /* unknown target, assume aflags is live */
            return false;
        }
    }
    return false;
}
Exemplo n.º 5
0
bool
instr_reads_from_aflags(instr_t *instr)
{
    uint aflags;

    aflags = instr_get_arith_flags(instr);
    if (TESTANY(EFLAGS_READ_6, aflags))
        return true;
    return false;
}
Exemplo n.º 6
0
static bool
drsys_iter_arg_cb(drsys_arg_t *arg, void *user_data)
{
    buf_info_t *buf = (buf_info_t *) user_data;
    ASSERT(arg->valid, "no args should be invalid");
    if ((arg->pre && !TEST(DRSYS_PARAM_RETVAL, arg->mode)) ||
        (!arg->pre && TESTANY(DRSYS_PARAM_OUT|DRSYS_PARAM_RETVAL, arg->mode)))
            print_arg(buf, arg);
    return true; /* keep going */
}
Exemplo n.º 7
0
bool
instr_writes_to_any_aflags(instr_t *instr)
{
    uint aflags;

    aflags = instr_get_arith_flags(instr);
    if (TESTANY(EFLAGS_WRITE_6, aflags))
        return true;

    return false;
}
Exemplo n.º 8
0
/* Semi-compatibility with the Windows CRT _access function.
 */
drfront_status_t
drfront_access(const char *fname, drfront_access_mode_t mode, OUT bool *ret)
{
    int r;
    struct stat st;
    uid_t euid;

    if (ret == NULL)
        return DRFRONT_ERROR_INVALID_PARAMETER;

    r = stat(fname, &st);

    if (r == -1) {
        *ret = false;
        if (errno == EACCES || errno == ENOENT || errno == ENOTDIR)
            return DRFRONT_SUCCESS;
        return DRFRONT_ERROR;
    } else if (mode == DRFRONT_EXIST) {
        /* Just checking for existence */
        *ret = true;
        return DRFRONT_SUCCESS;
    }

    *ret = false;
    euid = geteuid();
    /* It is assumed that (S_IRWXU >> 6) == DRFRONT_READ | DRFRONT_WRITE | DRFRONT_EXEC */
    if (euid == 0) {
        /* XXX DrMi#1857: we assume that euid == 0 means +rw access to any file,
         * and +x access to any file with at least one +x bit set.  This is
         * usually true but not always.
         */
        if (TEST(DRFRONT_EXEC, mode)) {
            *ret = TESTANY((DRFRONT_EXEC << 6) | (DRFRONT_EXEC << 3) | DRFRONT_EXEC,
                           st.st_mode);
        } else
            *ret = true;
    } else if (euid == st.st_uid) {
        /* Check owner permissions */
        *ret = TESTALL(mode << 6, st.st_mode);
    } else {
        /* Check other permissions */
        *ret = TESTALL(mode, st.st_mode);
    }

    if (*ret && S_ISDIR(st.st_mode) && TEST(DRFRONT_WRITE, mode)) {
        /* We use an actual write try, to avoid failing on a read-only filesystem
         * (DrMi#1857).
         */
        return drfront_dir_try_writable(fname, ret);
    }

    return DRFRONT_SUCCESS;
}
Exemplo n.º 9
0
/* iterate basic block to check if aflags are dead after (including) where */
static bool
bb_aflags_are_dead(instrlist_t *ilist, instr_t *where)
{
    instr_t *instr;
    uint flags;
    
    for (instr = where; instr != NULL; instr = instr_get_next(instr)) {
        flags = instr_get_arith_flags(instr);
        if (TESTANY(EFLAGS_READ_6, flags))
            return false;
        if (TESTALL(EFLAGS_WRITE_6, flags))
            return true;
    }
    return false;
}
Exemplo n.º 10
0
static dr_emit_flags_t
event_basic_block(void *drcontext, void *tag, instrlist_t *bb,
                  bool for_trace, bool translating)
{
    instr_t *instr, *first = instrlist_first(bb), *point = NULL;
    uint num_instrs;
	uint flags;
	uint need_restore;

	/* count instruction */
    for (instr  = first, num_instrs = 0;
         instr != NULL;
         instr = instr_get_next(instr)) {

        num_instrs++;

    }
	
	need_restore = 0;
	flags = instr_get_arith_flags(instr);
	/* eflags are not dead save eflags to register */
	if (!(TESTALL(EFLAGS_WRITE_6, flags) && !TESTANY(EFLAGS_READ_6, flags))) {
		need_restore = 1;
		dr_save_reg(drcontext, bb, first, DR_REG_XAX, SPILL_SLOT_1);
		dr_save_arith_flags_to_xax(drcontext, bb, first);
	}

	/* add the instruction count */
    instrlist_meta_preinsert
        (bb, first,
         INSTR_CREATE_add(drcontext, OPND_CREATE_ABSMEM
                               ((byte *)&global_count, OPSZ_4), OPND_CREATE_INT32(num_instrs)));

	/* Need to carry since it is a 8 byte variable. */
    instrlist_meta_preinsert
        (bb, first,
         INSTR_CREATE_adc(drcontext, OPND_CREATE_ABSMEM
                               ((byte *)&global_count + 4, OPSZ_4), OPND_CREATE_INT32(0)));

	/* resotre eflags */
	if (need_restore) {
		dr_restore_arith_flags_from_xax(drcontext, bb, first);
		dr_restore_reg(drcontext, bb, first, DR_REG_XAX, SPILL_SLOT_1);
	}

    return DR_EMIT_DEFAULT;
}
Exemplo n.º 11
0
static void
print_arg(buf_info_t *buf, drsys_arg_t *arg)
{
    if (arg->ordinal == -1)
        OUTPUT(buf, "\tretval: ");
    else
        OUTPUT(buf, "\targ %d: ", arg->ordinal);

    if (arg->enum_name != NULL) {
        if (drstrace_get_arg_symname(buf, arg))
            return;
    }
    /* XXX: add return value to dr_fprintf so we can more easily align
     * after PFX vs PIFX w/o having to print to buffer
     */
    switch (arg->type) {
    case DRSYS_TYPE_VOID:         print_simple_value(buf, arg, true); break;
    case DRSYS_TYPE_POINTER:      print_simple_value(buf, arg, true); break;
    case DRSYS_TYPE_BOOL:         print_simple_value(buf, arg, false); break;
    case DRSYS_TYPE_INT:          print_simple_value(buf, arg, false); break;
    case DRSYS_TYPE_SIGNED_INT:   print_simple_value(buf, arg, false); break;
    case DRSYS_TYPE_UNSIGNED_INT: print_simple_value(buf, arg, false); break;
    case DRSYS_TYPE_HANDLE:       print_simple_value(buf, arg, false); break;
    case DRSYS_TYPE_NTSTATUS:     print_simple_value(buf, arg, false); break;
    case DRSYS_TYPE_ATOM:         print_simple_value(buf, arg, false); break;
    default: {
        if (arg->value == 0) {
            OUTPUT(buf, "<null>");
        } else if (arg->pre && !TEST(DRSYS_PARAM_IN, arg->mode)) {
            OUTPUT(buf, PFX, arg->value);
        } else {
            if (!print_known_compound_type(buf, arg->type, (void *) arg->value))
                OUTPUT(buf, "<NYI>");
        }
    }
    }

    OUTPUT(buf, " (%s%s%stype=%s%s, size="PIFX")\n",
           (arg->arg_name == NULL) ? "" : "name=",
           (arg->arg_name == NULL) ? "" : arg->arg_name,
           (arg->arg_name == NULL) ? "" : ", ",
           (arg->type_name == NULL) ? "\"\"" : arg->type_name,
           (arg->type_name == NULL ||
            TESTANY(DRSYS_PARAM_INLINED|DRSYS_PARAM_RETVAL, arg->mode)) ? "" : "*",
           arg->size);
}
Exemplo n.º 12
0
/* Passing in stub's info avoids a vmvector lookup */
void
unlink_entrance_stub(dcontext_t *dcontext, cache_pc stub, uint flags,
                     coarse_info_t *info /*OPTIONAL*/)
{
	// COMPLETEDD #499 unlink_entrance_stub
	printf("Starting unlink_enterance_stub\n");
  cache_pc tgt;
  ASSERT(DYNAMO_OPTION(coarse_units));
  ASSERT(coarse_is_entrance_stub(stub));
  ASSERT(self_owns_recursive_lock(&change_linking_lock));
  LOG(THREAD, LOG_LINKS, 5,
      "unlink_entrance_stub "PFX"\n", stub);
  if (TESTANY(FRAG_IS_TRACE_HEAD|FRAG_IS_TRACE, flags))
      tgt = trace_head_return_coarse_prefix(stub, info);
  else
      tgt = fcache_return_coarse_prefix(stub, info);
  if (patch_coarse_branch(stub, tgt, HOT_PATCHABLE, info))
      STATS_INC(pcache_unprot_unlink);
}
Exemplo n.º 13
0
size_t
hashtable_persist_size(void *drcontext, hashtable_t *table, size_t entry_size,
                       void *perscxt, hasthable_persist_flags_t flags)
{
    uint count = 0;
    if (table->hashtype == HASH_INTPTR &&
        TESTANY(DR_HASHPERS_ONLY_IN_RANGE | DR_HASHPERS_ONLY_PERSISTED, flags)) {
        /* synch is already provided */
        uint i;
        ptr_uint_t start = 0;
        size_t size = 0;
        if (perscxt != NULL) {
            start = (ptr_uint_t) dr_persist_start(perscxt);
            size = dr_persist_size(perscxt);
        }
        count = 0;
        for (i = 0; i < HASHTABLE_SIZE(table->table_bits); i++) {
            hash_entry_t *he;
            for (he = table->table[i]; he != NULL; he = he->next) {
                if ((!TEST(DR_HASHPERS_ONLY_IN_RANGE, flags) ||
                     key_in_range(table, he, start, size)) &&
                    (!TEST(DR_HASHPERS_ONLY_PERSISTED, flags) ||
                     dr_fragment_persistable(drcontext, perscxt, he->key)))
                    count++;
            }
        }
    } else
        count = table->entries;
    /* we could have an OUT count param that user must pass to hashtable_persist,
     * but that's actually a pain for the user when persisting multiple tables,
     * and usage should always call hashtable_persist() right after calling
     * hashtable_persist_size().
     */
    table->persist_count = count;
    return sizeof(count) +
        (TEST(DR_HASHPERS_REBASE_KEY, flags) ? sizeof(ptr_uint_t) : 0) +
        count * (entry_size + sizeof(void*));
}
Exemplo n.º 14
0
static inline void
check_translation(instrlist_t *ilist, instr_t *inst)
{
    if (ilist->translation_target != NULL && instr_get_translation(inst) == NULL) {
        instr_set_translation(inst, ilist->translation_target);
    }
    if (instrlist_get_our_mangling(ilist))
        instr_set_our_mangling(inst, true);
#if defined(CLIENT_INTERFACE) && defined(ARM)
    if (instr_is_meta(inst)) {
        dr_pred_type_t auto_pred = ilist->auto_pred;
        if (instr_predicate_is_cond(auto_pred)) {
            CLIENT_ASSERT(!instr_is_cti(inst), "auto-predication does not support cti's");
            CLIENT_ASSERT(
                !TESTANY(EFLAGS_WRITE_NZCV,
                         instr_get_arith_flags(inst, DR_QUERY_INCLUDE_COND_SRCS)),
                "cannot auto predicate a meta-inst that writes to NZCV");
            if (!instr_is_predicated(inst))
                instr_set_predicate(inst, auto_pred);
        }
    }
#endif
}
Exemplo n.º 15
0
static dr_emit_flags_t
event_basic_block(void *drcontext, void *tag, instrlist_t *bb,
                  bool for_trace, bool translating)
{
    int i;
    const int MAX_INSTR_LEN = 64;
    char instr_name[MAX_INSTR_LEN];
    instr_t *instr, *first = instrlist_first(bb);
    uint flags;
    uint cur_flop_count = 0;
    uint tracked_instr_count[tracked_instrs_len];
    for( i = 0; i < tracked_instrs_len; i++ ) tracked_instr_count[i] = 0;

#ifdef VERBOSE
    dr_printf("in dynamorio_basic_block(tag="PFX")\n", tag);
# ifdef VERBOSE_VERBOSE
    instrlist_disassemble(drcontext, tag, bb, STDOUT);
# endif
#endif

    /* we use fp ops so we have to save fp state */
    byte fp_raw[512 + 16];
    byte *fp_align = (byte *) ( (((ptr_uint_t)fp_raw) + 16) & ((ptr_uint_t)-16) );


    if (translating) {
        return DR_EMIT_DEFAULT;
    }
    proc_save_fpstate(fp_align);

    int my_readfrom[DR_REG_LAST_VALID_ENUM+MY_NUM_EFLAGS+1];
    int my_writtento[DR_REG_LAST_VALID_ENUM+MY_NUM_EFLAGS+1];

    for (i = 0; i < DR_REG_LAST_VALID_ENUM+MY_NUM_EFLAGS+1; i++) {
        my_readfrom[i] = 0;
        my_writtento[i] = 0;
    }

    t_glob_reg_state glob_reg_state = {0,0,0,0,0,0,my_readfrom,my_writtento};

    int my_cur_size = 0;
    for (instr = instrlist_first(bb); instr != NULL; instr = instr_get_next(instr)) {
        my_cur_size++;

        /* ILP Calculations */
        glob_reg_state.raw_setnr = 1;
        glob_reg_state.war_setnr = 1;
        glob_reg_state.waw_setnr = 1;
        glob_reg_state.else_setnr = 1;
        glob_reg_state.final_setnr = 1;
        calc_set_num(instr, &glob_reg_state);

        /* Count flop instr */
        if( instr_is_floating( instr ) ) {
            cur_flop_count += 1;
        }

        /* Count mul instructions */
        instr_disassemble_to_buffer( drcontext, instr, instr_name, MAX_INSTR_LEN );
        for( i = 0; i < tracked_instrs_len; i++ ) {
            if( strncmp( instr_name, tracked_instrs[i], strlen(tracked_instrs[i])) == 0) {
                tracked_instr_count[i] += 1;
            }
        }
    }

    //now we can calculate the ILP.
    float ilp = ((float)my_cur_size) / ((float)(glob_reg_state.num_sets != 0 ?
                glob_reg_state.num_sets : 1));

    dr_mutex_lock(stats_mutex);

    // Due to lack of memory, we only store the ILPs for the latest MY_MAX_BB
    // basic blocks. This enables us to run e.g. firefox.
    int my_cur_num = my_bbcount % MY_MAX_BB;
    my_bbcount++;
    if(my_cur_num == 0 && my_bbcount > 1) {
         dr_printf("Overflow at %d\n", my_bbcount);
    }
    my_bbexecs[my_cur_num] = 0; //initialize
    my_bbsizes[my_cur_num] = my_cur_size;
    bb_flop_count[my_cur_num] = cur_flop_count;
    for( i = 0; i < tracked_instrs_len; i++ ) {
        bb_instr_count[my_cur_num*tracked_instrs_len+i] = tracked_instr_count[i];
    }
    my_bbilp[my_cur_num] = ilp;

    dr_mutex_unlock(stats_mutex);

#ifdef USE_CLEAN_CALL
     dr_insert_clean_call(drcontext, bb, instrlist_first(bb), clean_call, false, 1,
                           OPND_CREATE_INT32(my_cur_num));
#else
#ifdef INSERT_AT_END
    instr = NULL;
#else
    // Find place to insert inc instruction
    for (instr = first; instr != NULL; instr = instr_get_next(instr)) {
        flags = instr_get_arith_flags(instr);
        if (TESTALL(EFLAGS_WRITE_6, flags) && !TESTANY(EFLAGS_READ_6, flags))
            break;
    }
#endif
    if (instr == NULL) { // no suitable place found, save regs
        dr_save_reg(drcontext, bb, first, DR_REG_XAX, SPILL_SLOT_1);
        dr_save_arith_flags_to_xax(drcontext, bb, first);
    }
    // Increment my_bbexecs[my_current_bb] using the lock prefix
    instrlist_meta_preinsert
        (bb, (instr == NULL) ? first : instr,
         LOCK(INSTR_CREATE_inc(drcontext, OPND_CREATE_ABSMEM
                               ((byte *)&(my_bbexecs[my_cur_num]), OPSZ_4))));
    if (instr == NULL) { // no suitable place found earlier, restore regs
        dr_restore_arith_flags_from_xax(drcontext, bb, first);
        dr_restore_reg(drcontext, bb, first, DR_REG_XAX, SPILL_SLOT_1);
    }
#endif

    proc_restore_fpstate(fp_align);
    
#if defined(VERBOSE) && defined(VERBOSE_VERBOSE)
    dr_printf("Finished instrumenting dynamorio_basic_block(tag="PFX")\n", tag);
    instrlist_disassemble(drcontext, tag, bb, STDOUT);
#endif
    return DR_EMIT_DEFAULT;
}
Exemplo n.º 16
0
static void
print_arg(drsys_arg_t *arg)
{
    if (arg->ordinal == -1)
        OUTPUT("\tretval: ");
    else
        OUTPUT("\targ %d: ", arg->ordinal);
    /* XXX: add return value to dr_fprintf so we can more easily align
     * after PFX vs PIFX w/o having to print to buffer
     */
    switch (arg->type) {
    case DRSYS_TYPE_VOID:         print_simple_value(arg, true); break;
    case DRSYS_TYPE_POINTER:      print_simple_value(arg, true); break;
    case DRSYS_TYPE_BOOL:         print_simple_value(arg, false); break;
    case DRSYS_TYPE_INT:          print_simple_value(arg, false); break;
    case DRSYS_TYPE_SIGNED_INT:   print_simple_value(arg, false); break;
    case DRSYS_TYPE_UNSIGNED_INT: print_simple_value(arg, false); break;
    case DRSYS_TYPE_HANDLE:       print_simple_value(arg, false); break;
    case DRSYS_TYPE_NTSTATUS:     print_simple_value(arg, false); break;
    case DRSYS_TYPE_ATOM:         print_simple_value(arg, false); break;
    default: {
        if (arg->value == 0) {
            OUTPUT("<null>");
        } else if (arg->pre && !TEST(DRSYS_PARAM_IN, arg->mode)) {
            OUTPUT(PFX, arg->value);
        } else {
            switch (arg->type) {
            case DRSYS_TYPE_UNICODE_STRING: {
                print_unicode_string((UNICODE_STRING *) arg->value);
                break;
            }
            case DRSYS_TYPE_OBJECT_ATTRIBUTES: {
                OBJECT_ATTRIBUTES *oa = (OBJECT_ATTRIBUTES *) arg->value;
                OUTPUT("len="PIFX", root="PIFX", name=",
                       oa->Length, oa->RootDirectory);
                print_unicode_string(oa->ObjectName);
                OUTPUT(", att="PIFX", sd="PFX", sqos="PFX,
                       oa->Attributes, oa->SecurityDescriptor,
                       oa->SecurityQualityOfService);
                break;
            }
            case DRSYS_TYPE_IO_STATUS_BLOCK: {
                IO_STATUS_BLOCK *io = (IO_STATUS_BLOCK *) arg->value;
                OUTPUT("status="PIFX", info="PIFX"", io->StatusPointer.Status,
                       io->Information);
                break;
            }
            case DRSYS_TYPE_LARGE_INTEGER: {
                LARGE_INTEGER *li = (LARGE_INTEGER *) arg->value;
                OUTPUT("0x"HEX64_FORMAT_STRING, li->QuadPart);
                break;
            }
            default: {
                /* FIXME i#1089: add the other types */
                OUTPUT("<NYI>");
            }
            }
            /* XXX: we want KEY_VALUE_PARTIAL_INFORMATION, etc. like in
             * syscall_diagnostics.  Add drsyscall types for those, or hardcode here?
             */
        }
    }
    }
    
    OUTPUT(" (%s%s%stype=%s%s, size="PIFX")\n",
           (arg->arg_name == NULL) ? "" : "name=",
           (arg->arg_name == NULL) ? "" : arg->arg_name,
           (arg->arg_name == NULL) ? "" : ", ",
           (arg->type_name == NULL) ? "\"\"" : arg->type_name,
           (arg->type_name == NULL ||
            TESTANY(DRSYS_PARAM_INLINED|DRSYS_PARAM_RETVAL, arg->mode)) ? "" : "*",
           arg->size);
}