Skip to content

Commit

Permalink
Implemented a new "dis -s" option, which displays the filename and
Browse files Browse the repository at this point in the history
line number that is associated with a specified text location,
followed by a source code listing if it is available on the host
machine. The line associated with the text location will be marked
with an asterisk; depending upon gdb's internal "listsize" variable,
several lines will precede the marked location. If a "count" argument
is entered, it specifies the number of source code lines to be
displayed after the marked location; otherwise the remaining source
code of the containing function will be displayed.
(anderson@redhat.com)
  • Loading branch information
Dave Anderson committed Sep 1, 2015
1 parent c4bb18f commit b80d712
Show file tree
Hide file tree
Showing 2 changed files with 252 additions and 9 deletions.
68 changes: 67 additions & 1 deletion help.c
Original file line number Diff line number Diff line change
Expand Up @@ -6147,7 +6147,7 @@ NULL
char *help_dis[] = {
"dis",
"disassemble",
"[-rfludx][-b [num]] [address | symbol | (expression)] [count]",
"[-rfludxs][-b [num]] [address | symbol | (expression)] [count]",
" This command disassembles source code instructions starting (or ending) at",
" a text address that may be expressed by value, symbol or expression:\n",
" -r (reverse) displays all instructions from the start of the ",
Expand All @@ -6161,6 +6161,16 @@ char *help_dis[] = {
" If this option is used, then -r and -l are ignored.",
" -x override default output format with hexadecimal format.",
" -d override default output format with decimal format.",
" -s displays the filename and line number of the source code that",
" is associated with the specified text location, followed by a",
" source code listing if it is available on the host machine.",
" The line associated with the text location will be marked with",
" an asterisk; depending upon gdb's internal \"listsize\" variable,",
" several lines will precede the marked location. If a \"count\"",
" argument is entered, it specifies the number of source code",
" lines to be displayed after the marked location; otherwise",
" the remaining source code of the containing function will be",
" displayed.",
" -b [num] modify the pre-calculated number of encoded bytes to skip after",
" a kernel BUG (\"ud2a\") instruction; with no argument, displays",
" the current number of bytes being skipped. (x86 and x86_64 only)",
Expand Down Expand Up @@ -6292,6 +6302,62 @@ char *help_dis[] = {
" 0xffffffff8119d511 <vfs_read+369>: mov $0xfffffffffffffff2,%r12",
" 0xffffffff8119d518 <vfs_read+376>: jmp 0xffffffff8119d4c3 <vfs_read+291>",
" 0xffffffff8119d51a <vfs_read+378>: nopw 0x0(%rax,%rax,1)",
" ",
" Display the source code listing of the mmput() function:\n",
" %s> dis -s mmput",
" FILE: kernel/fork.c",
" LINE: 617",
" ",
" 612 ",
" 613 /*",
" 614 * Decrement the use count and release all resources for an mm.",
" 615 */",
" 616 void mmput(struct mm_struct *mm)",
" * 617 {",
" 618 might_sleep();",
" 619 ",
" 620 if (atomic_dec_and_test(&mm->mm_users)) {",
" 621 uprobe_clear_state(mm);",
" 622 exit_aio(mm);",
" 623 ksm_exit(mm);",
" 624 khugepaged_exit(mm); /* must run before exit_mmap */",
" 625 exit_mmap(mm);",
" 626 set_mm_exe_file(mm, NULL);",
" 627 if (!list_empty(&mm->mmlist)) {",
" 628 spin_lock(&mmlist_lock);",
" 629 list_del(&mm->mmlist);",
" 630 spin_unlock(&mmlist_lock);",
" 631 }",
" 632 if (mm->binfmt)",
" 633 module_put(mm->binfmt->module);",
" 634 mmdrop(mm);",
" 635 }",
" 636 }",
" ",
" The disassembly of dentry_kill() shows an indirect call to a function",
" whose address is contained within a register. Display the source code",
" associated with the indirect function call:\n",
" %s> dis dentry_kill",
" ...",
" 0xffffffff811dcfb4 <dentry_kill+324>: callq *%rax",
" ...",
" %s> dis -s 0xffffffff811dcfb4",
" FILE: fs/dcache.c",
" LINE: 276",
" ",
" 271 spin_unlock(&dentry->d_lock);",
" 272 spin_unlock(&inode->i_lock);",
" 273 if (!inode->i_nlink)",
" 274 fsnotify_inoderemove(inode);",
" 275 if (dentry->d_op && dentry->d_op->d_iput)",
" * 276 dentry->d_op->d_iput(dentry, inode);",
" 277 else",
" 278 iput(inode);",
" 279 } else {",
" 280 spin_unlock(&dentry->d_lock);",
" 281 }",
" 282 }",
" ",
NULL
};

Expand Down
193 changes: 185 additions & 8 deletions kernel.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ static void dump_log_legacy(void);
static void dump_variable_length_record(void);
static int is_livepatch(void);
static void show_kernel_taints(char *, int);
static void list_source_code(struct gnu_request *, int);


/*
Expand Down Expand Up @@ -1317,6 +1318,155 @@ verify_namelist()
program_usage(SHORT_FORM);
}


static void
list_source_code(struct gnu_request *req, int count_entered)
{
int argc, line, last, done, assembly;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
char file[BUFSIZE];
char *argv[MAXARGS];
struct syment *sp;
ulong remaining, offset;
char *p1;

sp = value_search(req->addr, &offset);
if (!sp || !is_symbol_text(sp))
error(FATAL, "%lx: not a kernel text address\n", req->addr);

sprintf(buf1, "list *0x%lx", req->addr);
open_tmpfile();
if (!gdb_pass_through(buf1, pc->tmpfile, GNU_RETURN_ON_ERROR)) {
close_tmpfile();
error(FATAL, "gdb command failed: %s\n", buf1);
}

done = FALSE;
last = line = assembly = file[0] = 0;
remaining = count_entered ? req->count : 0;

rewind(pc->tmpfile);
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
strcpy(buf2, buf1);
argc = parse_line(buf2, argv);
if (!line && hexadecimal(argv[0], 0) &&
STREQ(argv[1], "is") &&
(STREQ(argv[2], "in") || STREQ(argv[2], "at"))) {
/*
* Don't bother continuing beyond the initial
* list command if it's assembly language.
*/
if (STREQ(argv[2], "at"))
assembly = TRUE;

strip_beginning_char(argv[argc-1], '(');
strip_ending_char(argv[argc-1], '.');
strip_ending_char(argv[argc-1], ')');
p1 = strstr_rightmost(argv[argc-1], ":");
*p1 = NULLCHAR;
strcpy(file, argv[argc-1]);
line = atoi(p1+1);

fprintf(pc->saved_fp, "FILE: %s\nLINE: %d\n\n", file, line);

continue;
}

/*
* Check for 2 possible results of unavailable source.
*/
if ((argc == 3) &&
decimal(argv[0], 0) &&
STREQ(argv[1], "in") &&
STREQ(argv[2], file))
error(FATAL,
"%s: source code is not available\n\n", req->buf);

sprintf(buf3, "%s: No such file or directory.", file);
if (decimal(argv[0], 0) && strstr(buf1, buf3))
error(FATAL,
"%s: source code is not available\n\n", req->buf);

if (decimal(argv[0], 0)) {
if (count_entered && (last >= line)) {
if (!remaining--) {
done = TRUE;
break;
}
}
last = atoi(argv[0]);
fprintf(pc->saved_fp, "%s%s",
last == line ? "* " : " ", buf1);
} else
continue;

if (!count_entered && (last > line) &&
STREQ(first_space(buf1), "\t}\n")) {
done = TRUE;
break;
}
}
close_tmpfile();

if (!line) {
fprintf(fp, "FILE: (unknown)\nLINE: (unknown)\n\n");
error(FATAL, "%s: source code is not available\n\n", req->buf);
}

if ((count_entered && !remaining) || (!count_entered && assembly)) {
fprintf(fp, "\n");
return;
}

/*
* If the end of the containing function or a specified count
* has not been reached, continue the listing until it has.
*/
while (!done) {
open_tmpfile();
if (!gdb_pass_through("list", fp, GNU_RETURN_ON_ERROR)) {
close_tmpfile();
return;
}
rewind(pc->tmpfile);
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
strcpy(buf2, buf1);
argc = parse_line(buf2, argv);

if (decimal(argv[0], 0))
line = atoi(argv[0]);
else
continue;

if (count_entered) {
if (!remaining--) {
done = TRUE;
break;
}
}

if (line == last) {
done = TRUE;
break;
}
last = line;

fprintf(pc->saved_fp, " %s", buf1);

if (!count_entered &&
STREQ(first_space(buf1), "\t}\n")) {
done = TRUE;
break;
}
}
close_tmpfile();
}

fprintf(fp, "\n");
}

/*
* From either a syment pointer, or a virtual address evaluated
* from a symbol name plus an offset value, determine whether
Expand Down Expand Up @@ -1439,7 +1589,7 @@ cmd_dis(void)
{
int c;
int do_load_module_filter, do_machdep_filter, reverse, forward;
int unfiltered, user_mode, count_entered, bug_bytes_entered;
int unfiltered, user_mode, count_entered, bug_bytes_entered, sources;
unsigned int radix;
ulong curaddr;
ulong target;
Expand All @@ -1465,18 +1615,17 @@ cmd_dis(void)
return;
}

reverse = forward = count_entered = bug_bytes_entered = FALSE;
reverse = forward = count_entered = bug_bytes_entered = sources = FALSE;
sp = NULL;
unfiltered = user_mode = do_machdep_filter = do_load_module_filter = 0;
radix = 0;
target = 0;

req = (struct gnu_request *)GETBUF(sizeof(struct gnu_request));
req->buf = GETBUF(BUFSIZE);
req->flags |= GNU_FROM_TTY_OFF|GNU_RETURN_ON_ERROR;
req->count = 1;

while ((c = getopt(argcnt, args, "dxhulrfUb:B:")) != EOF) {
while ((c = getopt(argcnt, args, "dxhulsrfUb:B:")) != EOF) {
switch(c)
{
case 'd':
Expand All @@ -1499,20 +1648,29 @@ cmd_dis(void)
break;

case 'u':
if (sources)
error(FATAL,
"-s can only be used with kernel addresses\n");
user_mode = TRUE;
break;

case 'r':
if (forward)
error(FATAL,
"-r and -f are mutually exclusive\n");
if (sources)
error(FATAL,
"-r and -s are mutually exclusive\n");
reverse = TRUE;
break;

case 'f':
if (reverse)
error(FATAL,
"-r and -f are mutually exclusive\n");
if (sources)
error(FATAL,
"-f and -s are mutually exclusive\n");
forward = TRUE;
break;

Expand All @@ -1524,6 +1682,21 @@ cmd_dis(void)
BZERO(buf4, BUFSIZE);
break;

case 's':
if (reverse)
error(FATAL,
"-r and -s are mutually exclusive\n");
if (forward)
error(FATAL,
"-f and -s are mutually exclusive\n");
if (user_mode)
error(FATAL,
"-s can only be used with kernel addresses\n");
if (NO_LINE_NUMBERS())
error(INFO, "line numbers are not available\n");
sources = TRUE;
break;

case 'B':
case 'b':
kt->BUG_bytes = atoi(optarg);
Expand All @@ -1544,14 +1717,15 @@ cmd_dis(void)

if (args[optind]) {
if (can_eval(args[optind])) {
req->buf = args[optind];
req->addr = eval(args[optind], FAULT_ON_ERROR, NULL);
if (!user_mode &&
!resolve_text_symbol(args[optind], NULL, req, radix)) {
FREEBUF(req->buf);
FREEBUF(req);
return;
}
} else if (hexadecimal(args[optind], 0)) {
req->buf = args[optind];
req->addr = htol(args[optind], FAULT_ON_ERROR, NULL);
sp = value_search(req->addr, &offset);
if (!user_mode && !sp) {
Expand All @@ -1563,9 +1737,9 @@ cmd_dis(void)
if (!offset && sp && is_symbol_text(sp))
req->flags |= GNU_FUNCTION_ONLY;
} else if ((sp = symbol_search(args[optind]))) {
req->buf = args[optind];
req->addr = sp->value;
if (!resolve_text_symbol(args[optind], sp, req, radix)) {
FREEBUF(req->buf);
FREEBUF(req);
return;
}
Expand All @@ -1576,7 +1750,6 @@ cmd_dis(void)
fprintf(fp, "possible alternatives:\n");
if (!symbol_query(args[optind], " ", NULL))
fprintf(fp, " (none found)\n");
FREEBUF(req->buf);
FREEBUF(req);
return;
}
Expand All @@ -1594,6 +1767,11 @@ cmd_dis(void)
}
}

if (sources) {
list_source_code(req, count_entered);
return;
}

if (unfiltered) {
sprintf(buf1, "x/%ldi 0x%lx",
req->count ? req->count : 1, req->addr);
Expand Down Expand Up @@ -1735,7 +1913,6 @@ cmd_dis(void)
return;
else cmd_usage(pc->curcmd, SYNOPSIS);

FREEBUF(req->buf);
FREEBUF(req);
return;
}
Expand Down

0 comments on commit b80d712

Please sign in to comment.