diff options
Diffstat (limited to 'binutils/objdump.c')
-rw-r--r-- | binutils/objdump.c | 244 |
1 files changed, 149 insertions, 95 deletions
diff --git a/binutils/objdump.c b/binutils/objdump.c index a8cad9b..e9fee6e 100644 --- a/binutils/objdump.c +++ b/binutils/objdump.c @@ -61,6 +61,10 @@ #include "debug.h" #include "budbg.h" +#ifdef HAVE_MMAP +#include <sys/mman.h> +#endif + /* Internal headers for the ELF .stab-dump code - sorry. */ #define BYTES_IN_WORD 32 #include "aout/aout64.h" @@ -927,8 +931,12 @@ struct print_file_list struct print_file_list *next; const char *filename; const char *modname; - unsigned int line; - FILE *f; + const char *map; + size_t mapsize; + const char **linemap; + unsigned maxline; + unsigned last_line; + int first; }; static struct print_file_list *print_files; @@ -938,6 +946,99 @@ static struct print_file_list *print_files; #define SHOW_PRECEDING_CONTEXT_LINES (5) +/* Read a complete file into memory. */ + +static const char * +slurp_file (const char *fn, size_t *size) +{ +#ifdef HAVE_MMAP + int ps = getpagesize (); + size_t msize; +#endif + const char *map; + struct stat st; + int fd = open (fn, O_RDONLY); + + if (fd < 0) + return NULL; + if (fstat (fd, &st) < 0) + return NULL; + *size = st.st_size; +#ifdef HAVE_MMAP + msize = (*size + ps - 1) & ~(ps - 1); + map = mmap (NULL, msize, PROT_READ, MAP_SHARED, fd, 0); + if (map != (char *)-1L) + { + close(fd); + return map; + } +#endif + map = malloc (*size); + if (!map || (size_t) read (fd, (char *)map, *size) != *size) + { + free ((void *)map); + map = NULL; + } + close (fd); + return map; +} + +#define line_map_decrease 5 + +/* Precompute array of lines for a mapped file. */ + +static const char ** +index_file (const char *map, size_t size, unsigned int *maxline) +{ + const char *p, *lstart, *end; + int chars_per_line = 45; /* First iteration will use 40. */ + unsigned int lineno; + const char **linemap = NULL; + unsigned long line_map_size = 0; + + lineno = 0; + lstart = map; + end = map + size; + + for (p = map; p < end; p++) + { + if (*p == '\n') + { + if (p + 1 < end && p[1] == '\r') + p++; + } + else if (*p == '\r') + { + if (p + 1 < end && p[1] == '\n') + p++; + } + else + continue; + + /* End of line found. */ + + if (linemap == NULL || line_map_size < lineno + 1) + { + unsigned long newsize; + + chars_per_line -= line_map_decrease; + if (chars_per_line <= 1) + chars_per_line = 1; + line_map_size = size / chars_per_line + 1; + if (line_map_size < lineno + 1) + line_map_size = lineno + 1; + newsize = line_map_size * sizeof (char *); + linemap = xrealloc (linemap, newsize); + } + + linemap[lineno++] = lstart; + lstart = p + 1; + } + + *maxline = lineno; + return linemap; +} + /* Tries to open MODNAME, and if successful adds a node to print_files linked list and returns that node. Returns NULL on failure. */ @@ -945,24 +1046,22 @@ static struct print_file_list * try_print_file_open (const char *origname, const char *modname) { struct print_file_list *p; - FILE *f; - f = fopen (modname, "r"); - if (f == NULL) - return NULL; + p = xmalloc (sizeof (struct print_file_list)); - if (print_files != NULL && print_files->f != NULL) + p->map = slurp_file (modname, &p->mapsize); + if (p->map == NULL) { - fclose (print_files->f); - print_files->f = NULL; + free (p); + return NULL; } - - p = xmalloc (sizeof (struct print_file_list)); + + p->linemap = index_file (p->map, p->mapsize, &p->maxline); + p->last_line = 0; p->filename = origname; p->modname = modname; - p->line = 0; - p->f = f; p->next = print_files; + p->first = 1; print_files = p; return p; } @@ -1021,29 +1120,32 @@ update_source_path (const char *filename) return NULL; } -/* Skip ahead to a given line in a file, optionally printing each - line. */ +/* Print a source file line. */ -static void -skip_to_line (struct print_file_list *p, unsigned int line, - bfd_boolean show) +static void +print_line (struct print_file_list *p, unsigned int line) { - while (p->line < line) - { - char buf[100]; - - if (fgets (buf, sizeof buf, p->f) == NULL) - { - fclose (p->f); - p->f = NULL; - break; - } + const char *l; + + --line; + if (line >= p->maxline) + return; + l = p->linemap [line]; + fwrite (l, 1, strcspn (l, "\n\r"), stdout); + putchar ('\n'); +} - if (show) - printf ("%s", buf); +/* Print a range of source code lines. */ - if (strchr (buf, '\n') != NULL) - ++p->line; +static void +dump_lines (struct print_file_list *p, unsigned int start, unsigned int end) +{ + if (p->map == NULL) + return; + while (start <= end) + { + print_line (p, start); + start++; } } @@ -1084,79 +1186,31 @@ show_line (bfd *abfd, asection *section, bfd_vma addr_offset) && line > 0) { struct print_file_list **pp, *p; + unsigned l; for (pp = &print_files; *pp != NULL; pp = &(*pp)->next) if (strcmp ((*pp)->filename, filename) == 0) break; p = *pp; - if (p != NULL) - { - if (p != print_files) - { - int l; - - /* We have reencountered a file name which we saw - earlier. This implies that either we are dumping out - code from an included file, or the same file was - linked in more than once. There are two common cases - of an included file: inline functions in a header - file, and a bison or flex skeleton file. In the - former case we want to just start printing (but we - back up a few lines to give context); in the latter - case we want to continue from where we left off. I - can't think of a good way to distinguish the cases, - so I used a heuristic based on the file name. */ - if (strcmp (p->filename + strlen (p->filename) - 2, ".h") != 0) - l = p->line; - else - { - l = line - SHOW_PRECEDING_CONTEXT_LINES; - if (l < 0) - l = 0; - } - - if (p->f == NULL) - { - p->f = fopen (p->modname, "r"); - p->line = 0; - } - if (p->f != NULL) - skip_to_line (p, l, FALSE); - - if (print_files->f != NULL) - { - fclose (print_files->f); - print_files->f = NULL; - } - } - - if (p->f != NULL) - { - skip_to_line (p, line, TRUE); - *pp = p->next; - p->next = print_files; - print_files = p; - } - } - else - { + if (p == NULL) p = update_source_path (filename); - if (p != NULL) + if (p != NULL && line != p->last_line) + { + if (file_start_context && p->first) + l = 1; + else { - int l; - - if (file_start_context) - l = 0; - else - l = line - SHOW_PRECEDING_CONTEXT_LINES; - if (l < 0) - l = 0; - skip_to_line (p, l, FALSE); - if (p->f != NULL) - skip_to_line (p, line, TRUE); + l = line - SHOW_PRECEDING_CONTEXT_LINES; + if (l >= line) + l = 1; + if (p->last_line >= l && p->last_line <= line) + l = p->last_line + 1; } + dump_lines (p, l, line); + p->last_line = line; + p->first = 0; } } |