Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dynamic: x86_64: Fall back to original patching strategy when target is not running #1750

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
879 changes: 787 additions & 92 deletions arch/x86_64/mcount-dynamic.c

Large diffs are not rendered by default.

36 changes: 32 additions & 4 deletions arch/x86_64/mcount-insn.c
Original file line number Diff line number Diff line change
Expand Up @@ -583,15 +583,29 @@ static bool check_unsupported(struct mcount_disasm_engine *disasm, cs_insn *insn
return true;
}

/**
* check_endbr64 - check if instruction at @addr is endbr64
* @addr - address to check for endbr64
* @return - 1 if found, 0 if not
*/
int check_endbr64(unsigned long addr)
{
uint8_t endbr64[] = { 0xf3, 0x0f, 0x1e, 0xfa };

return !memcmp((void *)addr, endbr64, sizeof(endbr64));
}

int disasm_check_insns(struct mcount_disasm_engine *disasm, struct mcount_dynamic_info *mdi,
struct mcount_disasm_info *info)
{
int status;
cs_insn *insn = NULL;
uint32_t count, i, size;
uint8_t endbr64[] = { 0xf3, 0x0f, 0x1e, 0xfa };
void *trampoline_addr;
uint8_t *operand;
struct dynamic_bad_symbol *badsym;
unsigned long addr = info->addr;
bool is_call, is_trampoline;

badsym = mcount_find_badsym(mdi, info->addr);
if (badsym != NULL) {
Expand All @@ -609,9 +623,9 @@ int disasm_check_insns(struct mcount_disasm_engine *disasm, struct mcount_dynami
return INSTRUMENT_SKIPPED;

size = info->sym->size;
if (!memcmp((void *)info->addr, endbr64, sizeof(endbr64))) {
addr += sizeof(endbr64);
size -= sizeof(endbr64);
if (check_endbr64(info->addr)) {
addr += ENDBR_INSN_SIZE;
size -= ENDBR_INSN_SIZE;

if (size <= CALL_INSN_SIZE)
return INSTRUMENT_SKIPPED;
Expand All @@ -623,6 +637,20 @@ int disasm_check_insns(struct mcount_disasm_engine *disasm, struct mcount_dynami
if (count == 0)
return INSTRUMENT_FAILED;

/* Check for pre-existing dynamic instrumentation
1. check if opcode is call
2. check operand is trampoline address */
operand = &((uint8_t *)info->addr)[1];
trampoline_addr = (void *)mdi->trampoline - (info->addr + CALL_INSN_SIZE);
is_call = ((uint8_t *)info->addr)[0] == 0xe8;
if (is_call) {
is_trampoline = !memcmp(operand, &trampoline_addr, CALL_INSN_SIZE - 1);
if (is_trampoline) {
pr_dbg2("skip dynamically patched func: %s\n", info->sym->name);
return INSTRUMENT_SKIPPED;
}
}

for (i = 0; i < count; i++) {
uint8_t insns_byte[32] = {
0,
Expand Down
9 changes: 8 additions & 1 deletion cmds/live.c
Original file line number Diff line number Diff line change
Expand Up @@ -361,7 +361,7 @@ static int forward_options(struct uftrace_opts *opts)
}

/* provide a pattern type for options that need it */
if (opts->filter || opts->caller || opts->trigger) {
if (opts->filter || opts->caller || opts->trigger || opts->patch) {
status = forward_option(sfd, capabilities, UFTRACE_AGENT_OPT_PATTERN,
&opts->patt_type, sizeof(opts->patt_type));
if (status < 0)
Expand Down Expand Up @@ -389,6 +389,13 @@ static int forward_options(struct uftrace_opts *opts)
goto close;
}

if (opts->patch) {
status = forward_option(sfd, capabilities, UFTRACE_AGENT_OPT_PATCH, opts->patch,
strlen(opts->patch) + 1);
if (status < 0)
goto close;
}

close:
status_close = agent_message_send(sfd, UFTRACE_MSG_AGENT_CLOSE, NULL, 0);
if (status_close == 0) {
Expand Down
82 changes: 42 additions & 40 deletions libmcount/dynamic.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ struct code_page {

static LIST_HEAD(code_pages);

/* contains out-of-line execution code (return address -> modified instructions ptr) */
static struct Hashmap *code_hmap;

/* minimum function size for dynamic update */
Expand Down Expand Up @@ -224,8 +225,15 @@ __weak int mcount_patch_func(struct mcount_dynamic_info *mdi, struct uftrace_sym
return -1;
}

__weak int mcount_unpatch_func(struct mcount_dynamic_info *mdi, struct uftrace_symbol *sym,
struct mcount_disasm_engine *disasm)
__weak void mcount_patch_normal_func_fini(void)
{
}

__weak void mcount_unpatch_normal_func_fini(void)
{
}

__weak int mcount_unpatch_func(struct mcount_dynamic_info *mdi, struct uftrace_symbol *sym)
{
return -1;
}
Expand Down Expand Up @@ -384,10 +392,17 @@ static bool match_pattern_module(char *pathname)
return ret;
}

static bool match_pattern_list(struct uftrace_mmap *map, char *soname, char *sym_name)
/**
* match_pattern_list - match a symbol name against a pattern list
* @map - memory map of the symbol
* @soname - name of the module
* @sym_name - name of the symbol
* @return - -1 if match negative, 1 if match positive, 0 if no match
*/
static int match_pattern_list(struct uftrace_mmap *map, char *soname, char *sym_name)
{
struct patt_list *pl;
bool ret = false;
int ret = 0;
char *libname = basename(map->libname);

list_for_each_entry(pl, &patterns, list) {
Expand All @@ -398,7 +413,7 @@ static bool match_pattern_list(struct uftrace_mmap *map, char *soname, char *sym
continue;

if (match_filter_pattern(&pl->patt, sym_name))
ret = pl->positive;
ret = pl->positive ? 1 : -1;
}

return ret;
Expand All @@ -410,7 +425,6 @@ static void parse_pattern_list(char *patch_funcs, char *def_mod, enum uftrace_pa
char *name;
int j;
struct patt_list *pl;
bool all_negative = true;

strv_split(&funcs, patch_funcs, ";");

Expand All @@ -421,10 +435,8 @@ static void parse_pattern_list(char *patch_funcs, char *def_mod, enum uftrace_pa

if (name[0] == '!')
name++;
else {
else
pl->positive = true;
all_negative = false;
}

delim = strchr(name, '@');
if (delim == NULL) {
Expand All @@ -439,20 +451,6 @@ static void parse_pattern_list(char *patch_funcs, char *def_mod, enum uftrace_pa
list_add_tail(&pl->list, &patterns);
}

/* prepend match-all pattern, if all patterns are negative */
if (all_negative) {
pl = xzalloc(sizeof(*pl));
pl->positive = true;
pl->module = xstrdup(def_mod);

if (ptype == PATT_REGEX)
init_filter_pattern(ptype, &pl->patt, ".");
else
init_filter_pattern(PATT_GLOB, &pl->patt, "*");

list_add(&pl->list, &patterns);
}

strv_free(&funcs);
}

Expand Down Expand Up @@ -487,12 +485,6 @@ static bool skip_sym(struct uftrace_symbol *sym, struct mcount_dynamic_info *mdi
if (sym->type != ST_LOCAL_FUNC && sym->type != ST_GLOBAL_FUNC && sym->type != ST_WEAK_FUNC)
return true;

if (!match_pattern_list(map, soname, sym->name)) {
if (mcount_unpatch_func(mdi, sym, &disasm) == 0)
stats.unpatch++;
return true;
}

return false;
}

Expand Down Expand Up @@ -559,6 +551,7 @@ static void patch_normal_func_matched(struct mcount_dynamic_info *mdi, struct uf
unsigned i;
struct uftrace_symbol *sym;
bool found = false;
int match;
char *soname = get_soname(map->libname);

symtab = &map->mod->symtab;
Expand All @@ -568,14 +561,23 @@ static void patch_normal_func_matched(struct mcount_dynamic_info *mdi, struct uf

if (skip_sym(sym, mdi, map, soname))
continue;

found = true;
mcount_patch_func_with_stats(mdi, sym);

match = match_pattern_list(map, soname, sym->name);
if (!match)
continue;
else if (match == 1)
mcount_patch_func_with_stats(mdi, sym);
else
mcount_unpatch_func(mdi, sym);
}

if (!found)
stats.nomatch++;

mcount_patch_normal_func_fini();
mcount_unpatch_normal_func_fini();

free(soname);
}

Expand Down Expand Up @@ -846,27 +848,27 @@ TEST_CASE(dynamic_pattern_list)
pr_dbg("check simple match with default module\n");
parse_pattern_list("abc;!def", "main", PATT_SIMPLE);

TEST_EQ(match_pattern_list(main_map, NULL, "abc"), true);
TEST_EQ(match_pattern_list(main_map, NULL, "def"), false);
TEST_EQ(match_pattern_list(other_map, NULL, "xyz"), false);
TEST_EQ(match_pattern_list(main_map, NULL, "abc"), 1);
TEST_EQ(match_pattern_list(main_map, NULL, "def"), -1);
TEST_EQ(match_pattern_list(other_map, NULL, "xyz"), 0);

release_pattern_list();

pr_dbg("check negative regex match with default module\n");
parse_pattern_list("!^a", "main", PATT_REGEX);

TEST_EQ(match_pattern_list(main_map, NULL, "abc"), false);
TEST_EQ(match_pattern_list(main_map, NULL, "def"), true);
TEST_EQ(match_pattern_list(other_map, NULL, "xyz"), false);
TEST_EQ(match_pattern_list(main_map, NULL, "abc"), -1);
TEST_EQ(match_pattern_list(main_map, NULL, "def"), 0);
TEST_EQ(match_pattern_list(other_map, NULL, "xyz"), 0);

release_pattern_list();

pr_dbg("check wildcard match with other module\n");
parse_pattern_list("*@other", "main", PATT_GLOB);

TEST_EQ(match_pattern_list(main_map, NULL, "abc"), false);
TEST_EQ(match_pattern_list(main_map, NULL, "def"), false);
TEST_EQ(match_pattern_list(other_map, NULL, "xyz"), true);
TEST_EQ(match_pattern_list(main_map, NULL, "abc"), 0);
TEST_EQ(match_pattern_list(main_map, NULL, "def"), 0);
TEST_EQ(match_pattern_list(other_map, NULL, "xyz"), 1);

release_pattern_list();

Expand Down
11 changes: 10 additions & 1 deletion libmcount/mcount.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ bool mcount_auto_recover = ARCH_SUPPORT_AUTO_RECOVER;
/* global flag to control mcount behavior */
unsigned long mcount_global_flags = MCOUNT_GFL_SETUP;

/* global to indicate if the target is executing */
bool mcount_target_running = false;

/* TSD key to save mtd below */
pthread_key_t mtd_key = (pthread_key_t)-1;

Expand Down Expand Up @@ -107,7 +110,7 @@ static volatile bool agent_run = false;
#define MCOUNT_AGENT_CAPABILITIES \
(UFTRACE_AGENT_OPT_TRACE | UFTRACE_AGENT_OPT_DEPTH | UFTRACE_AGENT_OPT_THRESHOLD | \
UFTRACE_AGENT_OPT_PATTERN | UFTRACE_AGENT_OPT_FILTER | UFTRACE_AGENT_OPT_CALLER | \
UFTRACE_AGENT_OPT_TRIGGER)
UFTRACE_AGENT_OPT_TRIGGER | UFTRACE_AGENT_OPT_PATCH)

__weak void dynamic_return(void)
{
Expand Down Expand Up @@ -1988,6 +1991,11 @@ static int agent_apply_option(int opt, void *value, size_t size,
agent_setup_trigger(value, triggers);
break;

case UFTRACE_AGENT_OPT_PATCH:
pr_dbg3("apply patch '%s' (size=%d)\n", value, size);
mcount_dynamic_update(&mcount_sym_info, value, mcount_filter_setting.ptype);
break;

default:
ret = -1;
}
Expand Down Expand Up @@ -2337,6 +2345,7 @@ static __used void mcount_startup(void)

mcount_global_flags &= ~MCOUNT_GFL_SETUP;
mtd.recursion_marker = false;
mcount_target_running = true;
}

static void mcount_cleanup(void)
Expand Down
1 change: 1 addition & 0 deletions uftrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,7 @@ enum uftrace_agent_opt {
UFTRACE_AGENT_OPT_FILTER = (1U << 4), /* tracing filters */
UFTRACE_AGENT_OPT_CALLER = (1U << 5), /* tracing caller filters */
UFTRACE_AGENT_OPT_TRIGGER = (1U << 6), /* tracing trigger actions */
UFTRACE_AGENT_OPT_PATCH = (1U << 7), /* patch string */
};

extern struct uftrace_session *first_session;
Expand Down
Loading