/* listing.c - mainting assembly listings
- Copyright (C) 1991, 1992 Free Software Foundation, Inc.
+ Copyright (C) 1991, 92, 93, 94, 95, 96, 1997 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GAS; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+along with GAS; see the file COPYING. If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA. */
/*
Contributed by Steve Chamberlain
#include "subsegs.h"
#ifndef NO_LISTING
+
#ifndef LISTING_HEADER
#define LISTING_HEADER "GAS LISTING"
#endif
#define LISTING_LHS_CONT_LINES 4
#endif
-
-
-
/* This structure remembers which .s were used */
typedef struct file_info_struct
{
+ struct file_info_struct *next;
char *filename;
+ long pos;
int linenum;
- FILE *file;
- struct file_info_struct *next;
int at_end;
}
-
file_info_type;
/* this structure rememebrs which line from which file goes into which
frag */
-typedef struct list_info_struct
+struct list_info_struct
{
/* Frag which this line of source is nearest to */
fragS *frag;
+
/* The actual line in the source file */
unsigned int line;
/* Pointer to the file info struct for the file which this line
belongs to */
file_info_type *file;
+ /* The expanded text of any macro that may have been executing. */
+ char *line_contents;
+
/* Next in list */
struct list_info_struct *next;
-
/* Pointer to the file info struct for the high level language
source line that belongs here */
file_info_type *hll_file;
-
/* High level language source line */
int hll_line;
-
/* Pointer to any error message associated with this line */
char *message;
EDICT_TITLE,
EDICT_NOLIST,
EDICT_LIST,
+ EDICT_NOLIST_NEXT,
EDICT_EJECT
} edict;
char *edict_arg;
-}
-
-list_info_type;
+ /* Nonzero if this line is to be omitted because it contains
+ debugging information. This can become a flags field if we come
+ up with more information to store here. */
+ int debugging;
+};
+typedef struct list_info_struct list_info_type;
static struct list_info_struct *head;
struct list_info_struct *listing_tail;
/* File to output listings to. */
static FILE *list_file;
-/* this static array is used to keep the text of data to be printed
- before the start of the line.
- It is stored so we can give a bit more info on the next line. To much, and large
- initialized arrays will use up lots of paper.
- */
+/* This static array is used to keep the text of data to be printed
+ before the start of the line. */
-static char data_buffer[100];
-static unsigned int data_buffer_size;
+#define MAX_BYTES \
+ (((LISTING_WORD_SIZE * 2) + 1) * LISTING_LHS_WIDTH \
+ + ((((LISTING_WORD_SIZE * 2) + 1) * LISTING_LHS_WIDTH_SECOND) \
+ * LISTING_LHS_CONT_LINES) \
+ + 20)
+static char data_buffer[MAX_BYTES];
/* Prototypes. */
static void listing_message PARAMS ((const char *name, const char *message));
char *line, unsigned int size));
static void listing_page PARAMS ((list_info_type *list));
static unsigned int calc_hex PARAMS ((list_info_type *list));
-static void print_lines PARAMS ((list_info_type *list,
- char *string,
- unsigned int address));
+static void print_lines PARAMS ((list_info_type *, unsigned int,
+ char *, unsigned int));
static void list_symbol_table PARAMS ((void));
static void print_source PARAMS ((file_info_type *current_file,
list_info_type *list,
char *buffer,
unsigned int width));
-static int debugging_pseudo PARAMS ((char *line));
+static int debugging_pseudo PARAMS ((list_info_type *, const char *));
static void listing_listing PARAMS ((char *name));
}
+int listing_lhs_width = LISTING_LHS_WIDTH;
+int listing_lhs_width_second = LISTING_LHS_WIDTH_SECOND;
+int listing_lhs_cont_lines = LISTING_LHS_CONT_LINES;
+int listing_rhs_width = LISTING_RHS_WIDTH;
static file_info_type *file_info_head;
+static file_info_type *last_open_file_info;
+static FILE *last_open_file;
static file_info_type *
file_info (file_name)
file_info_head = p;
p->filename = xmalloc ((unsigned long) strlen (file_name) + 1);
strcpy (p->filename, file_name);
+ p->pos = 0;
p->linenum = 0;
p->at_end = 0;
- p->file = fopen (p->filename, "r");
- if (p->file)
- fgetc (p->file);
-
return p;
}
unsigned int line;
static unsigned int last_line = 0xffff;
static char *last_file = NULL;
- list_info_type *new;
+ list_info_type *new = NULL;
if (listing == 0)
return;
if (now_seg == absolute_section)
return;
+#ifdef OBJ_ELF
+ /* In ELF, anything in a section beginning with .debug or .line is
+ considered to be debugging information. This includes the
+ statement which switches us into the debugging section, which we
+ can only set after we are already in the debugging section. */
+ if ((listing & LISTING_NODEBUG) != 0
+ && listing_tail != NULL
+ && ! listing_tail->debugging)
+ {
+ const char *segname;
+
+ segname = segment_name (now_seg);
+ if (strncmp (segname, ".debug", sizeof ".debug" - 1) == 0
+ || strncmp (segname, ".line", sizeof ".line" - 1) == 0)
+ listing_tail->debugging = 1;
+ }
+#endif
+
as_where (&file, &line);
- if (line != last_line || (last_file && file && strcmp(file, last_file)))
+ if (ps == NULL)
{
- last_line = line;
- last_file = file;
- new_frag ();
+ if (line == last_line && !(last_file && file && strcmp(file, last_file)))
+ return;
new = (list_info_type *) xmalloc (sizeof (list_info_type));
- new->frag = frag_now;
- new->line = line;
- new->file = file_info (file);
+ new->line_contents = NULL;
+ }
+ else
+ {
+ new = (list_info_type *) xmalloc (sizeof (list_info_type));
+ new->line_contents = ps;
+ }
- if (listing_tail)
- {
- listing_tail->next = new;
- }
- else
- {
- head = new;
- }
- listing_tail = new;
- new->next = (list_info_type *) NULL;
- new->message = (char *) NULL;
- new->edict = EDICT_NONE;
- new->hll_file = (file_info_type *) NULL;
- new->hll_line = 0;
- new_frag ();
+ last_line = line;
+ last_file = file;
+ new_frag ();
+
+ if (listing_tail)
+ {
+ listing_tail->next = new;
+ }
+ else
+ {
+ head = new;
}
+ listing_tail = new;
+
+ new->frag = frag_now;
+ new->line = line;
+ new->file = file_info (file);
+ new->next = (list_info_type *) NULL;
+ new->message = (char *) NULL;
+ new->edict = EDICT_NONE;
+ new->hll_file = (file_info_type *) NULL;
+ new->hll_line = 0;
+ new->debugging = 0;
+ new_frag ();
+
+#ifdef OBJ_ELF
+ /* In ELF, anything in a section beginning with .debug or .line is
+ considered to be debugging information. */
+ if ((listing & LISTING_NODEBUG) != 0)
+ {
+ const char *segname;
+
+ segname = segment_name (now_seg);
+ if (strncmp (segname, ".debug", sizeof ".debug" - 1) == 0
+ || strncmp (segname, ".line", sizeof ".line" - 1) == 0)
+ new->debugging = 1;
+ }
+#endif
}
/* Attach all current frags to the previous line instead of the
char *p = line;
/* If we couldn't open the file, return an empty line */
- if (file->file == (FILE *) NULL || file->at_end)
+ if (file->at_end)
+ return "";
+
+ /* Check the cache and see if we last used this file. */
+ if (!last_open_file_info || file != last_open_file_info)
{
- return "";
- }
+ if (last_open_file)
+ {
+ last_open_file_info->pos = ftell (last_open_file);
+ fclose (last_open_file);
+ }
- if (file->linenum == 0)
- rewind (file->file);
+ last_open_file_info = file;
+ last_open_file = fopen (file->filename, "r");
+ if (!last_open_file)
+ return "";
- c = fgetc (file->file);
+ /* Seek to where we were last time this file was open. */
+ if (file->pos)
+ fseek(last_open_file, file->pos, SEEK_SET);
+ }
+
+ c = fgetc (last_open_file);
size -= 1; /* leave room for null */
*p++ = c;
count++;
- c = fgetc (file->file);
+ c = fgetc (last_open_file);
}
if (c == EOF)
calc_hex (list)
list_info_type * list;
{
+ int data_buffer_size;
list_info_type *first = list;
unsigned int address = (unsigned int) ~0;
-
fragS *frag;
fragS *frag_ptr;
-
unsigned int byte_in_frag;
-
/* Find first frag which says it belongs to this line */
frag = list->frag;
while (frag && frag->line != list)
{
/* Print as many bytes from the fixed part as is sensible */
byte_in_frag = 0;
- while (byte_in_frag < frag_ptr->fr_fix && data_buffer_size < sizeof (data_buffer) - 10)
+ while (byte_in_frag < frag_ptr->fr_fix
+ && data_buffer_size < sizeof (data_buffer) - 3)
{
if (address == ~0)
{
/* Print as many bytes from the variable part as is sensible */
while ((byte_in_frag
< frag_ptr->fr_fix + frag_ptr->fr_var * frag_ptr->fr_offset)
- && data_buffer_size < sizeof (data_buffer) - 10)
+ && data_buffer_size < sizeof (data_buffer) - 3)
{
if (address == ~0)
{
frag_ptr = frag_ptr->fr_next;
}
- data_buffer[data_buffer_size++] = 0;
+ data_buffer[data_buffer_size] = '\0';
return address;
}
static void
-print_lines (list, string, address)
+print_lines (list, lineno, string, address)
list_info_type *list;
+ unsigned int lineno;
char *string;
unsigned int address;
{
/* Print the stuff on the first line */
listing_page (list);
- nchars = (LISTING_WORD_SIZE * 2 + 1) * LISTING_LHS_WIDTH;
+ nchars = (LISTING_WORD_SIZE * 2 + 1) * listing_lhs_width;
/* Print the hex for the first line */
if (address == ~0)
{
- fprintf (list_file, "% 4d ", list->line);
+ fprintf (list_file, "% 4d ", lineno);
for (idx = 0; idx < nchars; idx++)
fprintf (list_file, " ");
{
if (had_errors ())
{
- fprintf (list_file, "% 4d ???? ", list->line);
+ fprintf (list_file, "% 4d ???? ", lineno);
}
else
{
- fprintf (list_file, "% 4d %04x ", list->line, address);
+ fprintf (list_file, "% 4d %04x ", lineno, address);
}
/* And the data to go along with it */
on_page++;
}
- for (lines = 0;
- lines < LISTING_LHS_CONT_LINES
- && *src;
- lines++)
+ for (lines = 0; lines < listing_lhs_cont_lines && *src; lines++)
{
- nchars = ((LISTING_WORD_SIZE * 2) + 1) * LISTING_LHS_WIDTH_SECOND - 1;
+ nchars = (((LISTING_WORD_SIZE * 2) + 1)
+ * listing_lhs_width_second - 1);
idx = 0;
/* Print any more lines of data, but more compactly */
- fprintf (list_file, "% 4d ", list->line);
+ fprintf (list_file, "% 4d ", lineno);
while (*src && idx < nchars)
{
}
#if defined (BFD64)
else if (sizeof (val) > 4)
- {
- char buf1[30];
- sprintf_vma (buf1, val);
- strcpy (buf, "00000000");
- strcpy (buf + 8 - strlen (buf1), buf1);
- }
+ sprintf_vma (buf, val);
#endif
else
abort ();
char *buffer;
unsigned int width;
{
- if (current_file->file)
+ if (!current_file->at_end)
{
while (current_file->linenum < list->hll_line
&& !current_file->at_end)
}
/* Sometimes the user doesn't want to be bothered by the debugging
- records inserted by the compiler, see if the line is suspicious */
+ records inserted by the compiler, see if the line is suspicious. */
static int
-debugging_pseudo (line)
- char *line;
+debugging_pseudo (list, line)
+ list_info_type *list;
+ const char *line;
{
+ static int in_debug;
+ int was_debug;
+
+ if (list->debugging)
+ {
+ in_debug = 1;
+ return 1;
+ }
+
+ was_debug = in_debug;
+ in_debug = 0;
+
while (isspace (*line))
line++;
if (*line != '.')
- return 0;
+ {
+#ifdef OBJ_ELF
+ /* The ELF compiler sometimes emits blank lines after switching
+ out of a debugging section. If the next line drops us back
+ into debugging information, then don't print the blank line.
+ This is a hack for a particular compiler behaviour, not a
+ general case. */
+ if (was_debug
+ && *line == '\0'
+ && list->next != NULL
+ && list->next->debugging)
+ {
+ in_debug = 1;
+ return 1;
+ }
+#endif
+
+ return 0;
+ }
line++;
return 1;
return 0;
-
}
static void
int show_listing = 1;
unsigned int width;
- buffer = xmalloc (LISTING_RHS_WIDTH);
+ buffer = xmalloc (listing_rhs_width);
eject = 1;
list = head;
while (list)
{
- width = LISTING_RHS_WIDTH > paper_width ? paper_width :
- LISTING_RHS_WIDTH;
+ width = listing_rhs_width > paper_width ? paper_width :
+ listing_rhs_width;
switch (list->edict)
{
case EDICT_NOLIST:
show_listing--;
break;
+ case EDICT_NOLIST_NEXT:
+ break;
case EDICT_EJECT:
break;
case EDICT_NONE:
current_hll_file = list->hll_file;
}
- if (current_hll_file && list->hll_line && listing & LISTING_HLL)
+ if (current_hll_file && list->hll_line && (listing & LISTING_HLL))
{
print_source (current_hll_file, list, buffer, width);
}
- while (list->file->file
- && list->file->linenum < list->line
- && !list->file->at_end)
+ if (list->line_contents)
{
- p = buffer_line (list->file, buffer, width);
-
- if (!((listing & LISTING_NODEBUG) && debugging_pseudo (p)))
+ if (!((listing & LISTING_NODEBUG)
+ && debugging_pseudo (list, list->line_contents)))
{
- print_lines (list, p, calc_hex (list));
+ print_lines (list, list->file->linenum,
+ list->line_contents, calc_hex (list));
+ }
+ }
+ else
+ {
+ while (list->file->linenum < list->line
+ && !list->file->at_end)
+ {
+ unsigned int address;
+
+ p = buffer_line (list->file, buffer, width);
+
+ if (list->file->linenum < list->line)
+ address = ~ (unsigned int) 0;
+ else
+ address = calc_hex (list);
+
+ if (!((listing & LISTING_NODEBUG)
+ && debugging_pseudo (list, p)))
+ print_lines (list, list->file->linenum, p, address);
}
}
}
else
{
- while (list->file->file
- && list->file->linenum < list->line
+ while (list->file->linenum < list->line
&& !list->file->at_end)
p = buffer_line (list->file, buffer, width);
}
+ if (list->edict == EDICT_NOLIST_NEXT)
+ --show_listing;
+
list = list->next;
}
free (buffer);
listing_print (name)
char *name;
{
+ int using_stdout;
+ file_info_type *fi;
+
title = "";
subtitle = "";
if (name == NULL)
- list_file = stdout;
+ {
+ list_file = stdout;
+ using_stdout = 1;
+ }
else
{
list_file = fopen (name, "w");
- if (list_file == NULL)
+ if (list_file != NULL)
+ using_stdout = 0;
+ else
{
as_perror ("can't open list file: %s", name);
list_file = stdout;
+ using_stdout = 1;
}
}
if (listing & LISTING_LISTING)
{
listing_listing (name);
-
}
+
if (listing & LISTING_SYMBOLS)
{
list_symbol_table ();
}
+
+ if (! using_stdout)
+ {
+ if (fclose (list_file) == EOF)
+ as_perror ("error closing list file: %s", name);
+ }
+
+ if (last_open_file)
+ {
+ fclose (last_open_file);
+ }
}
}
+/* Turn listing on or off. An argument of 0 means to turn off
+ listing. An argument of 1 means to turn on listing. An argument
+ of 2 means to turn off listing, but as of the next line; that is,
+ the current line should be listed, but the next line should not. */
+
void
listing_list (on)
int on;
{
if (listing)
- listing_tail->edict = on ? EDICT_LIST : EDICT_NOLIST;
+ {
+ switch (on)
+ {
+ case 0:
+ if (listing_tail->edict == EDICT_LIST)
+ listing_tail->edict = EDICT_NONE;
+ else
+ listing_tail->edict = EDICT_NOLIST;
+ break;
+ case 1:
+ if (listing_tail->edict == EDICT_NOLIST
+ || listing_tail->edict == EDICT_NOLIST_NEXT)
+ listing_tail->edict = EDICT_NONE;
+ else
+ listing_tail->edict = EDICT_LIST;
+ break;
+ case 2:
+ listing_tail->edict = EDICT_NOLIST_NEXT;
+ break;
+ default:
+ abort ();
+ }
+ }
}