This is read.c in view mode; [Download] [Up]
/* read.c - read a source file - Copyright (C) 1986,1987 Free Software Foundation, Inc. This file is part of GAS, the GNU Assembler. GAS is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. GAS is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 675 Mass Ave, Cambridge, MA 02139, USA. */ #define MASK_CHAR (0xFF) /* If your chars aren't 8 bits, you will change this a bit. But then, GNU isn't spozed to run on your machine anyway. (RMS is so shortsighted sometimes.) */ #define MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT (16) /* This is the largest known floating point */ /* format (for now). It will grow when we */ /* do 4361 style flonums. */ /* Routines that read assembler source text to build spagetti in memory. */ /* Another group of these functions is in the expr.c module */ #include <ctype.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include "stuff/round.h" #include "as.h" #include "flonum.h" #include "struc-symbol.h" #include "expr.h" #include "read.h" #include "hash.h" #include "obstack.h" #include "md.h" #include "symbols.h" #include "sections.h" #include "input-scrub.h" #include "input-file.h" #include "hex_value.h" #include "messages.h" #include "xmalloc.h" #include "app.h" /* * Parsing of input is done off of this pointer which points to the next char * of source file to parse. */ char *input_line_pointer = NULL; /* * buffer_limit is the value returned by the input_scrub_next_buffer() in * read_a_source_file() and is not static only so read_an_include_file can save * and restore it. */ char *buffer_limit = NULL; /* -> 1 + last char in buffer. */ /* * This table is used by the macros is_name_beginner() and is_part_of_name() * defined in read.h . */ const char lex_type[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ABCDEFGHIJKLMNO */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* PQRSTUVWXYZ[\]^_ */ 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, /* _!"#$%&'()*+,-./ */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 0123456789:;<=>? */ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* @ABCDEFGHIJKLMNO */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, /* PQRSTUVWXYZ[\]^_ */ 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* `abcdefghijklmno */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, /* pqrstuvwxyz{|}~. */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* Allow all chars */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* with the high bit */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* set in names */ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 }; /* * In: a character. * Out: TRUE if this character ends a line. */ #define _ (0) static const char is_end_of_line[256] = { _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, _, /* @abcdefghijklmno */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _,99, _, _, _, _, /* 0123456789:;<=>? */ #if defined(M88K) || defined(M98K) || defined(HPPA) 99, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* @ABCDEFGHIJKLMNO */ #else _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ #endif _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, /* */ _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _ /* */ }; #undef _ /* * The conditional assembly feature (.if, .else, .elseif and .endif) is * implemented with cond_state that tells us what we are in the middle of * processing. ignore can be either TRUE or FALSE. When TRUE we are ignoring * the block of code in the middle of a conditional. MAX_IF_DEPTH is the * maximum depth that if's can be nested. */ #define MAX_IF_DEPTH 20 typedef enum { no_cond, /* no conditional is being processed */ if_cond, /* inside if conditional */ elseif_cond,/* inside elseif conditional */ else_cond /* inside else conditional */ }cond_type; struct cond_state { cond_type the_cond; int cond_met; int ignore; }; typedef struct cond_state cond_stateS; static cond_stateS the_cond_state = {no_cond, FALSE, FALSE}; static cond_stateS last_states[MAX_IF_DEPTH]; static int if_depth = 0; /* * Assembler macros are implemented with these variables and functions. */ #define MAX_MACRO_DEPTH 20 static int macro_depth = 0; static struct hash_control *ma_hash = NULL; /* use before set up: NULL-> address error */ static struct obstack macros; /* obstack for macro text */ static char *macro_name = NULL; /* name of macro we are defining */ static int count_lines = TRUE; /* turns line number counting on and off */ static int macros_on = TRUE; /* .macros_on and .macros_off toggles this to allow macros to be turned off, which allows macros to override a machine instruction and still use it. */ static void expand_macro(char *macro_contents); static void macro_begin(void); /* * The .dump and .load feature is implemented with these variables and * functions. */ static FILE *dump_fp = NULL; static char *write_macro(char *string, char *value); static char * write_symbol(char *string, char *value); /* Functions private to this file */ static void parse_a_buffer(char *buffer); static void parse_line_comment(char **buffer); static segT get_segmented_expression(expressionS *expP); static void pseudo_op_begin(void); static void pseudo_set(symbolS *symbolP); static void stab(int what); static char get_absolute_expression_and_terminator(long *val_pointer); static char *demand_copy_C_string(int *len_pointer); static char *demand_copy_string(int *lenP); static int is_it_end_of_statement(void); static void equals(char *sym_name); static int next_char_of_string(void); #ifdef M68K /* we allow big cons only on the 68k machines */ /* * This is setup by read_begin() and used by big_cons() with using grow_bignum() * to make it bigger if needed. */ #define BIGNUM_BEGIN_SIZE (16) static char *bignum_low; /* Lowest char of bignum. */ static char *bignum_limit;/* 1st illegal address of bignum. */ static char *bignum_high; /* Highest char of bignum, may point to (bignum_start-1), never >= bignum_limit. */ static void grow_bignum(void); #endif /* M68K */ /* * This is set in read_a_source_file() to the section number of the text section * for used by the machine dependent md_assemble() to create line number stabs * for assembly instructions in the text section when -g is seen. */ unsigned long text_nsect = 0; /* * These are the names of the section types used by the .section directive. */ struct type_name { char *name; unsigned type; }; static struct type_name type_names[] = { { "regular", S_REGULAR }, { "cstring_literals", S_CSTRING_LITERALS }, { "4byte_literals", S_4BYTE_LITERALS }, { "8byte_literals", S_8BYTE_LITERALS }, { "literal_pointers", S_LITERAL_POINTERS }, { "non_lazy_symbol_pointers", S_NON_LAZY_SYMBOL_POINTERS }, { "lazy_symbol_pointers", S_LAZY_SYMBOL_POINTERS }, { "symbol_stubs", S_SYMBOL_STUBS }, { "mod_init_funcs", S_MOD_INIT_FUNC_POINTERS }, { NULL, 0 } }; /* * These are the names of the section attributes used by the .section directive. */ struct attribute_name { char *name; unsigned attribute; }; static struct attribute_name attribute_names[] = { { "none", 0 }, { "pure_instructions", S_ATTR_PURE_INSTRUCTIONS }, { "reloc_at_launch", S_ATTR_RELOC_AT_LAUNCH }, { NULL, 0 } }; /* * These are the built in sections know to the assembler with a directive. * They are known as which segment and section name as well as the type & * attribute, and default alignment. */ struct builtin_section { char *directive; char *segname; char *sectname; unsigned long flags; /* type & attribute */ unsigned long default_align; unsigned long sizeof_stub; }; static const struct builtin_section builtin_sections[] = { /* * The text section must be first in this list as it is used by * read_a_source_file() to do the equivalent of a .text at the start * of the file and for s_builtin_section() to set S_ATTR_PURE_INSTRUCTIONS. */ { "text", "__TEXT", "__text" }, { "const", "__TEXT", "__const" }, { "static_const", "__TEXT", "__static_const" }, { "cstring", "__TEXT", "__cstring", S_CSTRING_LITERALS }, { "literal4", "__TEXT", "__literal4", S_4BYTE_LITERALS, 2 }, { "literal8", "__TEXT", "__literal8", S_8BYTE_LITERALS, 3 }, { "constructor", "__TEXT", "__constructor" }, { "destructor", "__TEXT", "__destructor" }, { "fvmlib_init0", "__TEXT", "__fvmlib_init0" }, { "fvmlib_init1", "__TEXT", "__fvmlib_init1" }, { "mod_init_func", "__TEXT", "__mod_init_func", S_MOD_INIT_FUNC_POINTERS, 2 }, { "symbol_stub", "__TEXT", "__symbol_stub", S_SYMBOL_STUBS | S_ATTR_PURE_INSTRUCTIONS, #if defined(M68K) 1, 20 #endif #if defined(I386) 0, 16 #endif #if defined(HPPA) 2, 28 #endif }, { "picsymbol_stub", "__TEXT", "__picsymbol_stub", S_SYMBOL_STUBS | S_ATTR_PURE_INSTRUCTIONS, #if defined(M68K) 1, 22 #endif #if defined(I386) 0, 16 #endif #if defined(HPPA) 2, 28 #endif }, { "non_lazy_symbol_pointer","__DATA","__nl_symbol_ptr", S_NON_LAZY_SYMBOL_POINTERS, 2 }, { "lazy_symbol_pointer", "__DATA", "__la_symbol_ptr", S_LAZY_SYMBOL_POINTERS, 2 }, { "dyld", "__DATA", "__dyld" }, { "data", "__DATA", "__data" }, { "static_data", "__DATA", "__static_data" }, { "objc_class", "__OBJC", "__class" }, { "objc_meta_class", "__OBJC", "__meta_class" }, { "objc_string_object", "__OBJC", "__string_object" }, { "objc_protocol", "__OBJC", "__protocol" }, { "objc_cat_cls_meth", "__OBJC", "__cat_cls_meth" }, { "objc_cat_inst_meth", "__OBJC", "__cat_inst_meth" }, { "objc_cls_meth", "__OBJC", "__cls_meth" }, { "objc_inst_meth", "__OBJC", "__inst_meth" }, { "objc_message_refs", "__OBJC", "__message_refs", S_LITERAL_POINTERS, 2}, { "objc_cls_refs", "__OBJC", "__cls_refs", S_LITERAL_POINTERS, 2}, { "objc_class_names", "__OBJC", "__class_names", S_CSTRING_LITERALS }, { "objc_module_info", "__OBJC", "__module_info" }, { "objc_symbols", "__OBJC", "__symbols" }, { "objc_category", "__OBJC", "__category" }, { "objc_meth_var_types", "__OBJC", "__meth_var_types", S_CSTRING_LITERALS }, { "objc_class_vars", "__OBJC", "__class_vars" }, { "objc_instance_vars", "__OBJC", "__instance_vars" }, { "objc_meth_var_names", "__OBJC", "__meth_var_names", S_CSTRING_LITERALS }, { "objc_selector_strs", "__OBJC", "__selector_strs", S_CSTRING_LITERALS }, { 0 } }; /* set up pseudo-op tables */ static struct hash_control *po_hash = NULL; #if !defined(I860) /* i860 has it's own align and org */ static void s_align(int value); static void s_org(int value); #endif static void s_private_extern(int value); static void s_indirect_symbol(int value); static void s_abort(int value); static void s_comm(int value); static void s_desc(int value); static void s_file(int value); static void s_fill(int value); static void s_lcomm(int value); static void s_lsym(int value); static void s_set(int value); static void s_reference(int value); static void s_include(int value); static void s_dump(int value); static void s_load(int value); static void s_if(int value); static void s_elseif(int value); static void s_else(int value); static void s_endif(int value); static void s_macros_on(int value); static void s_macros_off(int value); static void s_section(int value); static void s_zerofill(int value); static unsigned long s_builtin_section(const struct builtin_section *s); /* * The machine independent pseudo op table. */ static const pseudo_typeS pseudo_table[] = { #if !defined(I860) /* i860 has it's own align and org */ { "align", s_align, 0 }, { "org", s_org, 0 }, #endif #ifndef M88K /* m88k has it's own abs that uses the s_abs() in here */ { "abs", s_abs, 0 }, #endif { "private_extern", s_private_extern, 0}, { "indirect_symbol", s_indirect_symbol, 0}, { "abort", s_abort, 0 }, { "ascii", stringer, 0 }, { "asciz", stringer, 1 }, { "byte", cons, 1 }, { "comm", s_comm, 0 }, { "desc", s_desc, 0 }, { "double", float_cons, 'd' }, { "file", s_file, 0 }, { "fill", s_fill, 0 }, { "globl", s_globl, 0 }, { "lcomm", s_lcomm, 0 }, { "line", s_line, 0 }, { "long", cons, 4 }, { "lsym", s_lsym, 0 }, { "section", s_section, 0 }, { "zerofill", s_zerofill, 0 }, { "set", s_set, 0 }, { "short", cons, 2 }, { "single", float_cons, 'f' }, { "space", s_space, 0 }, { "stabd", stab, 'd' }, { "stabn", stab, 'n' }, { "stabs", stab, 's' }, { "reference",s_reference, 0 }, { "include", s_include, 0 }, { "macro", s_macro, 0 }, { "endmacro", s_endmacro, 0 }, { "macros_on",s_macros_on, 0 }, { "macros_off",s_macros_off, 0 }, { "if", s_if, 0 }, { "elseif", s_elseif, 0 }, { "else", s_else, 0 }, { "endif", s_endif, 0 }, { "dump", s_dump, 0 }, { "load", s_load, 0 }, { NULL } /* end sentinel */ }; /* * read_begin() initializes the assember to read assembler source input. */ void read_begin( void) { pseudo_op_begin(); macro_begin(); obstack_begin(¬es, 5000); #ifdef M68K /* we allow big cons only on the 68k machines */ bignum_low = xmalloc((long)BIGNUM_BEGIN_SIZE); bignum_limit = bignum_low + BIGNUM_BEGIN_SIZE; #endif } /* * pseudo_op_begin() creates a hash table of pseudo ops from the machine * independent and machine dependent pseudo op tables. */ static void pseudo_op_begin( void) { char *errtxt; const pseudo_typeS *pop; unsigned long i; pseudo_typeS *sections_pseudo_table; po_hash = hash_new(); errtxt = ""; for(pop = pseudo_table; pop->poc_name && *errtxt == '\0'; pop++) errtxt = hash_insert(po_hash, pop->poc_name, (char *)pop); for(pop = md_pseudo_table; pop->poc_name && *errtxt == '\0'; pop++) errtxt = hash_insert(po_hash, pop->poc_name, (char *)pop); for(i = 0; builtin_sections[i].directive != NULL; i++) ; sections_pseudo_table = xmalloc((i + 1) * sizeof(pseudo_typeS)); for(i = 0; builtin_sections[i].directive != NULL; i++){ sections_pseudo_table[i].poc_name = builtin_sections[i].directive; sections_pseudo_table[i].poc_handler = (void (*)(int))s_builtin_section; sections_pseudo_table[i].poc_val = (int)(builtin_sections + i); } sections_pseudo_table[i].poc_name = NULL; for(pop = (const pseudo_typeS *)sections_pseudo_table; pop->poc_name && *errtxt == '\0'; pop++) errtxt = hash_insert(po_hash, pop->poc_name, (char *)pop); if(*errtxt != '\0'){ as_fatal("error constructing pseudo-op table (%s)", errtxt); } } /* * The NeXT version of: read_a_source_file() * * This differs from the GNU version by taking the guts of the GNU * read_a_source_file() (with the outer most loop removed) and renaming it * parse_a_buffer(). With the NeXT version of read_a_source file simply * containing that outer loop and a call to parse_a_buffer(). This is done * So that expand_macro() and parse_line_comment() can call parse_a_buffer() * with the buffers they create. */ void read_a_source_file( char *buffer) /* 1st character of each buffer of lines is here. */ { cond_stateS starting_cond_state; short starting_if_depth; symbolS *symbolP; starting_cond_state = the_cond_state; starting_if_depth = if_depth; /* Do not change segments or subsegments if this is a .include */ if(doing_include == FALSE){ /* * This is a new file so switch start as if a .text was seen. This * call to s_builtin_section() relys on the fact that the text * section is first in the built in sections list. */ if(flagseen['n'] == FALSE) text_nsect = s_builtin_section(builtin_sections); /* * If the -g flag is present generate the lead stabs for this * physical file that is not an include file. Each physical file's * stabs are enclosed by a pair of source name stabs, N_SO, (one at * the begining of the file with the name of the file and one at the * end with the name ""). This is seen by nm(1) as: * 00000000 - 01 0000 SO {standard input} * ... * 00000020 - 01 0000 SO * To make the debugger work line numbers stabs, N_SLINE, must be * contained "in a function" (after a function stab, N_FUN). To * make a function stab work it must have a type number. Since type * numbers 1 and 2 (the 1 in "int:t1=..." and the 2 in "char:t2=..." * are "magic" to the debugger we use type 3 for the types of the * function stabs we generate for each text label (see the routine * make_stab_for_symbol() in symbols.c). So at lead stabs at the * begining of each physical file include three type stabs, L_LSYM * with the correct symbol name. The since we must have the types * 1 and 2 they are just what the 'C' would produce but we don't * use them. Type 3 is the void type like the 'C' compiler would * produce which we use for the function stabs' type. These three * look like this to nm(1): * 00000000 - 00 0000 LSYM int:t1=r1;-2147483648;2147483647; * 00000000 - 00 0000 LSYM char:t2=r2;0;127; * 00000000 - 00 0000 LSYM void:t3=3 * * Then for each text label we see, make_stab_for_symbol() will * generate a stab like this (for the example lable _main): * 00000000 - 01 0007 FUN _main:F3 * where the 'F' in F3 is an upper case 'F' for global labels and * a lower case 'f' for non globals. * * Then for each instruction we assemble in the text we generate * a line number, S_LINE, stab (see md_assembler in m68k.c, m88k.c * etc). These look like: * 00000000 - 01 0008 SLINE * where the 0008 is the line number. */ if(flagseen['g']){ symbolP = symbol_new( physical_input_file, 100 /* N_SO */, text_nsect, 0, obstack_next_free(&frags) - frag_now->fr_literal, frag_now); symbolP = symbol_new( "int:t1=r1;-2147483648;2147483647;", 128 /* N_LSYM */, 0,0,0, &zero_address_frag); symbolP = symbol_new( "char:t2=r2;0;127;", 128 /* N_LSYM */, 0,0,0, &zero_address_frag); symbolP = symbol_new( "void:t3=3", 128 /* N_LSYM */, 0,0,0, &zero_address_frag); } } else{ /* * If we are now reading an include file we will bracket it's * stabs with a pair of: * 00000010 - 01 0000 SOL include_file * ... * 0000001c - 01 0000 SOL previous_file * We generate the first N_SOL here and the one for the * previous_file in s_include() in read.c. * * CAVAT: This will only work if the include file starts off in the * (__TEXT,__text) sections and ends in the (__TEXT,__text) section. */ if(flagseen['g'] && frchain_now->frch_nsect == text_nsect){ symbolP = symbol_new( physical_input_file, 132 /* N_SOL */, text_nsect, 0, obstack_next_free(&frags) - frag_now->fr_literal, frag_now); } } while((buffer_limit = input_scrub_next_buffer(&buffer)) != NULL) parse_a_buffer(buffer); if(the_cond_state.the_cond != starting_cond_state.the_cond || the_cond_state.ignore != starting_cond_state.ignore|| if_depth != starting_if_depth) as_warn("file contains unmatched .ifs or .elses"); if(doing_include == FALSE){ /* See the comment at the top of this routine for a description of what is going on here */ if(flagseen['n'] == FALSE) text_nsect = s_builtin_section(builtin_sections); if(flagseen['g']){ (void)symbol_new( "", 100 /* N_SO */, text_nsect, 0, obstack_next_free(&frags) - frag_now->fr_literal, frag_now); } } } /* * parse_a_buffer() operates on a buffer of lines. It drives the * parsing of lines of assembly code. The lines are assumed to be "well formed" * assembly so the syntax recognized in here is that produced by the output of * the assembly preprocessor (app) or by the compiler when it produces a file * that starts with "#NO_APP\n". A "well formed" assembly is lines with exactly * zero or one leading "well formed space character" (' ', '\t' or '\f') * followed by lines of: * zero or more lables (a name or a digit followed by a colon) * each followed by zero or one "well formed space character" * exactly one of the following followed by a logicial end of line: * a pseudo opcode * followed by zero or one space (' ') characters and it's * arguments (the space is required when the first * character of the first argument could be part of a name) * a macro to be expanded * a machine opcode * a null statement * an assignment to a symbol * a full line comment (in the case of "well formed" assembly it * must be "#APP\n" of a collection of lines * wrapped in "#APP\n ... #NO_APP\n") * * input: * buffer pointer to the start of the buffer of lines * (passed as an argument) * buffer_limit pointer to the end of the buffer of lines, that is the * the character it points to is NOT part of the buffer * (buffer_limit is declared in this file) * * Assumptions about the buffer of lines: * buffer[-1] == '\n' as done in input-scrub.c with the cpp macro * BEFORE_STRING ("\n") * buffer_limit[-1] == '\n' also as done in input-scrub.c which handles * partial lines internally to itself and always * passes back a buffer of complete lines. * * input/output: (for other parsing routines) * input_line_pointer pointer to the next thing in the buffer after * what has been recognized (a global) */ static void parse_a_buffer( char *buffer) { char c; /* contains the first non-space character the current word used to figure out what it is */ char *s; /* points to a name with character after the name replaced with a '\0' so it is a 'C' string */ char after_name; /* contains that first character after a name that got replaced with a '\0' */ char *after_name_pointer;/* points to the end of the name where the '\0' is for error use only */ char end_of_line; /* contains an end of line character that got replaced with a '\0' */ char *start_of_line;/* points to the locical start of line we're parsing, used only for macro expansion */ pseudo_typeS *pop; /* pointer to a pseudo op stucture returned by hash_find(po_hash, s+1) to determine if it is one */ char *the_macro; /* pointer to a macro name returned by hash_find(ma_hash, s) to determine if it is one */ int digit_value; /* the value of a digit label as an integer, 1: == 1 */ /* since this is a buffer of full lines it must end in a new line */ know(buffer_limit[-1] == '\n'); input_line_pointer = buffer; /* while we have more of this buffer to parse keep parsing */ while(input_line_pointer < buffer_limit){ /* * At the top of this loop we know that we just parsed a label or we * are at the beginning of a logical line (since their can be more * than one label on a line). start_of_line is only used by * expand_macro() */ start_of_line = input_line_pointer; /* * If we are not counting lines (as in the case when called by * expand_macro() ) and we just previously scaned over a newline * (a physical end of line) bump the line counters (see the comments * at the head of this routine about "assumptions about the buffer" * and why it is safe to index input_line_pointer by -1. */ if(count_lines == TRUE && input_line_pointer[-1] == '\n') bump_line_counters (); /* * We expect a "well-formed" assembler statement. This means it was * processed by app or produced by a compiler where the file started * with a leading "#APP\n". A "well-formed" statement allows zero * or one leading white space characters. */ c = *input_line_pointer; input_line_pointer++; if(c == '\t' || c == ' ' || c=='\f'){ c = *input_line_pointer; input_line_pointer++; } know(c != ' '); /* No further leading whitespace. */ /* * c contains the 1st significant character, *input_line_pointer * points after that character. */ /* * look for the begining of a name which could be one of the * following assembly statements: * A pseudo opcode and locical end of line * A macro to be expanded and locical end of line * A machine opcode and locical end of line * A user-defined label (name not digit)(no end of line needed) * At NeXT labels can be enclosed in ""'s so that Objective-C like * names (with spaces and colons) can be part of a name, the * routine get_symbol_end() knows about this. */ if(is_name_beginner(c) || c == '"'){ if( c == '"') s = input_line_pointer--; else s = --input_line_pointer; after_name = get_symbol_end(); /* name's delimiter */ after_name_pointer = input_line_pointer; /* * after_name is the character after symbol. That character's * place in the input line is now '\0',done by get_symbol_end(). * s points to the beginning of the symbol (in the case of a * pseudo-op, *s == '.'). *input_line_pointer == '\0' where * after_name was. after_name_pointer is recorded so it their * is an error after the line has been restored the '\0' can * be reset and the name printed. */ /* * Look for a name that should be a pseudo op. That is it is * not a user defined label or an assignment to a symbol name. * This must be done so such thing as ".foo:" and ".bar=1" are * not mistaken for illegal pseudo ops and that something like * ".long: .long 1" creates a symbol named ".long". */ if(*s == '.' && (after_name != ':' && after_name != '=' && !((after_name == ' ' || after_name == '\t') && input_line_pointer[1] == '=') ) ){ /* * Lookup what should be a pseudo op and then restore the * line. */ pop = (pseudo_typeS *)hash_find(po_hash, s+1); *input_line_pointer = after_name; /* * A pseudo op must be followed by character that is not * part of a name so it can be parsed. If their is a first * argument that could start with a character in a name then * one "well formed space" (space or a tab) must follow the * pseudo op (otherwise the space is optional). */ if(after_name == ' ' || after_name == '\t') input_line_pointer++; /* * Now the current state of the line is the after_name has * been placed back in the line (the line is restored) and * input_line_pointer is at the start of the first argument * of the pseudo op (if any). */ if(the_cond_state.ignore){ /* * When ignoring a block of code during conditional * assembly we can't ignore .if, .else, and .endif * pseudo ops. */ if(pop != NULL && ( (pop->poc_handler == s_if) || (pop->poc_handler == s_elseif) || (pop->poc_handler == s_else) || (pop->poc_handler == s_endif) ) ) (*pop->poc_handler)(pop->poc_val); else totally_ignore_line(); } else if(macro_name){ /* * When defining a macro we can't ignore .endmacro * pseudo ops. */ if(pop != NULL && pop->poc_handler == s_endmacro) (*pop->poc_handler)(pop->poc_val); else add_to_macro_definition(start_of_line); } else{ if(pop != NULL) (*pop->poc_handler)(pop->poc_val); else{ after_name = *after_name_pointer; *after_name_pointer = '\0'; /* * If macros are on see if this is a use of a macro * otherwise it is an unknown pseudo op. */ if(macros_on == TRUE && (the_macro = hash_find(ma_hash, s)) != NULL){ *after_name_pointer = after_name; expand_macro(the_macro); } else{ as_warn ("Unknown pseudo-op: %s", s); *after_name_pointer = after_name; ignore_rest_of_line(); } } } continue; } /* if(*s == '.' && ... ) */ /* * If we are in a conditional and the state is that we are now * not including lines to be assembled then ignore the line. */ if(the_cond_state.ignore){ *input_line_pointer = after_name; totally_ignore_line(); } /* * If we are in the state of defining a macro then take the line * for the macro definition. */ else if(macro_name != NULL){ *input_line_pointer = after_name; add_to_macro_definition(start_of_line); } /* * Look for a user defined label. */ else if(after_name == ':'){ colon(s); #ifdef I860 /* * Intel :: feature, which makes the label global if * followed by two "::"'s . This is ifdef'ed in so their * is no else cause thus the slightly odd logic. */ if(input_line_pointer[1] == ':'){ struct symbol *symbolP; symbolP = symbol_find_or_make(s); symbolP->sy_type |= N_EXT; /* make symbol name global */ *input_line_pointer = ':'; /* Restore first ':' */ input_line_pointer++; /* step over first ':' */ } #endif /* put ':' back for error messages and step over it */ *input_line_pointer = ':'; input_line_pointer++; } /* * Parse the assignment to a symbol. The syntax for this is * <symbol><equal><expression>. */ else if(after_name == '=' || ((after_name == ' ' || after_name == '\t') && input_line_pointer[1] == '=')){ equals(s); demand_empty_rest_of_line(); } /* * If macros are on see if this is a use of a macro. */ else if(macros_on == TRUE && (the_macro = hash_find(ma_hash, s)) != NULL){ *input_line_pointer = after_name; expand_macro(the_macro); } /* * Now assume it is a machine instruction and if not it * will be handled as an error. Machine instructions must be * one to a line. */ else{ *input_line_pointer = after_name; while(is_end_of_line[(int)*input_line_pointer] == FALSE) input_line_pointer ++; end_of_line = *input_line_pointer; *input_line_pointer = '\0'; md_assemble(s); *input_line_pointer = end_of_line; input_line_pointer++; } /* * At this point we have parsed all things that could have * started with a name. Since one of these things (user defined * lables could appear more than once on a line we do a continue * here and start parsing as if at the begining of another * logicial line. */ continue; } /* if(is_name_beginner(c) || c == '"') */ /* empty statement */ if(is_end_of_line[(int)c]) continue; /* * If we are in a conditional and the state is that we are now * not including lines to be assembled then ignore the line. */ if(the_cond_state.ignore){ totally_ignore_line(); continue; } /* * If we are in the state of defining a macro then take the line * for the macro definition. */ if(macro_name != NULL){ add_to_macro_definition(start_of_line); continue; } /* local label ("4:") */ if(isdigit(c)){ digit_value = c - '0'; if(*input_line_pointer++ == ':' ){ local_colon(digit_value); } else{ as_warn("Spurious digit %d.", digit_value); input_line_pointer--; ignore_rest_of_line(); } continue; } /* * The only full line comment that should make it here is the first * of the pair of "#APP\n ... #NO_APP\n" that the compiler uses to * wrap around asm() statements. If that is the case then * parse_line_comment() creates a buffer with those lines in it and * calls parse_a_buffer() with that buffer. Then returns here * skiping over that part of the current buffer. */ if(c != '\0' && strchr(md_line_comment_chars, c) != NULL){ parse_line_comment(&buffer); continue; } as_warn("Junk character %d.",c); ignore_rest_of_line(); } /* while(input_line_pointer < buffer_limit) */ } /* * parse_line_comment() parses a line comment for parse_a_buffer(). Since * parse_a_buffer() only operates on "well formed" assembly the only legal * line comment that should appear is a "#APP\n ... #NO_APP\n" pair which * tells us to scrub the characters between them and then parse them. */ static void parse_line_comment( char **buffer) { char *s; char *ends; char *new_buf; char *new_tmp; int new_length; char *tmp_buf; char *old_input_line_pointer; char *old_buffer_limit; /* parse_a_buffer should never see any line comment if app is on */ know(preprocess == FALSE); s = input_line_pointer; /* This must be a #APP\n line comment if not ignore it */ if(strncmp(s,"APP\n",4) != 0) return; if(count_lines == TRUE) bump_line_counters(); s += sizeof("APP\n") - 1; /* * Search for the matching #NO_APP\n in this buffer, if it is found * in this buffer the un-scrubed characters between the "#APP\n" and * "#NO_APP\n" start where s is pointing to and end where ends is * pointing to. */ ends = strstr(s, "#NO_APP\n"); tmp_buf = NULL; if(ends == NULL){ /* The matching #NO_APP\n for the #APP\n wasn't in this buffer. */ int tmp_len; int num; /* * First create a temporary place (tmp_buf of size tmp_len) to * collect the un-scrubbed characters between the "#APP\n" and the * "#NO_APP\n" (or end of file) when we find it in some buffer. */ tmp_len = buffer_limit - s; tmp_buf = xmalloc(tmp_len); /* * Copy the end of the buffer that contains the first part of * the un-scrubbed contents starting just after the "#APP\n". * This is so the the current buffer (buffer) can be used to * collect the the rest of the un-scrubbed contents and to find * the matching "#NO_APP\n". */ memcpy(tmp_buf, s, tmp_len); /* * This loop collects the remaining un-scrubed contents between * "#APP\n" and the "#NO_APP\n" into tmp_buf (adjusting tmp_len) * and looks for the matching "#NO_APP\n". */ do{ buffer_limit = input_scrub_next_buffer(buffer); /* * We treat runing into the end of the file as if it was the * "#NO_APP" we were looking for. */ if(buffer_limit == NULL) break; ends = strstr(*buffer, "#NO_APP\n"); if(ends != NULL) num = ends - *buffer; else num = buffer_limit - *buffer; tmp_buf = xrealloc(tmp_buf, tmp_len + num); memcpy(tmp_buf + tmp_len, *buffer, num); tmp_len += num; }while(ends == NULL); /* * Now set up buffer, buffer_limit and input_line_pointer be past * all the characters of the "#APP\n ... #NO_APP\n" set so that * when we return parsing will be picked up from their. */ if(ends != NULL) input_line_pointer = ends + sizeof("#NO_APP\n") - 1; else{ input_line_pointer = *buffer; buffer_limit = *buffer; } /* * Now set s to the start, and ends to the end of the un-scrubed * contents of the collected characters between the "#APP\n" and * "#NO_APP\n" pair. */ s = tmp_buf; ends = s + tmp_len; } else{ /* * The matching "#NO_APP\n" was in the buffer as we were called so * s is the start, and ends is the end of the un-scrubed contents * of the characters between the "#APP\n" and "#NO_APP\n" pair. * Now to set up buffer, buffer_limit and input_line_pointer be past * all the characters of the "#APP\n ... #NO_APP\n" set so that * when we return parsing will be picked up from their all that has * to be done is move the input_line_pointer past the "#NO_APP\n". */ input_line_pointer = ends + sizeof("#NO_APP\n") - 1; } /* * Now that we have the un-scrubed characters beween s and ends setup * to scrub them into a new buffer (new_buf of size new_length to * new_tmp). */ new_length = 100; new_buf = xmalloc(new_length); new_tmp = new_buf; *new_tmp++ = '\n'; /* place leading \n in buffer for parse_a_buffer */ scrub_string = s; scrub_last_string = ends; for(;;){ int c; c = do_scrub_next_char(scrub_from_string, scrub_to_string); if(c == EOF) break; *new_tmp++ = c; if(new_tmp == new_buf + new_length){ new_buf = xrealloc(new_buf, new_length + 100); new_tmp = new_buf + new_length; new_length += 100; } } *new_tmp = '\n'; /* place trailing \n in buffer for parse_a_buffer */ /* * If we used a temporary buffer to collect the un-scrubbed characters * it is no longer needed and can be free()'ed. */ if(tmp_buf != NULL) free(tmp_buf); /* * Now we are ready to recursively call parse_a_buffer() with our buffer * of scrubed characters. So save the state of parse_a_buffer() and set * it up with our buffer of scrubed characters. */ old_input_line_pointer = input_line_pointer; old_buffer_limit = buffer_limit; input_line_pointer = new_buf; buffer_limit = new_tmp; parse_a_buffer(new_buf); /* * Free the buffer that held the scrubbed characters */ free(new_buf); /* * After coming back from our recursive call parse_a_buffer() we want * resume parsing after the "#NO_APP\n". So bump the line counters * for the "#NO_APP\n" and restore the state so we can return to * parse_a_buffer(). */ if(count_lines == TRUE) bump_line_counters(); input_line_pointer = old_input_line_pointer; buffer_limit = old_buffer_limit; return; } /* * s_abort() implements the pseudo op: * .abort [ "abort_string" ] */ static void s_abort( int value) { char *p; p = input_line_pointer; while(is_end_of_line[(int)*p] == FALSE) p++; *p = '\0'; as_fatal(".abort %s detected. Assembly stopping.", input_line_pointer); } #if !defined(I860) /* i860 has it's own align and org */ /* * s_align() implements the pseudo op * .align expression [ , fill_expression ] */ static void s_align( int value) { int temp; long temp_fill; temp = get_absolute_expression(); #define MAX_ALIGNMENT (15) if(temp > MAX_ALIGNMENT) as_warn("Alignment too large: %d. assumed.", temp = MAX_ALIGNMENT); else if(temp < 0){ as_warn("Alignment negative. 0 assumed."); temp = 0; } if(*input_line_pointer == ','){ input_line_pointer ++; temp_fill = get_absolute_expression (); } else temp_fill = 0; /* Only make a frag if we HAVE to. . . */ if(temp != 0) frag_align(temp, (int)temp_fill); /* * If this alignment is larger than any previous alignment then this * becomes the section's alignment. */ if(frchain_now->frch_section.align < temp) frchain_now->frch_section.align = temp; demand_empty_rest_of_line(); } #endif /* !defined(I860) i860 has it's own align and org */ /* * s_comm() implements the pseudo op: * .comm name , expression */ static void s_comm( int value) { char *name; char c; char *p; int temp; symbolS *symbolP; if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); /* just after name is now '\0' */ p = input_line_pointer; *p = c; SKIP_WHITESPACE(); if(*input_line_pointer != ','){ as_warn("Expected comma after symbol-name"); ignore_rest_of_line(); return; } input_line_pointer++; /* skip ',' */ if((temp = get_absolute_expression ()) < 0){ as_warn(".COMMon length (%d.) <0! Ignored.", temp); ignore_rest_of_line(); return; } *p = 0; symbolP = symbol_find_or_make(name); *p = c; if((symbolP->sy_type & N_TYPE) != N_UNDF || symbolP->sy_other != 0 || symbolP->sy_desc != 0) { as_warn("Ignoring attempt to re-define symbol"); ignore_rest_of_line(); return; } if(symbolP->sy_value != 0){ if(symbolP->sy_value != temp) as_warn("Length of .comm \"%s\" is already %ld. Not changed " "to %d.", symbolP->sy_name, symbolP->sy_value, temp); } else{ symbolP -> sy_value = temp; symbolP -> sy_type |= N_EXT; } know(symbolP->sy_frag == &zero_address_frag); demand_empty_rest_of_line(); } /* * s_desc() implements the pseudo op: * .desc name , expression * sets the n_desc field of a symbol. */ static void s_desc( int value) { char *name; char c; char *p; symbolS *symbolP; int temp; if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); p = input_line_pointer; symbolP = symbol_table_lookup(name); *p = c; SKIP_WHITESPACE(); if(*input_line_pointer != ','){ *p = 0; as_warn("Expected comma after name \"%s\"", name); *p = c; ignore_rest_of_line(); } else{ input_line_pointer++; temp = get_absolute_expression(); *p = 0; symbolP = symbol_find_or_make(name); *p = c; symbolP->sy_desc = temp; } demand_empty_rest_of_line(); } /* * s_file() implements the pseudo op: * .file name [ level_number ] * the level number is generated by /lib/cpp and is just ignored. */ static void s_file( int value) { char *s; int length; struct symbol *symbolP; /* Some assemblers tolerate immediately following '"' */ if((s = demand_copy_string(&length))){ SKIP_WHITESPACE(); if(*input_line_pointer >= '0' && *input_line_pointer <= '9'){ while(*input_line_pointer >= '0' && *input_line_pointer <= '9') input_line_pointer++; } new_logical_line(s, -1); demand_empty_rest_of_line(); /* * This is to generate stabs for debugging assembly code. * See the comments about stabs in read_a_source_file() * for a description of what is going on here. */ if(flagseen['g'] && frchain_now->frch_nsect == text_nsect){ symbolP = symbol_new( logical_input_file, 132 /* N_SOL */, text_nsect, 0, obstack_next_free(&frags) - frag_now->fr_literal, frag_now); } } } /* * s_fill() implements the pseudo op: * .fill repeat_expression , fill_size , fill_expression */ static void s_fill( int value) { long temp_repeat; long temp_size; long temp_fill; char *p; if(get_absolute_expression_and_terminator(&temp_repeat) != ','){ input_line_pointer--; /* Backup over what was not a ','. */ as_warn("Expect comma after rep-size in .fill"); ignore_rest_of_line(); return; } if(get_absolute_expression_and_terminator(&temp_size) != ','){ input_line_pointer--; /* Backup over what was not a ','. */ as_warn("Expected comma after size in .fill"); ignore_rest_of_line(); return; } /* * This is to be compatible with BSD 4.2 AS, not for any rational * reason. */ #define BSD_FILL_SIZE_CROCK_8 (8) if(temp_size > BSD_FILL_SIZE_CROCK_8){ as_warn(".fill size clamped to %d.", BSD_FILL_SIZE_CROCK_8); temp_size = BSD_FILL_SIZE_CROCK_8 ; } if(temp_size < 0){ as_warn("Size negative: .fill ignored."); temp_size = 0; } /* * bug fix, if md_number_to_chars() is called with something other than * 1,2 or 4 it calls abort(). So we don't let the size be something * like 3. Bug #13017. */ else if(temp_size != 0 && temp_size != 1 && temp_size != 2 && temp_size != 4){ as_warn("Repeat must be 0,1,2 or 4, .fill ignored"); temp_size = 0; } else if(temp_repeat <= 0){ as_warn("Repeat < 0, .fill ignored"); temp_size = 0; } temp_fill = get_absolute_expression(); /* * Note: .fill (),0 emits no frag (since we are asked to .fill 0 bytes) * but emits no error message because it seems a legal thing to do. * It is a degenerate case of .fill but could be emitted by a compiler. */ if(temp_size != 0){ p = frag_var(rs_fill, (int)temp_size, (int)temp_size, (relax_substateT)0, (symbolS *)0, temp_repeat, (char *)0); memset(p, '\0', (int)temp_size); /* * The magic number BSD_FILL_SIZE_CROCK_4 is from BSD 4.2 VAX * flavoured AS. The following bizzare behaviour is to be * compatible with above. I guess they tried to take up to 8 * bytes from a 4-byte expression and they forgot to sign extend. */ #define BSD_FILL_SIZE_CROCK_4 (4) md_number_to_chars(p, temp_fill, temp_size > BSD_FILL_SIZE_CROCK_4 ? BSD_FILL_SIZE_CROCK_4 : (int)temp_size); } demand_empty_rest_of_line(); } /* * s_globl() implements the pseudo op: * .globl name [ , name ] */ void s_globl( int value) { char *name; int c; symbolS *symbolP; do{ if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); symbolP = symbol_find_or_make(name); *input_line_pointer = c; SKIP_WHITESPACE(); symbolP->sy_type |= N_EXT; if(c == ','){ input_line_pointer++; SKIP_WHITESPACE(); if(*input_line_pointer == '\n') c = '\n'; } }while(c == ','); demand_empty_rest_of_line(); } /* * s_private_extern() implements the pseudo op: * .private_extern name [ , name ] */ static void s_private_extern( int value) { char *name; int c; symbolS *symbolP; if(!flagseen['k']) as_fatal("incompatible feature used: .private_extern (must specify " "-k to be used)"); do{ if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); symbolP = symbol_find_or_make(name); *input_line_pointer = c; SKIP_WHITESPACE(); symbolP->sy_type |= N_EXT; symbolP->sy_type |= N_PEXT; if(c == ','){ input_line_pointer++; SKIP_WHITESPACE(); if(*input_line_pointer == '\n') c = '\n'; } }while(c == ','); demand_empty_rest_of_line(); } /* * s_indirect_symbol() implements the pseudo op: * .indirect_symbol name */ static void s_indirect_symbol( int value) { char *name; int c; unsigned long section_type; if(!flagseen['k']) as_fatal("incompatible feature used: .indirect_symbol (must " "specify -k to be used)"); if(frchain_now == NULL){ know(flagseen['n']); as_fatal("with -n a section directive must be seen before assembly " "can begin"); } section_type = frchain_now->frch_section.flags & SECTION_TYPE; if(section_type != S_NON_LAZY_SYMBOL_POINTERS && section_type != S_LAZY_SYMBOL_POINTERS && section_type != S_SYMBOL_STUBS){ as_warn("indirect symbol not in a symbol pointer or stub section, " ".indirect_symbol ignored"); ignore_rest_of_line(); return; } if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); indirect_symbol_new(name, frag_now, obstack_next_free(&frags) - frag_now->fr_literal); *input_line_pointer = c; demand_empty_rest_of_line(); } /* * s_lcomm() implements the pseudo op: * .lcomm name , size_expression [ , align_expression ] */ static void s_lcomm( int value) { char *name; char c; char *p; int size; symbolS *symbolP; int align; static frchainS *bss = NULL; if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); p = input_line_pointer; *p = c; SKIP_WHITESPACE(); if(*input_line_pointer != ','){ as_warn("Expected comma after name"); ignore_rest_of_line(); return; } input_line_pointer ++; if((size = get_absolute_expression()) < 0){ as_warn("BSS length (%d.) <0! Ignored.", size); ignore_rest_of_line(); return; } #define MAX_ALIGNMENT (15) align = 0; if(*input_line_pointer == ','){ input_line_pointer++; align = get_absolute_expression(); if(align > MAX_ALIGNMENT){ as_warn("Alignment too large: %d. assumed.", MAX_ALIGNMENT); align = MAX_ALIGNMENT; } else if(align < 0){ as_warn("Alignment negative. 0 assumed."); align = 0; } } *p = 0; symbolP = symbol_find_or_make(name); *p = c; if((symbolP->sy_type & N_TYPE) == N_UNDF && symbolP->sy_value == 0){ if(bss == NULL){ bss = section_new(SEG_DATA, SECT_BSS, S_ZEROFILL, 0, 0); bss->frch_root = xmalloc(SIZEOF_STRUCT_FRAG); memset(bss->frch_root, '\0', SIZEOF_STRUCT_FRAG); bss->frch_last = bss->frch_root; } bss->frch_root->fr_address = round(bss->frch_root->fr_address, 1 << align); symbolP->sy_value = bss->frch_root->fr_address; symbolP->sy_type = N_SECT; symbolP->sy_other = bss->frch_nsect; symbolP->sy_frag = bss->frch_root; bss->frch_root->fr_address += size; /* * If this alignment is larger than any previous alignment then this * becomes the section's alignment. */ if(bss->frch_section.align < align) bss->frch_section.align = align; } else as_warn("Ignoring attempt to re-define symbol."); demand_empty_rest_of_line(); } /* * s_line() implements the pseudo op: * .line line_number */ void s_line( int value) { /* * Assume delimiter is part of expression. BSD4.2 as fails with * delightful bug, so we are not being incompatible here. */ /* * Since the assembler bumps it's line counters at the end of a line * and it is the case that the .line is on it's own line what the * intent is that the line number is for the next line. Thus * the -1 . This is the way cpp'ed assembler files work which is the * common case. */ new_logical_line((char *)NULL, (int)(get_absolute_expression()) - 1); demand_empty_rest_of_line(); } /* * s_lsym() implements the pseudo op: * .lsym name , expression */ static void s_lsym( int value) { char *name; char c; char *p; segT segment; expressionS exp; symbolS *symbolP; /* we permit ANY expression: BSD4.2 demands constants */ if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); p = input_line_pointer; *p = c; SKIP_WHITESPACE(); if(*input_line_pointer != ','){ *p = 0; as_warn("Expected comma after name \"%s\"", name); *p = c; ignore_rest_of_line(); return; } input_line_pointer++; segment = expression(&exp); if(segment != SEG_ABSOLUTE && segment != SEG_SECT){ /* this warning still need fixing */ as_warn("Bad expression: %s", seg_name[(int)segment]); ignore_rest_of_line(); return; } know(segment == SEG_ABSOLUTE || segment == SEG_SECT); *p = 0; if(segment == SEG_SECT) symbolP = symbol_new(name, N_SECT, frchain_now->frch_nsect, 0, (valueT)(exp.X_add_number), &zero_address_frag); else symbolP = symbol_new(name, N_ABS, 0, 0, (valueT)(exp.X_add_number), &zero_address_frag); *p = c; demand_empty_rest_of_line(); } #if !defined(I860) /* i860 has it's own align and org */ /* * s_org() implements the pseudo op: * .org expression */ static void s_org( int value) { segT segment; expressionS exp; long temp_fill; char *p; /* * Don't believe the documentation of BSD 4.2 AS. * There is no such thing as a sub-segment-relative origin. * Any absolute origin is given a warning, then assumed to be * segment-relative. * Any segmented origin expression ("foo+42") had better be in the right * segment or the .org is ignored. * * BSD 4.2 AS warns if you try to .org backwards. We cannot because we * never know sub-segment sizes when we are reading code. * BSD will crash trying to emit -ve numbers of filler bytes in certain * .orgs. We don't crash, but see as-write for that code. */ segment = get_known_segmented_expression(&exp); if(*input_line_pointer == ','){ input_line_pointer ++; temp_fill = get_absolute_expression (); } else temp_fill = 0; if((segment != SEG_SECT || exp.X_add_symbol->sy_other != frchain_now->frch_nsect) && segment != SEG_ABSOLUTE) as_warn("Illegal expression. current section assumed."); p = frag_var(rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol, exp.X_add_number, (char *)0); *p = temp_fill; demand_empty_rest_of_line(); } #endif /* !defined(I860) i860 has it's own align and org */ /* * s_set() implements the pseudo op: * .set name , expression */ static void s_set( int value) { char *name; char delim; char *end_name; symbolS *symbolP; if( * input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; delim = get_symbol_end(); end_name = input_line_pointer; *end_name = delim; SKIP_WHITESPACE(); if(*input_line_pointer != ','){ *end_name = 0; as_warn("Expected comma after name \"%s\"", name); *end_name = delim; ignore_rest_of_line(); return; } input_line_pointer++; *end_name = 0; if(name[0] == '.' && name[1] == '\0'){ /* Turn 'set . , mumble' into a .org mumble */ segT segment; expressionS exp; char *ptr; segment = get_known_segmented_expression(&exp); if((segment != SEG_SECT || exp.X_add_symbol->sy_other != frchain_now->frch_nsect) && segment != SEG_ABSOLUTE) as_warn("Illegal expression. current section assumed."); ptr = frag_var(rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol, exp.X_add_number, (char *)0); *ptr = 0; *end_name = delim; return; } symbolP = symbol_find_or_make(name); *end_name = delim; pseudo_set(symbolP); demand_empty_rest_of_line(); } /* * s_abs() implements the pseudo op: * .abs name , expression * which sets symbol to 1 or 0 depending on if the expression is an absolute * expression. This is intended for use in macros. */ void s_abs( int value) { char *name; char c; char *p; segT segment; expressionS exp; symbolS *symbolP; if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); p = input_line_pointer; *p = c; SKIP_WHITESPACE(); if(*input_line_pointer != ','){ *p = 0; as_warn("Expected comma after name \"%s\"", name); *p = c; ignore_rest_of_line(); return; } input_line_pointer++; *p = 0; segment = expression(&exp); symbolP = symbol_find_or_make(name); symbolP->sy_type = N_ABS; symbolP->sy_other = 0; /* NO_SECT */ symbolP->sy_frag = &zero_address_frag; if(segment == SEG_ABSOLUTE) symbolP->sy_value = 1; else symbolP->sy_value = 0; *p = c; totally_ignore_line(); } /* * s_space() implements the pseudo op: * .space repeat_expression [ , fill_expression ] */ void s_space( int value) { long temp_repeat; long temp_fill; char *p; /* Just like .fill, but temp_size = 1 */ if(get_absolute_expression_and_terminator(&temp_repeat) == ','){ temp_fill = get_absolute_expression(); } else{ input_line_pointer--; /* Backup over what was not a ','. */ temp_fill = 0; } if(temp_repeat <= 0){ as_warn("Repeat < 0, .space ignored"); ignore_rest_of_line(); return; } p = frag_var(rs_fill, 1, 1, (relax_substateT)0, (symbolS *)0, temp_repeat, (char *)0); *p = temp_fill; demand_empty_rest_of_line(); } static unsigned long s_builtin_section( const struct builtin_section *s) { frchainS *frcP; if(!flagseen['k']){ if((s->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS || (s->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS || (s->flags & SECTION_TYPE) == S_SYMBOL_STUBS || (s->flags & SECTION_TYPE) == S_MOD_INIT_FUNC_POINTERS || (s->flags & SECTION_ATTRIBUTES) != 0) as_fatal("incompatible feature used: directive .%s (must " "specify -k to be used)", s->directive); } /* * If we allowed to use the new features that are incompatible with 3.2 * and this is the text section (which relys on the fact that the text * section is first in the built in sections list) then add the * S_ATTR_PURE_INSTRUCTIONS to the section attributes. */ if(flagseen['k'] && s == builtin_sections){ frcP = section_new(s->segname, s->sectname, s->flags & SECTION_TYPE, (s->flags & SECTION_ATTRIBUTES) | S_ATTR_PURE_INSTRUCTIONS, s->sizeof_stub); } else{ frcP = section_new(s->segname, s->sectname, s->flags & SECTION_TYPE, s->flags & SECTION_ATTRIBUTES, s->sizeof_stub); } if(frcP->frch_section.align < s->default_align) frcP->frch_section.align = s->default_align; return(frcP->frch_nsect); } /* * s_section() implements the pseudo op: * .section segname , sectname [[[ , type ] , attribute] , sizeof_stub] */ static void s_section( int value) { char *segname, *sectname, *typename; char c, d, e, *p, *q, *r; struct type_name *type_name; unsigned long type, attribute; struct section s; frchainS *frcP; unsigned long sizeof_stub; struct attribute_name *attribute_name; char *attributename, *sizeof_stub_name, f, g, *t, *u, *endp; segname = input_line_pointer; do{ c = *input_line_pointer++ ; }while(c != ',' && c != '\0' && c != '\n'); if(c != ','){ as_warn("Expected comma after segment-name"); ignore_rest_of_line(); return; } p = input_line_pointer - 1; sectname = input_line_pointer; do{ d = *input_line_pointer++ ; }while(d != ',' && d != '\0' && d != '\n'); if(p + 1 == input_line_pointer){ as_warn("Expected section-name after comma"); ignore_rest_of_line(); return; } q = input_line_pointer - 1; *p = 0; if(strlen(segname) > sizeof(s.segname)){ as_warn("segment-name: %s too long (maximum %ld characters)", segname, sizeof(s.segname)); ignore_rest_of_line(); *p = c; return; } *q = 0; if(strlen(sectname) > sizeof(s.sectname)){ as_warn("section-name: %s too long (maximum %ld characters)", sectname, sizeof(s.sectname)); ignore_rest_of_line(); return; } /* * Now see if the optional section type is present. */ type = 0; type_name = type_names; attribute = 0; attribute_name = attribute_names; sizeof_stub = 0; if(d == ','){ typename = input_line_pointer; do{ e = *input_line_pointer++ ; } while(e != ',' && e != '\0' && e != '\n'); r = input_line_pointer - 1; *r = 0; for(type_name = type_names; type_name->name != NULL; type_name++) if(strcmp(type_name->name, typename) == 0) break; if(type_name->name == NULL){ as_warn("unknown section type: %s", typename); ignore_rest_of_line(); return; } *r = e; type = type_name->type; /* * Now see if the optional section attribute is present. */ if(e == ','){ attributename = input_line_pointer; do{ f = *input_line_pointer++ ; }while(f != ',' && f != '\0' && f != '\n'); t = input_line_pointer - 1; *t = 0; for(attribute_name = attribute_names; attribute_name->name != NULL; attribute_name++) if(strcmp(attribute_name->name, attributename) == 0) break; if(attribute_name->name == NULL){ as_warn("unknown section attribute: %s", attributename); ignore_rest_of_line(); return; } *t = f; attribute = attribute_name->attribute; /* * Now get the section stub size if this is a stub section. */ if(type == S_SYMBOL_STUBS){ if(f == ','){ sizeof_stub_name = input_line_pointer; do{ g = *input_line_pointer++ ; }while(g != '\0' && g != '\n'); u = input_line_pointer - 1; *u = 0; sizeof_stub = strtoul(sizeof_stub_name, &endp, 0); if(*endp != '\0'){ as_warn("size of stub section: %s not a proper " "number", sizeof_stub_name); ignore_rest_of_line(); return; } *u = g; } else{ as_warn("missing size of stub section (%s,%s)", segname, sectname); ignore_rest_of_line(); return; } } } else if(type == S_SYMBOL_STUBS){ as_warn("missing size of stub section (%s,%s)", segname, sectname); ignore_rest_of_line(); return; } } input_line_pointer--; if(!flagseen['k']){ if(type == S_NON_LAZY_SYMBOL_POINTERS || type == S_LAZY_SYMBOL_POINTERS || type == S_SYMBOL_STUBS || type == S_MOD_INIT_FUNC_POINTERS) as_fatal("incompatible feature used: section type %s (must " "specify -k to be used)", type_name->name); if(attribute != 0) as_fatal("incompatible feature used: section attribute %s " "(must specify -k to be used)", attribute_name->name); } frcP = section_new(segname, sectname, type, attribute, sizeof_stub); *p = c; *q = d; demand_empty_rest_of_line(); } /* * s_zerofill() implements the pseudo op: * .zerofill segname , sectname [, symbolname , size_expression [ , align]] */ static void s_zerofill( int value) { char *segname, *sectname, c, d, *p, *q, *name; struct section s; frchainS *frcP; symbolS *symbolP; int size, align; segname = input_line_pointer; do{ c = *input_line_pointer++ ; }while(c != ',' && c != '\0' && c != '\n'); if(c != ','){ as_warn("Expected comma after segment-name"); ignore_rest_of_line(); return; } p = input_line_pointer - 1; sectname = input_line_pointer; do{ d = *input_line_pointer++ ; }while(d != ',' && d != '\0' && d != '\n'); if(p + 1 == input_line_pointer){ as_warn("Expected section-name after comma"); ignore_rest_of_line(); return; } q = input_line_pointer - 1; *p = 0; if(strlen(segname) > sizeof(s.segname)){ as_warn("segment-name: %s too long (maximum %ld characters)", segname, sizeof(s.segname)); ignore_rest_of_line(); *p = c; return; } *q = 0; if(strlen(sectname) > sizeof(s.sectname)){ as_warn("section-name: %s too long (maximum %ld characters)", sectname, sizeof(s.sectname)); ignore_rest_of_line(); *p = c; *q = d; return; } frcP = section_new(segname, sectname, S_ZEROFILL, 0, 0); if(frcP->frch_root == NULL){ frcP->frch_root = xmalloc(SIZEOF_STRUCT_FRAG); frcP->frch_last = frcP->frch_root; memset(frcP->frch_root, '\0', SIZEOF_STRUCT_FRAG); } *p = c; *q = d; /* * If this is the end of the line all that was wanted was to create the * the section which is now done, so return. */ if(d != ',') return; if(*input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); p = input_line_pointer; *p = c; SKIP_WHITESPACE(); if(*input_line_pointer != ','){ as_warn("Expected comma after symbol-name"); ignore_rest_of_line(); return; } input_line_pointer ++; if((size = get_absolute_expression()) < 0){ as_warn("zerofill size (%d.) <0! Ignored.", size); ignore_rest_of_line(); return; } align = 0; if(*input_line_pointer == ','){ input_line_pointer++; align = get_absolute_expression(); if(align > MAX_ALIGNMENT){ as_warn("Alignment too large: %d. assumed.", MAX_ALIGNMENT); align = MAX_ALIGNMENT; } else if(align < 0){ as_warn("Alignment negative. 0 assumed."); align = 0; } } *p = 0; symbolP = symbol_find_or_make(name); *p = c; if((symbolP->sy_type & N_TYPE) == N_UNDF && symbolP->sy_value == 0){ frcP->frch_root->fr_address = round(frcP->frch_root->fr_address, 1 << align); symbolP->sy_value = frcP->frch_root->fr_address; symbolP->sy_type = N_SECT | (symbolP->sy_type & N_EXT); symbolP->sy_other = frcP->frch_nsect; symbolP->sy_frag = frcP->frch_root; frcP->frch_root->fr_address += size; } else as_warn("Ignoring attempt to re-define symbol."); demand_empty_rest_of_line(); } /* * s_reference() implements the pseudo op: * .reference name */ static void s_reference( int value) { char *name; char c; char *p; symbolS *symbolP; if(* input_line_pointer == '"') name = input_line_pointer + 1; else name = input_line_pointer; c = get_symbol_end(); p = input_line_pointer; *p = 0; symbolP = symbol_find_or_make(name); *p = c; demand_empty_rest_of_line(); } /* * s_include() implements the pseudo op: * .include "filename" */ static void s_include( int value) { char *filename; int length; symbolS *symbolP; /* Some assemblers tolerate immediately following '"' */ if((filename = demand_copy_string( & length ) )) { demand_empty_rest_of_line(); read_an_include_file(filename); } /* * This is to generate stabs for debugging assembly code. * See the second comment about stabs in read_a_source_file() * for a description of what is going on here */ if(flagseen['g'] && frchain_now->frch_nsect == text_nsect){ symbolP = symbol_new( physical_input_file, 132 /* N_SOL */, text_nsect, 0, obstack_next_free(&frags) - frag_now->fr_literal, frag_now); } } /* * demand_empty_rest_of_line() checks to make sure we are at the end of a line * and if not ignores the rest of the line. * This is global so machine dependent pseudo-ops can use this. */ void demand_empty_rest_of_line( void) { SKIP_WHITESPACE(); if(is_end_of_line[(int)*input_line_pointer]) input_line_pointer++; else ignore_rest_of_line(); } /* * ignore_rest_of_line() advances input_line_pointer to the next line and if * there is anything left on the current line print a warning. * This is global so machine dependent pseudo-ops can use this. */ void ignore_rest_of_line( void) { if(!is_end_of_line[(int)*input_line_pointer]){ as_warn("Rest of line ignored. 1st junk character valued %d (%c).", *input_line_pointer, *input_line_pointer); while(input_line_pointer < buffer_limit && !is_end_of_line[(int)*input_line_pointer]) input_line_pointer++; } input_line_pointer++; /* Return pointing just after end-of-line. */ know(is_end_of_line[(int)(input_line_pointer[-1])]); } /* * stab() * * Handle .stabX directives, which used to be open-coded. * So much creeping featurism overloaded the semantics that we decided * to put all .stabX thinking in one place. Here. * * We try to make any .stabX directive legal. Other people's AS will often * do assembly-time consistency checks: eg assigning meaning to n_type bits * and "protecting" you from setting them to certain values. (They also zero * certain bits before emitting symbols. Tut tut.) * * If an expression is not absolute we either gripe or use the relocation * information. Other people's assemblers silently forget information they * don't need and invent information they need that you didn't supply. * * .stabX directives always make a symbol table entry. It may be junk if * the rest of your .stabX directive is malformed. */ static void stab( int what) /* d == .stabd, n == .stabn, and s == .stabs */ { symbolS *symbolP; char *string; int saved_type; int length; int goof; /* TRUE if we have aborted. */ long longint; saved_type = 0; /* * Enter with input_line_pointer pointing past .stabX and any following * whitespace. */ goof = FALSE; if(what == 's'){ string = demand_copy_C_string(&length); SKIP_WHITESPACE(); if(*input_line_pointer == ',') input_line_pointer ++; else{ as_warn("I need a comma after symbol's name"); goof = TRUE; } } else string = ""; /* * Input_line_pointer->after ','. String -> symbol name. */ if(!goof){ symbolP = symbol_new(string, 0,0,0,0,(struct frag *)0); switch(what){ case 'd': symbolP->sy_name = NULL; /* .stabd feature. */ symbolP->sy_value = obstack_next_free(&frags) - frag_now->fr_literal; symbolP->sy_frag = frag_now; break; case 'n': case 's': symbolP->sy_frag = &zero_address_frag; break; default: BAD_CASE( what ); break; } if(get_absolute_expression_and_terminator(&longint) == ','){ saved_type = longint; symbolP->sy_type = longint; } else{ as_warn("I want a comma after the n_type expression"); goof = TRUE; input_line_pointer--; /* Backup over a non-',' char. */ } } if(!goof){ if(get_absolute_expression_and_terminator(&longint) == ',') symbolP->sy_other = longint; else { as_warn("I want a comma after the n_other expression"); goof = TRUE; input_line_pointer--; /* Backup over a non-',' char. */ } } if(!goof){ symbolP->sy_desc = get_absolute_expression(); if(what == 's' || what == 'n'){ if(*input_line_pointer != ','){ as_warn( "I want a comma after the n_desc expression" ); goof = TRUE; } else input_line_pointer ++; } } if((!goof) && (what=='s' || what=='n')){ pseudo_set(symbolP); symbolP->sy_type = saved_type; } else if(!goof){ /* for stabd the sy_other (n_sect) gets set to the current section*/ symbolP->sy_other = frchain_now->frch_nsect; } if(goof) ignore_rest_of_line(); else demand_empty_rest_of_line(); } /* * pseudo_set() * * In: Pointer to a symbol. * Input_line_pointer -> expression. * * Out: Input_line_pointer -> just after any whitespace after expression. * Tried to set symbol to value of expression. * Will change sy_type, sy_value, sy_frag; *(old ->> May set need_pass_2 == TRUE. <<-- commented out by GNU below it * uses symbolP->sy_forward = exp.X_add_symbol;) */ static void pseudo_set( symbolS *symbolP) { expressionS exp; segT segment; int ext; know(symbolP); /* NULL pointer is logic error. */ ext = (symbolP->sy_type & N_EXT); segment = expression(&exp); switch(segment){ case SEG_BIG: as_warn("%s number illegal. Absolute 0 assumed.", exp.X_add_number > 0 ? "Bignum" : "Floating-Point"); symbolP->sy_type = N_ABS | ext; symbolP->sy_other = 0; /* NO_SECT */ symbolP->sy_value = 0; symbolP->sy_frag = &zero_address_frag; break; case SEG_NONE: as_warn("No expression: Using absolute 0"); symbolP->sy_type = N_ABS | ext; symbolP->sy_other = 0; /* NO_SECT */ symbolP->sy_value = 0; symbolP->sy_frag = &zero_address_frag; break; case SEG_DIFFSECT: if(exp.X_add_symbol && exp.X_subtract_symbol && exp.X_add_symbol->sy_other == exp.X_subtract_symbol->sy_other) exp.X_add_number += exp.X_add_symbol->sy_value - exp.X_subtract_symbol->sy_value; else as_warn("Complex expression. Absolute segment assumed." ); /* fall through */ case SEG_ABSOLUTE: symbolP->sy_type = N_ABS | ext; symbolP->sy_other = 0; /* NO_SECT */ symbolP->sy_value = exp.X_add_number; symbolP->sy_frag = &zero_address_frag; break; case SEG_SECT: symbolP->sy_type = N_SECT | ext; symbolP->sy_other = exp.X_add_symbol->sy_other; symbolP->sy_value = exp.X_add_number + exp.X_add_symbol->sy_value; symbolP->sy_frag = exp.X_add_symbol->sy_frag; break; case SEG_UNKNOWN: symbolP->sy_forward = exp.X_add_symbol; /* commented out by GNU */ /* as_warn("unknown symbol"); */ /* need_pass_2 = TRUE; */ break; default: BAD_CASE(segment); break; } } /* * cons() * * CONStruct more frag of .bytes, or .words etc. * This understands EXPRESSIONS, as opposed to big_cons(). * * Bug (?) * * This has a split personality. We use expression() to read the * value. We can detect if the value won't fit in a byte or word. * But we can't detect if expression() discarded significant digits * in the case of a long. Not worth the crocks required to fix it. * * Worker function to do .byte, .short, .long, statements. * This clobbers input_line_pointer, checks end-of-line. */ void cons( int nbytes) /* nbytes == 1 for .byte, 2 for .word, 4 for .long */ { char c; long mask; /* high-order bits to truncate */ long unmask; /* what bits we will store */ long get; /* the bits of the expression we get */ long use; /* the bits of the expression after truncation */ char *p; /* points into the frag */ segT segment; expressionS exp; /* * Input_line_pointer -> 1st char after pseudo-op-code and could legally * be a end-of-line. (Or, less legally an eof - which we cope with.) */ if(nbytes >= sizeof(long int)) mask = 0; else mask = ~0 << (BITS_PER_CHAR * nbytes); /* Don't store these bits. */ unmask = ~mask; /* Do store these bits. */ /* * The following awkward logic is to parse ZERO or more expressions, * comma seperated. Recall an expression includes its leading & * trailing blanks. We fake a leading ',' if there is (supposed to * be) a 1st expression, and keep demanding 1 expression for each ','. */ if(is_it_end_of_statement()){ c = 0; /* Skip loop. */ input_line_pointer++; /* Matches end-of-loop 'correction'. */ } else c = ','; /* Do loop. */ while(c == ','){ segment = expression(&exp); /* At least scan over the expression */ if(segment == SEG_DIFFSECT && exp.X_add_symbol == NULL){ as_warn("Subtracting symbol \"%s\"(segment\"%s\") is too " "hard. Absolute segment assumed.", exp.X_subtract_symbol->sy_name, seg_name[(int)N_TYPE_seg[ exp.X_subtract_symbol->sy_type & N_TYPE]]); segment = SEG_ABSOLUTE; /* Leave exp .X_add_number alone. */ } p = frag_more(nbytes); switch(segment){ case SEG_BIG: as_warn("%s number illegal. Absolute 0 assumed.", exp.X_add_number > 0 ? "Bignum" : "Floating-Point"); md_number_to_chars(p, (long)0, nbytes); break; case SEG_NONE: as_warn("0 assumed for missing expression"); exp.X_add_number = 0; know(exp.X_add_symbol == NULL); /* fall into SEG_ABSOLUTE */ case SEG_ABSOLUTE: get = exp.X_add_number; use = get & unmask; if((get & mask) && (get & mask) != mask){ /* Leading bits contain both 0s & 1s. */ as_warn("Value x%x truncated to x%x.", (unsigned int)get, (unsigned int)use); } /* put bytes in right order. */ md_number_to_chars(p, use, nbytes); break; case SEG_DIFFSECT: case SEG_UNKNOWN: case SEG_SECT: fix_new(frag_now, p - frag_now->fr_literal, nbytes, exp.X_add_symbol, exp.X_subtract_symbol, exp.X_add_number, 0, 0, 0); break; default: BAD_CASE(segment); break; } /* switch(segment) */ c = *input_line_pointer++; } /* while(c==',') */ input_line_pointer--; /* Put terminator back into stream. */ demand_empty_rest_of_line(); } #ifdef M68K /* we allow big cons only on the 68k machines */ /* * big_cons() * * CONStruct more frag(s) of .quads, or .octa etc. * Makes 0 or more new frags. * This understands only bignums, not expressions. Cons() understands * expressions. * * Constants recognised are '0...'(octal) '0x...'(hex) '...'(decimal). * * This creates objects with struct obstack_control objs, destroying * any context objs held about a partially completed object. Beware! * * * I think it sucks to have 2 different types of integers, with 2 * routines to read them, store them etc. * It would be nicer to permit bignums in expressions and only * complain if the result overflowed. However, due to "efficiency"... * * Worker function to do .quad and .octa statements. * This clobbers input_line_pointer, checks end-of-line. */ void big_cons( int nbytes) /* 8 == .quad, 16 == .octa ... */ { char c; /* input_line_pointer -> c. */ int radix; long length;/* Number of chars in an object. */ int digit; /* Value of 1 digit. */ int carry; /* For multi-precision arithmetic. */ int work; /* For multi-precision arithmetic. */ char *p,*q; /* For multi-precision arithmetic. */ int i; /* * The following awkward logic is to parse ZERO or more strings, * comma seperated. Recall an expression includes its leading & * trailing blanks. We fake a leading ',' if there is (supposed to * be) a 1st expression, and keep demanding 1 expression for each ','. */ if(is_it_end_of_statement()){ c = 0; /* Skip loop. */ } else{ c = ','; /* Do loop. */ --input_line_pointer; } while(c == ','){ ++input_line_pointer; SKIP_WHITESPACE(); c = *input_line_pointer; /* c contains 1st non-blank char of what we hope is a number */ if(c == '0'){ c = *++input_line_pointer; if(c == 'x' || c=='X'){ c = *++input_line_pointer; radix = 16; } else{ radix = 8; } } else{ radix = 10; } /* * This feature (?) is here to stop people worrying about * mysterious zero constants: which is what they get when * they completely omit digits. */ if(hex_value[(int)c] >= radix){ as_warn("Missing digits. 0 assumed."); } bignum_high = bignum_low - 1; /* Start constant with 0 chars. */ for( ; (digit = hex_value[(int)c]) < radix; c = *++input_line_pointer){ /* Multiply existing number by radix, then add digit. */ carry = digit; for(p = bignum_low; p <= bignum_high; p++){ work = (*p & MASK_CHAR) * radix + carry; *p = work & MASK_CHAR; carry = work >> BITS_PER_CHAR; } if(carry){ grow_bignum(); *bignum_high = carry & MASK_CHAR; know((carry & ~ MASK_CHAR) == 0); } } length = bignum_high - bignum_low + 1; if(length > nbytes){ as_warn("Most significant bits truncated in integer constant."); } else{ long leading_zeroes; for(leading_zeroes = nbytes - length; leading_zeroes; leading_zeroes--){ grow_bignum(); *bignum_high = 0; } } p = frag_more(nbytes); if(md_target_byte_sex == BIG_ENDIAN_BYTE_SEX){ q = (char *)bignum_low; for(i = nbytes - 1; i >= 0; i--) *p++ = q[i]; } else{ memcpy(p, bignum_low, (int)nbytes); } /* C contains character after number. */ SKIP_WHITESPACE(); c = *input_line_pointer; /* C contains 1st non-blank character after number. */ } demand_empty_rest_of_line(); } /* * grow_bignum() extends bignum (that is adjust bignum_low, bignum_high and * bignum_limit). */ static void grow_bignum( void) { long length; bignum_high++; if(bignum_high >= bignum_limit) { length = bignum_limit - bignum_low; bignum_low = xrealloc(bignum_low, length + length); bignum_high = bignum_low + length; bignum_limit = bignum_low + length + length; } } #endif /* M68K we allow big cons only on the 68k machines */ /* * float_cons() * * CONStruct some more frag chars of .floats .ffloats etc. * Makes 0 or more new frags. * This understands only floating literals, not expressions. Sorry. * * A floating constant is defined by atof_generic(), except it is preceded * by 0d 0f 0g or 0h. After observing the STRANGE way my BSD AS does its * reading, I decided to be incompatible. This always tries to give you * rounded bits to the precision of the pseudo-op. Former AS did premature * truncatation, restored noisy bits instead of trailing 0s AND gave you * a choice of 2 flavours of noise according to which of 2 floating-point * scanners you directed AS to use. * * In: input_line_pointer -> whitespace before, or '0' of flonum. * * Worker function to do .double, .float, .single statements. * This clobbers input_line-pointer, checks end-of-line. */ void float_cons( int float_type) /* 'f':.ffloat ... 'F':.float ... */ { char *p; char c; int length; /* Number of chars in an object. */ char *err; /* Error from scanning floating literal. */ char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT]; /* * The following awkward logic is to parse ZERO or more strings, * comma seperated. Recall an expression includes its leading & * trailing blanks. We fake a leading ',' if there is (supposed to * be) a 1st expression, and keep demanding 1 expression for each ','. */ if(is_it_end_of_statement()){ c = 0; /* Skip loop. */ ++input_line_pointer; /* -> past termintor. */ } else{ c = ','; /* Do loop. */ } while(c == ','){ /* input_line_pointer -> 1st char of a flonum (we hope!). */ SKIP_WHITESPACE(); /* * Skip any 0{letter} that may be present. Don't even check if the * letter is legal. Someone may invent a "z" format and this routine * has no use for such information. Lusers beware: you get * diagnostics if your input is ill-conditioned. */ if(input_line_pointer[0] == '0' && isalpha(input_line_pointer[1])) input_line_pointer+=2; err = md_atof(float_type, temp, &length); know(length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT); know(length > 0); if(*err != '\0'){ as_warn( "Bad floating literal: %s", err); ignore_rest_of_line(); /* Input_line_pointer -> just after end-of-line. */ c = 0; /* Break out of loop. */ } else{ p = frag_more(length); memcpy(p, temp, length); SKIP_WHITESPACE(); c = *input_line_pointer ++; /* C contains 1st non-white character after number. */ /* input_line_pointer -> just after terminator (c). */ } } --input_line_pointer; /* -> terminator (is not ','). */ demand_empty_rest_of_line(); } /* * stringer() * * We read 0 or more ',' seperated, double-quoted strings. * * Worker function to do .ascii etc statements. * Checks end-of-line. */ void stringer( int append_zero) /* 0: don't append '\0', else 1 */ { int c; /* * The following awkward logic is to parse ZERO or more strings, * comma seperated. Recall a string expression includes spaces * before the opening '\"' and spaces after the closing '\"'. * We fake a leading ',' if there is (supposed to be) * a 1st, expression. We keep demanding expressions for each * ','. */ if(is_it_end_of_statement()){ c = 0; /* Skip loop. */ ++ input_line_pointer; /* Compensate for end of loop. */ } else{ c = ','; /* Do loop. */ } for( ; c == ','; c = *input_line_pointer++){ SKIP_WHITESPACE(); if(*input_line_pointer == '\"'){ ++input_line_pointer; /* -> 1st char of string. */ while((c = next_char_of_string()) >= 0){ FRAG_APPEND_1_CHAR(c); } if(append_zero){ FRAG_APPEND_1_CHAR(0); } know(input_line_pointer[-1] == '\"'); } else{ as_warn("Expected \"-ed string"); } SKIP_WHITESPACE(); } --input_line_pointer; demand_empty_rest_of_line(); } /* * next_char_of_string() is used by stringer() and demand_copy_string() and * returns the next character from input_line_pointer that is in the string or * -1 for the trailing " character. This routine handles escaped characters * like \b, \f, etc. */ static int next_char_of_string( void) { int c; long number, i; c = *input_line_pointer++; /* make sure the 0xff char is not returned as -1 */ c = (c & MASK_CHAR); switch(c){ case '\"': c = -1; break; case '\\': c = *input_line_pointer++; switch(c){ case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case '\\': case '"': break; /* As itself. */ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': for(i = 0, number = 0; i < 3 && isdigit(c) && c < '8'; i++, c = *input_line_pointer++) number = number * 8 + c - '0'; c = number; --input_line_pointer; break; case '\n': /* To be compatible with BSD 4.2 as: give the user a linefeed */ c = '\n'; break; default: as_warn( "Bad escaped character in string, '?' assumed" ); c = '?'; break; } break; default: break; } return(c); } /* * get_segmented_expression() is passed an expression to fill in and return that * is anything except a bignum or a missing expression. */ static segT get_segmented_expression( expressionS *expP) { segT retval; retval = expression(expP); if(retval == SEG_NONE || retval == SEG_BIG){ as_warn("Expected address expression: absolute 0 assumed"); retval = expP->X_seg = SEG_ABSOLUTE; expP->X_add_number = 0; expP->X_add_symbol = NULL; expP->X_subtract_symbol = NULL; } return(retval); /* SEG_ ABSOLUTE,UNKNOWN,SECT */ } /* * get_known_segmented_expression() is passed an expression to fill in and * return that is anything except an unknown, bignum or a missing expression. */ segT get_known_segmented_expression( expressionS *expP) { segT retval; char *name1; char *name2; retval = get_segmented_expression(expP); if(retval == SEG_UNKNOWN){ name1 = expP->X_add_symbol ? expP->X_add_symbol->sy_name : ""; name2 = expP->X_subtract_symbol ? expP->X_subtract_symbol->sy_name : ""; if(name1 && name2){ as_warn("Symbols \"%s\" \"%s\" are undefined: absolute 0 " "assumed.", name1, name2); } else{ as_warn("Symbol \"%s\" undefined: absolute 0 assumed.", name1 ? name1 : name2); } retval = SEG_ABSOLUTE; expP->X_seg = SEG_ABSOLUTE; expP->X_add_number = 0; expP->X_add_symbol = NULL; expP->X_subtract_symbol = NULL; } know(retval == SEG_ABSOLUTE || retval == SEG_SECT || retval == SEG_DIFFSECT); return(retval); } /* * get_absolute_expression() gets an absolute expression and returns the value * of that expression. */ long get_absolute_expression( void) { expressionS exp; segT s; s = expression(&exp); if(s != SEG_ABSOLUTE){ /* is this right? if not absolute: no message and return 0 */ if(s != SEG_NONE){ as_warn("Bad Absolute Expression, absolute 0 assumed."); } exp.X_add_number = 0; } return(exp.X_add_number); } /* * get_absolute_expression_and_terminator() gets an absolute expression and * returning the value of that expression indirectly through val_pointer and * returns the terminator. */ static char /* return terminator */ get_absolute_expression_and_terminator( long *val_pointer) /* return value of expression */ { *val_pointer = get_absolute_expression(); return(*input_line_pointer++); } /* * demand_copy_C_string() * * Like demand_copy_string, but return NULL if the string contains any '\0's. * Give a warning if that happens. */ static char * demand_copy_C_string( int *len_pointer) { char *s; int len; if((s = demand_copy_string(len_pointer))){ for(len = *len_pointer; len > 0; len--){ if(*s == '\0'){ s = 0; len = 1; *len_pointer = 0; as_warn("This string may not contain \'\\0\'"); } } } return(s); } /* * demand_copy_string() * * Demand string, but return a safe (=private) copy of the string. * Return NULL if we can't read a string here. */ static char * demand_copy_string( int *lenP) { int c; int len; char *retval; len = 0; SKIP_WHITESPACE(); if(*input_line_pointer == '\"'){ input_line_pointer++; /* Skip opening quote. */ while((c = next_char_of_string()) >= 0){ obstack_1grow(¬es, c); len++; } /* * This next line is so demand_copy_C_string will return a null * termanated string. */ obstack_1grow(¬es, '\0'); retval = obstack_finish(¬es); } else{ as_warn("Missing string"); retval = NULL; ignore_rest_of_line(); } *lenP = len; return(retval); } /* * is_it_end_of_statement() * * In: Input_line_pointer -> next character. * * Do: Skip input_line_pointer over all whitespace. * * Out: TRUE if input_line_pointer -> end-of-line. */ static int is_it_end_of_statement( void) { SKIP_WHITESPACE(); return(is_end_of_line[(int)*input_line_pointer]); } /* * equals() implements the assembly statement: * x = expression */ static void equals( char *sym_name) { struct symbol *symbolP; segT segment; expressionS exp; char *p; /* Turn '. = mumble' into a .org mumble */ if(sym_name[0]=='.' && sym_name[1]=='\0'){ if(input_line_pointer[1] == '=') input_line_pointer += 2; else *input_line_pointer++ = '='; /* Put it back */ if(*input_line_pointer==' ' || *input_line_pointer=='\t') input_line_pointer++; segment = get_known_segmented_expression(&exp); if((segment != SEG_SECT || exp.X_add_symbol->sy_other != frchain_now->frch_nsect) && segment != SEG_ABSOLUTE) as_warn("Illegal expression. current section assumed."); p = frag_var(rs_org, 1, 1, (relax_substateT)0, exp.X_add_symbol, exp.X_add_number, (char *)0); *p = 0; return; } symbolP = symbol_find_or_make(sym_name); if(input_line_pointer[1] == '=') input_line_pointer += 2; else *input_line_pointer++ = '='; /* Put it back */ if(*input_line_pointer==' ' || *input_line_pointer=='\t') input_line_pointer++; pseudo_set(symbolP); } /* * s_if() implements the pseudo op: * .if expression * that does conditional assembly using assembler defined expressions. */ static void s_if( int value) { if(if_depth >= MAX_IF_DEPTH) as_fatal("You can't nest if's more than %d levels deep", MAX_IF_DEPTH); last_states[if_depth++] = the_cond_state; the_cond_state.the_cond = if_cond; if(the_cond_state.ignore) totally_ignore_line(); else{ the_cond_state.cond_met = get_absolute_expression(); the_cond_state.ignore = !the_cond_state.cond_met; demand_empty_rest_of_line(); } } /* * s_elseif() implements the pseudo op: * .elseif expression * that does conditional assembly using assembler defined expressions. */ static void s_elseif( int value) { int last_ignore_state; if(the_cond_state.the_cond != if_cond && the_cond_state.the_cond != elseif_cond) as_fatal("Encountered a .elseif that doesn't follow a .if or an " ".elseif"); the_cond_state.the_cond = elseif_cond; last_ignore_state = FALSE; if(if_depth) last_ignore_state = last_states[if_depth-1].ignore; if(last_ignore_state || the_cond_state.cond_met){ the_cond_state.ignore = TRUE; totally_ignore_line(); } else{ the_cond_state.cond_met = get_absolute_expression(); the_cond_state.ignore = !the_cond_state.cond_met; demand_empty_rest_of_line(); } } /* * s_else() implements the pseudo op: * .else * that does conditional assembly using assembler defined expressions. */ static void s_else( int value) { int last_ignore_state; if(the_cond_state.the_cond != if_cond && the_cond_state.the_cond != elseif_cond) as_fatal("Encountered a .else that doesn't follow a .if or an " ".elseif"); the_cond_state.the_cond = else_cond; last_ignore_state = FALSE; if(if_depth) last_ignore_state = last_states[if_depth-1].ignore; if(last_ignore_state || the_cond_state.cond_met) the_cond_state.ignore = TRUE; else the_cond_state.ignore = FALSE; demand_empty_rest_of_line(); } /* * s_endif() implements the pseudo op: * .endif * that does conditional assembly using assembler defined expressions. */ static void s_endif( int value) { if((the_cond_state.the_cond == no_cond) || (if_depth == 0)) as_fatal("Encountered a .endif that doesn't follow a .if or .else"); the_cond_state = last_states[--if_depth]; demand_empty_rest_of_line(); } /* * totally_ignore_line() ignores lines during conditional assembly. */ void totally_ignore_line( void) { if(!is_end_of_line[(int)*input_line_pointer]){ while(input_line_pointer < buffer_limit && !is_end_of_line[(int)*input_line_pointer]){ input_line_pointer ++; } } input_line_pointer++; /* Return pointing just after end-of-line. */ know(is_end_of_line[(int)(input_line_pointer[-1])]); } /* * s_macros_on() implements the pseudo op: * .macros_on */ static void s_macros_on( int value) { macros_on = TRUE; demand_empty_rest_of_line(); } /* * s_macros_off() implements the pseudo op: * .macros_off */ void s_macros_off( int value) { macros_on = FALSE; demand_empty_rest_of_line(); } /* * s_macro() implements the pseudo op: * .macro macro_name * that defines a macro. */ void s_macro( int value) { int c; pseudo_typeS *pop; if(macro_name) as_warn("Can't define a macro inside another macro definition"); else{ SKIP_WHITESPACE(); while(is_part_of_name(c = *input_line_pointer ++)) obstack_1grow (¯os, c); obstack_1grow(¯os, '\0'); --input_line_pointer; macro_name = obstack_finish(¯os); if(macro_name == "") as_warn("Missing name of macro"); if(*macro_name == '.'){ pop = (pseudo_typeS *)hash_find(po_hash, macro_name + 1); if(pop != NULL) as_warn("Pseudo-op name: %s can't be a macro name", macro_name); } } totally_ignore_line(); } /* * s_endmacro() implements the pseudo op: * .endmacro * which is the end of a macro definition. */ void s_endmacro( int value) { char *errorString; if(!macro_name){ as_warn ("This .endmacro does not match with a preceeding .macro"); ignore_rest_of_line(); } else{ obstack_1grow(¯os, '\0'); errorString = hash_insert(ma_hash, macro_name, obstack_finish(¯os)); if(*errorString) as_warn("The macro named \"%s\" is already defined", macro_name); macro_name = FALSE; } } /* * macro_begin() initializes macros. */ static void macro_begin( void) { ma_hash = hash_new(); obstack_begin(¯os, 5000); } /* * add_to_macro_definition() is called after a .macro to store the contents of * a macro into the obstack. */ void add_to_macro_definition( char *char_pointer) { char c; do{ c = *char_pointer ++; know(c != '\0'); obstack_1grow(¯os, c); }while((c != ':') && !(is_end_of_line[(int)c])); if(char_pointer > input_line_pointer) input_line_pointer = char_pointer; } /* * expand_macro() is called to expand macros. */ static void expand_macro( char *macro_contents) { char *buffer; char c; int index, nargs; char *last_buffer_limit; int last_count_lines; char *last_input_line_pointer; char *arguments [10]; /* at most 10 arguments, each is substituted */ if(macro_depth >= MAX_MACRO_DEPTH) as_fatal("You can't nest macro's more than %d levels deep", MAX_MACRO_DEPTH); macro_depth++; /* copy each argument to a object in the macro obstack */ nargs = 0; for(index = 0; index < 10; index ++){ if(*input_line_pointer == ' ') ++input_line_pointer; know(*input_line_pointer != ' '); c = *input_line_pointer; if(is_end_of_line[(int)c]) arguments[index] = NULL; else{ int parenthesis_depth = 0; do{ c = *input_line_pointer++; if(parenthesis_depth){ if(c == ')') parenthesis_depth --; } else{ if(c == '(') parenthesis_depth ++; else if(is_end_of_line[(int)c] || (c == ' ') || (c == ',')) break; } know(c != '\0'); if(is_end_of_line[(int)c]) as_warn("missmatched parenthesis"); obstack_1grow(¯os, c); }while(1); obstack_1grow(¯os, '\0'); arguments[index] = obstack_finish(¯os); nargs++; if(is_end_of_line[(int)c]) --input_line_pointer; else if(c == ' ') if(*input_line_pointer == ',') input_line_pointer++; } } if(!is_end_of_line[(int)c]){ as_warn("More than 10 arguments not allowed for macros"); ignore_rest_of_line(); } /* * Build a buffer containing the macro contents with arguments * substituted */ obstack_1grow(¯os, '\n'); while((c = *macro_contents++)){ if(c == '$'){ if((*macro_contents >= '0') && (*macro_contents <= '9')){ index = *macro_contents++ - '0'; last_input_line_pointer = macro_contents; macro_contents = arguments[index]; if(macro_contents){ while ((c = * macro_contents ++)) obstack_1grow (¯os, c); } macro_contents = last_input_line_pointer; continue; } else if (*macro_contents == 'n'){ macro_contents++ ; obstack_1grow(¯os, nargs + '0'); continue; } } obstack_1grow (¯os, c); } obstack_1grow (¯os, '\n'); obstack_1grow (¯os, '\0'); last_buffer_limit = buffer_limit; last_count_lines = count_lines; last_input_line_pointer = input_line_pointer; buffer_limit = obstack_next_free (¯os) - 1; buffer = obstack_finish (¯os); count_lines = FALSE; /* printf("expanded macro: %s", buffer + 1); */ parse_a_buffer(buffer + 1); obstack_free (¯os, buffer); for(index = 9; index >= 0; index --) if(arguments[index]) obstack_free(¯os, arguments[index]); buffer_limit = last_buffer_limit; count_lines = last_count_lines; input_line_pointer = last_input_line_pointer; macro_depth--; } /* * s_dump() implements the pseudo op: * .dump filename * that does a quick binary dump of symbol tables. */ static void s_dump( int value) { char *filename; int length; static char null_string[] = ""; if((filename = demand_copy_string(&length))){ demand_empty_rest_of_line(); if((dump_fp = fopen(filename, "w+"))){ hash_apply(ma_hash, write_macro); fwrite(null_string, 1, 1, dump_fp); hash_apply(sy_hash, write_symbol); fwrite(null_string, 1, 1, dump_fp); fclose(dump_fp); } else as_warn("couldn't write to dump file: \"%s\"", filename); } } /* * write_macro() used by hash_apply indirectly through s_dump() to write one * macro. */ static char * write_macro( char *string, char *value) { know(string); know(value); know(strlen(string)); fwrite(string, (strlen(string) + 1), 1, dump_fp); fwrite(value, (strlen(value) + 1), 1, dump_fp); return(NULL); } /* * write_symbol() used by hash_apply indirectly through s_dump() to write one * N_ABS symbol and its value. */ static char * write_symbol( char *string, char *value) { symbolS *symbolP; symbolP = (symbolS *)value; know(symbolP); if(((symbolP->sy_type) & N_TYPE) == N_ABS){ know(string); know(strlen(string)); fwrite(string, (strlen(string) + 1), 1, dump_fp); fwrite(&(symbolP -> sy_value), 4, 1, dump_fp); } return(NULL); } /* * s_load() implements the pseudo op: * .load filename * that does a quick binary load of symbol tables. */ static void s_load( int value) { char *char_pointer; char *filename; int length; char the_char; symbolS *the_symbol; symbolS *temp_symbol_lastP; static symbolS *dump_symbol_lastP; if((filename = demand_copy_string(&length))){ demand_empty_rest_of_line(); if((dump_fp = fopen(filename, "r+"))){ do{ do{ the_char = getc(dump_fp); obstack_1grow(¯os, the_char); }while(the_char); char_pointer = obstack_finish (¯os); if(!(*char_pointer)) break; do{ the_char = getc(dump_fp); obstack_1grow(¯os, the_char); }while(the_char); if(*hash_insert(ma_hash, char_pointer, obstack_finish(¯os))) as_warn("a macro named \"%s\" encountered in a .load " "is already defined", char_pointer); }while(1); /* * We don't want to link in symbols that were loaded so they * don't go out in the object file. Instead these symbols * should go out in the object file that did the .dump . */ temp_symbol_lastP = symbol_lastP; symbol_lastP = dump_symbol_lastP; do{ do{ the_char = getc(dump_fp); obstack_1grow(¯os, the_char); }while(the_char); char_pointer = obstack_base(¯os); obstack_next_free(¯os) = char_pointer; if(!(*char_pointer)) break; the_symbol = symbol_find_or_make(char_pointer); the_symbol->sy_type = N_ABS; char_pointer = (char *)&the_symbol->sy_value; *char_pointer++ = getc(dump_fp); *char_pointer++ = getc(dump_fp); *char_pointer++ = getc(dump_fp); *char_pointer++ = getc(dump_fp); the_symbol->sy_frag = &zero_address_frag; }while(1); dump_symbol_lastP = symbol_lastP; symbol_lastP = temp_symbol_lastP; fclose(dump_fp); } else as_fatal("Couldn't find the dump file: \"%s\"", filename); } }
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.