/* **********************************************************
 * Copyright (c) 2012-2021 Google, Inc.  All rights reserved.
 * Copyright (c) 2000-2008 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of VMware, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

/* Copyright (c) 2003-2007 Determina Corp. */
/* Copyright (c) 2001-2003 Massachusetts Institute of Technology */
/* Copyright (c) 2000-2001 Hewlett-Packard Company */

/*
 * link.h - link exports
 */

#ifndef _CORE_LINK_H_ /* using different define than /usr/include/link.h */
#define _CORE_LINK_H_ 1

/* linkstub_t flags
 * WARNING: flags field is a ushort, so max flag is 0x8000!
 */
enum {
    /***************************************************/
    /* these first flags are also used on the instr_t data structure */

    /* Type of branch and thus which struct is used for this exit.
     * Due to a tight namespace (flags is a ushort field), we pack our
     * 3 types into these 2 bits, so the LINKSTUB_ macros are used to
     * distinguish, rather than raw bit tests:
     *
     *   name          LINK_DIRECT LINK_INDIRECT  struct
     *   ---------------  ---         ---         --------------------------
     *   (subset of fake)  0           0          linkstub_t
     *   normal direct     1           0          direct_linkstub_t
     *   normal indirect   0           1          indirect_linkstub_t
     *   cbr fallthrough   1           1          cbr_fallthrough_linkstub_t
     *
     * Note that we can have fake linkstubs that should be treated as
     * direct or indirect, so LINK_FAKE is a separate flag.
     */
    LINK_DIRECT = 0x0001,
    LINK_INDIRECT = 0x0002,
    /* more specifics on type of branch
     * must check LINK_DIRECT vs LINK_INDIRECT for JMP and CALL.
     * absence of all of these is relied on as an indicator of shared_syscall
     * in indirect_linkstub_target(), so we can't get rid of LINK_RETURN
     * and use absence of CALL & JMP to indicate it.
     */
    LINK_RETURN = 0x0004,
    /* JMP|CALL indicates JMP_PLT, so use EXIT_IS_{JMP,CALL} rather than these raw bits */
    LINK_CALL = 0x0008,
    LINK_JMP = 0x0010,

    /* Indicates a far cti which uses a separate ibl entry */
    LINK_FAR = 0x0020,

    /* This exit cti has a preceding NOP to avoid its immediate from crossing a cache
     * line.  This flag lets us identify NOPs which we added as opposed to the client.
     */
    LINK_PADDED = 0x0040,

#ifdef X64
    /* PR 257963: since we don't store targets of ind branches, we need a flag
     * so we know whether this is a trace cmp exit, which has its own ibl entry
     */
    LINK_TRACE_CMP = 0x0080,
#endif
    /* Flags that tell DR to take some action upon returning to d_r_dispatch.
     * This first one is multiplexed via dcontext->upcontext.upcontext.exit_reason.
     * All uses are assumed to be unlinkable.
     */
    LINK_SPECIAL_EXIT = 0x0100,
#ifdef WINDOWS
    LINK_CALLBACK_RETURN = 0x0200,
#else
    /* PR 286922: we support both OP_sys{call,enter}- and OP_int-based system calls */
    LINK_NI_SYSCALL_INT = 0x0200,
#endif
    /* indicates whether exit is before a non-ignorable syscall */
    LINK_NI_SYSCALL = 0x0400,
    LINK_FINAL_INSTR_SHARED_FLAG = LINK_NI_SYSCALL,
    /* end of instr_t-shared flags  */
    /***************************************************/

    LINK_FRAG_OFFS_AT_END = 0x0800,

    LINK_END_OF_LIST = 0x1000,

    LINK_FAKE = 0x2000,

    LINK_LINKED = 0x4000,

    LINK_SEPARATE_STUB = 0x8000,

    /* WARNING: flags field is a ushort, so max flag is 0x8000! */
};

#ifdef UNIX
#    define LINK_NI_SYSCALL_ALL (LINK_NI_SYSCALL | LINK_NI_SYSCALL_INT)
#else
#    define LINK_NI_SYSCALL_ALL LINK_NI_SYSCALL
#endif

/* linkstub_t heap layout is now quite variable.  linkstubs are laid out
 * after the fragment_t structure, which is itself variable.
 * The indirect_linkstub_t split was case 6468
 * The cbr_fallthrough_linkstub_t split was case 6528
 *
 *   fragment_t/trace_t
 *   array composed of three different sizes of linkstub_t subclasses:
 *     direct_linkstub_t
 *     cbr_fallthrough_linkstub_t
 *     indirect_linkstub_t
 *   post_linkstub_t
 *
 * There are three types of specially-supported basic blocks that
 * have no post_linkstub_t:
 *
 *   fragment_t
 *   indirect_linkstub_t
 *
 *   fragment_t
 *   direct_linkstub_t
 *   direct_linkstub_t
 *
 *   fragment_t
 *   direct_linkstub_t
 *   cbr_fallthrough_linkstub_t
 */

/* To save memory, we have three types of linkstub structs: direct,
 * cbr_fallthrough, and indirect.  They each contain the flags field
 * at the same offset, so this struct can be used as a superclass.
 */
struct _linkstub_t {
    ushort flags; /* contains LINK_ flags above */

    /* We limit all fragment bodies to USHRT_MAX size
     * thus we can save space by making the branch ptr a ushort offset.
     * Do not directly access this field -- use EXIT_CTI_PC()
     */
    ushort cti_offset; /* offset from fragment start_pc of this cti */
};

/* linkage info common to all direct fragment exits */
typedef struct _common_direct_linkstub_t {
    linkstub_t l;

    /* outgoing stubs of a fragment never change, and are allocated in
     * an array, but we walk them like a linked list since we don't want
     * to waste space storing the count and all of our access patterns
     * want to touch them all anyway.
     * use LINKSTUB_NEXT_EXIT() to access the next, and LINKSTUB_FINAL()
     * to test if the current guy is the final.
     *
     * Incoming stubs do change and we use this field to chain them:
     */
    linkstub_t *next_incoming;

#ifdef TRACE_HEAD_CACHE_INCR
    /* for linking to trace head, we store the actual fragment_t target.
     * if the target is deleted the link will be unlinked, preventing
     * a stale fragment_t* pointer from sitting around
     */
    fragment_t *target_fragment;
#endif
} common_direct_linkstub_t;

/* linkage info for each direct fragment exit */
typedef struct _direct_linkstub_t {
    common_direct_linkstub_t cdl;

    /* tag identifying the intended app target of the exit branch
     * Do not directly access this field -- use EXIT_TARGET_TAG(), which
     * works for all linkstub types.
     */
    app_pc target_tag;

    /* Must be absolute pc b/c we relocate some stubs away from fragment body.
     * Do not directly access this field -- use EXIT_STUB_PC(), which
     * works for all linkstub types.
     */
    cache_pc stub_pc;
} direct_linkstub_t;

/* linkage info for cbr fallthrough exits that satisfy two conditions:
 *   1) separate stubs will not be individually freed -- we could
 *      have a fancy scheme that frees both at once, but we simply
 *      disallow the struct if any freeing will occur.
 *   2) the fallthrough target is within range of a signed short from the
 *      owning fragment's start pc (this is typically true even w/ eliding).
 *   3) the fallthrough exit immediately follows the cbr exit.
 * this direct stub split was case 6528.
 */
typedef struct _cbr_fallthrough_linkstub_t {
    /* We have no cti_offset as we assume this exit's cti immediately follows
     * the preceding cbr (see conditions above).
     *
     * Our target_tag uses the cti_offset field instead.  Since this struct is
     * only used for the 2nd (fallthrough) exit of a cbr whose target
     * is within range for a signed short from the owning fragment_t's tag,
     * we re-use the cti_offset field.
     *
     * This struct is also only used when its exit stub is adjacent to the
     * prior exit's, so we don't need to store stub_pc here.
     */
    common_direct_linkstub_t cdl;
} cbr_fallthrough_linkstub_t;

/* linkage info for each indirect fragment exit (slit for case 6468) */
typedef struct _indirect_linkstub_t {
    linkstub_t l;
} indirect_linkstub_t;

/* Data shared among all linkstubs for a particular fragment_t.
 * Kept at the end of the array of linkstubs, and not present for
 * certain common types of fragment_t.
 */
typedef struct _post_linkstub_t {
    /* fragment_t + linkstub_t heap size cannot exceed the maximum fragment
     * body size since max sizeof linkstub_t-subclass (16) < min sizeof
     * exit stub (15) + corresponding cti (at least 2).  Thus we can
     * use a ushort here, paving the way for an additional field in
     * the future (though right now the other 2 bytes are wasted as
     * this struct is aligned to 4 bytes ).
     */
    ushort fragment_offset;
    /* We force the compiler to maintain our 4-byte-alignment for heap allocations.
     * FIXME: should we support un-aligned in non-special heaps?
     * or could use special heap if we really wanted to save this 2 bytes.
     * also see case 6518 for a way to use this for Traces.
     */
    ushort padding;
} post_linkstub_t;

/* For chaining together a list of inter-coarse-unit incoming stubs.  To
 * eliminate the need for wrappers for a series of fine-grained linkstubs, we
 * directly chain those -- so when walking, walk a fine entry's linkstubs
 * completely before going to the next coarse_incoming_t entry.
 */
typedef struct _coarse_incoming_t {
    union {
        cache_pc stub_pc;
        linkstub_t *fine_l;
    } in;
    bool coarse;
    struct _coarse_incoming_t *next;
} coarse_incoming_t;

/* this one does not take in flags b/c historically we used other fields */
#define LINKSTUB_FAKE(l) (TEST(LINK_FAKE, (l)->flags))
/* direct includes normal direct and cbr fallthrough */
#define LINKSTUB_DIRECT(flags) (TEST(LINK_DIRECT, (flags)))
#define LINKSTUB_NORMAL_DIRECT(flags) \
    (TEST(LINK_DIRECT, (flags)) && !TEST(LINK_INDIRECT, (flags)))
#define LINKSTUB_INDIRECT(flags) \
    (!TEST(LINK_DIRECT, (flags)) && TEST(LINK_INDIRECT, (flags)))
#define LINKSTUB_CBR_FALLTHROUGH(flags) \
    (TEST(LINK_DIRECT, (flags)) && TEST(LINK_INDIRECT, (flags)))

/* used with both LINK_* and INSTR_*_EXIT flags */
#define EXIT_IS_CALL(flags) (TEST(LINK_CALL, flags) && !TEST(LINK_JMP, flags))
#define EXIT_IS_JMP(flags) (TEST(LINK_JMP, flags) && !TEST(LINK_CALL, flags))
#define EXIT_IS_IND_JMP_PLT(flags) (TESTALL(LINK_JMP | LINK_CALL, flags))

#define LINKSTUB_FINAL(l) (TEST(LINK_END_OF_LIST, (l)->flags))

/* We assume this combination of flags is unique for coarse proxies. */
#define LINKSTUB_COARSE_PROXY(flags) \
    (TESTALL(LINK_FAKE | LINK_DIRECT | LINK_SEPARATE_STUB, (flags)))

/* could optimize to avoid redundant tests */
#define LINKSTUB_SIZE(l)                                                              \
    (LINKSTUB_NORMAL_DIRECT((l)->flags)                                               \
         ? sizeof(direct_linkstub_t)                                                  \
         : (LINKSTUB_INDIRECT((l)->flags) ? sizeof(indirect_linkstub_t)               \
                                          : (LINKSTUB_CBR_FALLTHROUGH((l)->flags)     \
                                                 ? sizeof(cbr_fallthrough_linkstub_t) \
                                                 : sizeof(linkstub_t))))

#define LINKSTUB_NEXT_EXIT(l) \
    ((LINKSTUB_FINAL((l))) ? NULL : ((linkstub_t *)(((byte *)(l)) + LINKSTUB_SIZE(l))))

/* we pay the cost of the check in release builds to have the
 * safety return value of NULL
 */
#define LINKSTUB_NEXT_INCOMING(l)                                             \
    (LINKSTUB_NORMAL_DIRECT((l)->flags)                                       \
         ? ((linkstub_t *)(((direct_linkstub_t *)(l))->cdl.next_incoming))    \
         : (LINKSTUB_CBR_FALLTHROUGH((l)->flags)                              \
                ? ((linkstub_t *)(((cbr_fallthrough_linkstub_t *)(l))         \
                                      ->cdl.next_incoming))                   \
                : (ASSERT(false && "indirect linkstub has no next_incoming"), \
                   ((linkstub_t *)NULL))))

/* if sharing a stub then no offs, else offs to get to subsequent stub */
#define CBR_FALLTHROUGH_STUB_OFFS(f) \
    (INTERNAL_OPTION(cbr_single_stub) ? 0 : DIRECT_EXIT_STUB_SIZE((f)->flags))

#define EXIT_CTI_PC_HELPER(f, l) \
    (ASSERT(LINKSTUB_NORMAL_DIRECT((l)->flags)), ((f)->start_pc + (l)->cti_offset))

#define EXIT_CTI_PC(f, l)                                                            \
    (LINKSTUB_CBR_FALLTHROUGH((l)->flags)                                            \
         ? (cbr_fallthrough_exit_cti(EXIT_CTI_PC_HELPER(f, FRAGMENT_EXIT_STUBS(f)))) \
         : ((f)->start_pc + (l)->cti_offset))

#define EXIT_STUB_PC_HELPER(dc, f, l) \
    (ASSERT(LINKSTUB_NORMAL_DIRECT((l)->flags)), (((direct_linkstub_t *)(l))->stub_pc))

#define EXIT_STUB_PC(dc, f, l)                                          \
    (LINKSTUB_NORMAL_DIRECT((l)->flags)                                 \
         ? (((direct_linkstub_t *)(l))->stub_pc)                        \
         : (LINKSTUB_CBR_FALLTHROUGH((l)->flags)                        \
                ? (EXIT_STUB_PC_HELPER(dc, f, FRAGMENT_EXIT_STUBS(f)) + \
                   CBR_FALLTHROUGH_STUB_OFFS(f))                        \
                : indirect_linkstub_stub_pc(dc, f, l)))

#define EXIT_TARGET_TAG(dc, f, l)                                                        \
    (LINKSTUB_NORMAL_DIRECT((l)->flags)                                                  \
         ? (((direct_linkstub_t *)(l))->target_tag)                                      \
         : (LINKSTUB_CBR_FALLTHROUGH((l)->flags) ? ((f)->tag + ((short)(l)->cti_offset)) \
                                                 : indirect_linkstub_target(dc, f, l)))

#ifdef WINDOWS
#    define EXIT_TARGETS_SHARED_SYSCALL(flags) \
        (DYNAMO_OPTION(shared_syscalls) &&     \
         !TESTANY(LINK_RETURN | LINK_CALL | LINK_JMP, (flags)))
#else
#    define EXIT_TARGETS_SHARED_SYSCALL(flags) (false)
#endif

/* indirect exits w/o inlining have no stub at all for -no_indirect_stubs */
#define EXIT_HAS_STUB(l_flags, f_flags)                                       \
    (DYNAMO_OPTION(indirect_stubs) || !LINKSTUB_INDIRECT((l_flags)) ||        \
     (!EXIT_TARGETS_SHARED_SYSCALL(l_flags) &&                                \
      ((DYNAMO_OPTION(inline_trace_ibl) && TEST(FRAG_IS_TRACE, (f_flags))) || \
       (DYNAMO_OPTION(inline_bb_ibl) && !TEST(FRAG_IS_TRACE, (f_flags))))))

/* two cases with no local stub: a separate stub or no stub at all */
#define EXIT_HAS_LOCAL_STUB(l_flags, f_flags) \
    (EXIT_HAS_STUB(l_flags, f_flags) && !TEST(LINK_SEPARATE_STUB, (l_flags)))

void
d_r_link_init(void);
void
d_r_link_exit(void);
void
link_reset_init(void);
void
link_reset_free(void);
void
link_thread_init(dcontext_t *dcontext);
void
link_thread_exit(dcontext_t *dcontext);

/* coarse-grain support */

cache_pc
coarse_stubs_create(coarse_info_t *info, cache_pc pc, size_t size);

void
coarse_stubs_delete(coarse_info_t *info);

uint
coarse_stub_alignment(coarse_info_t *info);

void
coarse_mark_trace_head(dcontext_t *dcontext, fragment_t *f, coarse_info_t *info,
                       cache_pc stub, cache_pc body);

void
coarse_unit_unlink(dcontext_t *dcontext, coarse_info_t *info);

void
coarse_unit_unlink_outgoing(dcontext_t *dcontext, coarse_info_t *info);

#ifdef DEBUG
bool
coarse_unit_outgoing_linked(dcontext_t *dcontext, coarse_info_t *info);
#endif

cache_pc
coarse_stub_lookup_by_target(dcontext_t *dcontext, coarse_info_t *info,
                             cache_pc target_tag);

void
coarse_lazy_link(dcontext_t *dcontext, fragment_t *targetf);

cache_pc
fcache_return_coarse_prefix(cache_pc stub_pc, coarse_info_t *info /*OPTIONAL*/);

cache_pc
trace_head_return_coarse_prefix(cache_pc stub_pc, coarse_info_t *info /*OPTIONAL*/);

cache_pc
get_coarse_ibl_prefix(dcontext_t *dcontext, cache_pc stub_pc,
                      ibl_branch_type_t branch_type);

bool
in_coarse_stubs(cache_pc pc);

bool
in_coarse_stub_prefixes(cache_pc pc);

cache_pc
coarse_deref_ibl_prefix(dcontext_t *dcontext, cache_pc target);

coarse_info_t *
get_stub_coarse_info(cache_pc pc);

/* Initializes an array of linkstubs beginning with first */
void
linkstubs_init(linkstub_t *first, int num_direct, int num_indirect, fragment_t *f);

bool
is_linkable(dcontext_t *dcontext, fragment_t *from_f, linkstub_t *l, fragment_t *targetf,
            bool have_link_lock, bool mark_new_trace_head);

uint
linkstub_size(dcontext_t *dcontext, fragment_t *f, linkstub_t *l);

uint
linkstub_propagatable_flags(uint flags);

bool
linkstub_frag_offs_at_end(uint flags, int direct_exits, int indirect_exits);

bool
use_cbr_fallthrough_short(uint flags, int direct_exits, int indirect_exits);

uint
linkstubs_heap_size(uint flags, int direct_exits, int indirect_exits);

fragment_t *
linkstub_fragment(dcontext_t *dcontext, linkstub_t *l);

#ifdef X64
/* Converts the canonical empty fragment to an empty fragment marked FRAG_32_BIT */
fragment_t *
empty_fragment_mark_x86(fragment_t *f);
#endif

#ifdef DEBUG
bool
linkstub_owned_by_fragment(dcontext_t *dcontext, fragment_t *f, linkstub_t *l);
#endif

void
set_last_exit(dcontext_t *dcontext, linkstub_t *l);
void
last_exit_deleted(dcontext_t *dcontext);
void
set_coarse_ibl_exit(dcontext_t *dcontext);
int
get_last_linkstub_ordinal(dcontext_t *dcontext);

linkstub_t *
get_deleted_linkstub(dcontext_t *dcontext);
const linkstub_t *
get_starting_linkstub(void);
const linkstub_t *
get_reset_linkstub(void);
const linkstub_t *
get_syscall_linkstub(void);
const linkstub_t *
get_selfmod_linkstub(void);
#ifdef AARCH64
/* On AArch64 we need to refer to linkstub_selfmod from aarch64.asm. */
extern const linkstub_t linkstub_selfmod;
#endif
const linkstub_t *
get_ibl_deleted_linkstub(void);
/* This is used for Windows APC, callback, etc. and Linux sigreturn, forge fault, etc. */
const linkstub_t *
get_asynch_linkstub(void);
const linkstub_t *
get_native_exec_linkstub(void);
const linkstub_t *
get_native_exec_syscall_linkstub(void);
#ifdef HOT_PATCHING_INTERFACE
const linkstub_t *
get_hot_patch_linkstub(void);
#endif
const linkstub_t *
get_client_linkstub(void);
const linkstub_t *
get_special_ibl_linkstub(ibl_branch_type_t ibl_type, bool is_trace);
const linkstub_t *
get_ibl_sourceless_linkstub(uint link_flags, uint frag_flags);
bool
is_ibl_sourceless_linkstub(const linkstub_t *l);
const linkstub_t *
get_coarse_exit_linkstub(void);
const linkstub_t *
get_coarse_trace_head_exit_linkstub(void);

#define IS_COARSE_LINKSTUB(l) \
    ((l) == get_coarse_exit_linkstub() || (l) == get_coarse_trace_head_exit_linkstub())

#ifdef WINDOWS
const linkstub_t *
get_shared_syscalls_unlinked_linkstub(void);
#    define IS_SHARED_SYSCALLS_UNLINKED_LINKSTUB(l) \
        ((l) == get_shared_syscalls_unlinked_linkstub())

const linkstub_t *
get_shared_syscalls_trace_linkstub(void);
const linkstub_t *
get_shared_syscalls_bb_linkstub(void);
#    define IS_SHARED_SYSCALLS_LINKSTUB(l)              \
        ((l) == get_shared_syscalls_trace_linkstub() || \
         (l) == get_shared_syscalls_bb_linkstub() ||    \
         (l) == get_shared_syscalls_unlinked_linkstub())
#    define IS_SHARED_SYSCALLS_TRACE_LINKSTUB(l) \
        ((l) == get_shared_syscalls_trace_linkstub())
#else
#    define IS_SHARED_SYSCALLS_UNLINKED_LINKSTUB(l) false
#    define IS_SHARED_SYSCALLS_LINKSTUB(l) false
#    define IS_SHARED_SYSCALLS_TRACE_LINKSTUB(l) false
#endif

bool
should_separate_stub(dcontext_t *dcontext, app_pc target, uint fragment_flags);
int
local_exit_stub_size(dcontext_t *dcontext, app_pc target, uint fragment_flags);
void
separate_stub_create(dcontext_t *dcontext, fragment_t *f, linkstub_t *l);
void
linkstub_free_exitstubs(dcontext_t *dcontext, fragment_t *f);

void
linkstubs_shift(dcontext_t *dcontext, fragment_t *f, ssize_t shift);

void
link_new_fragment(dcontext_t *dcontext, fragment_t *f);
void
link_fragment_outgoing(dcontext_t *dcontext, fragment_t *f, bool new_fragment);
void
unlink_fragment_outgoing(dcontext_t *dcontext, fragment_t *f);
void
link_fragment_incoming(dcontext_t *dcontext, fragment_t *f, bool new_fragment);
void
unlink_fragment_incoming(dcontext_t *dcontext, fragment_t *f);

void
shift_links_to_new_fragment(dcontext_t *dcontext, fragment_t *old_f, fragment_t *new_f);
future_fragment_t *
incoming_remove_fragment(dcontext_t *dcontext, fragment_t *f);

/* if this linkstub shares the stub with the next linkstub, returns the
 * next linkstub; else returns NULL
 */
linkstub_t *
linkstub_shares_next_stub(dcontext_t *dcontext, fragment_t *f, linkstub_t *l);

/* Returns the total size needed for stubs if info is frozen. */
size_t
coarse_frozen_stub_size(dcontext_t *dcontext, coarse_info_t *info, uint *num_fragments,
                        uint *num_stubs);

/* Removes any incoming data recording the outgoing link from stub */
void
coarse_remove_outgoing(dcontext_t *dcontext, cache_pc stub, coarse_info_t *src_info);

void
coarse_update_outgoing(dcontext_t *dcontext, cache_pc old_stub, cache_pc new_stub,
                       coarse_info_t *src_info, bool replace);

void
coarse_unit_shift_links(dcontext_t *dcontext, coarse_info_t *info);

/* Updates the info pointers embedded in the coarse_stub_areas vector */
void
coarse_stubs_set_info(coarse_info_t *info);

/* Sets the final used pc in a frozen stub region */
void
coarse_stubs_set_end_pc(coarse_info_t *info, byte *end_pc);

#endif /* _CORE_LINK_H_ */