/* Windows Put Interface */ /* Copyright (C) 2001 Analog Devices Inc., All Rights Reserved. ** This contains Analog Devices Background IP and Development IP as ** defined in the ADI/Intel Collaboration Agreement. ** ADI/Intel Confidential */ /* This module provides functions for generating a Windows assembler or object * file from the e-machine. For assembler files, the code within functions * is output from within the e-machine but all surrounding assembler * directives are dealt with by this module. For object files this is * similar except that the e-machine fills up this module's code area * with bytes representing the machine code produced, and more work has to * be done to deal with relocations and symbol tables. * * This put interface is derived from previous put interfaces written for * different object file formats. It has been written from scratch, * removing a lot of redundant code from the compiler, and now adds support * for generating assembler files. It is intended that this file will not * have to rely on include files specific to a particular file format and * so all such definitions are declared locally in this file. It is also * intended that the format of output file produced will be switchable at * run-time, so no conditional compilation should exist. * * The put interface functions expect sizes and alignments to be passed as * bytes, whether they be for a byte-addressed target architecture or a * word-addressed target architecture. The sizes and alignments will be * converted accordingly when the output file is written. * * Graeme Roy (3rd September, 2001) */ #define __EXTENSIONS__ #include #include #include #include #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include "put.h" #define SECTION_STACK_SIZE 64 /* maximum items on section stack */ #define SCOPE_STACK_SIZE 64 /* maximum items on scope stack */ #define BIG_ENDIAN 0 /* architecture is big-endian */ #define LITTLE_ENDIAN 1 /* architecture is little-endian */ #define DEBUG_COFF 2 /* debugging format is COFF */ #define DEBUG_CODEVIEW 3 /* debugging format is CodeView */ #define SECT_READONLY 1 /* section contents are read-only */ #define SECT_EXECUTABLE 2 /* section contents may be executed */ #define SECT_DEBUG 4 /* section contents contain debugging info */ #define DIRECT_ANY 0 /* any directive */ #define DIRECT_EXPORT 1 /* export directive */ #define DIRECT_LIBRARY 2 /* library directive */ #define TARGET_386 25 /* target processor is i386 */ #define COFF_ARCH_386 0x014C /* i386-specific object file */ #define COFF_SECT_CODE 0x0020 /* section contents contain code */ #define COFF_SECT_DATA 0x0040 /* section contents contain data */ #define COFF_SECT_BSS 0x0080 /* section contents occupy no file space */ #define COFF_SECT_INFO 0x0200 /* section contents contain comments */ #define COFF_SECT_REMOVE 0x0800 /* section contents should be removed */ #define COFF_SECT_DISCARD 0x02000000 /* section contents can be discarded */ #define COFF_SECT_EXEC 0x20000000 /* section contents may be executed */ #define COFF_SECT_READ 0x40000000 /* section contents are readable */ #define COFF_SECT_WRITE 0x80000000 /* section contents are writable */ #define COFF_SYMBOL_ABS 0xFFFF /* symbol is not affected by relocation */ #define COFF_SYMBOL_DEBUG 0xFFFE /* symbol is a debug symbol */ #define COFF_SYMBOL_OBJECT 0x00 /* symbol represents an object */ #define COFF_SYMBOL_FUNC 0x20 /* symbol represents a function */ #define COFF_SYMBOL_GLOBAL 2 /* symbol has global visibility */ #define COFF_SYMBOL_LOCAL 3 /* symbol has local visibility */ #define COFF_SYMBOL_FUNCAUX 101 /* symbol is function auxilliary */ #define COFF_SYMBOL_FILE 103 /* symbol has file visibility */ #define COFF_SYMBOL_WEAK 105 /* symbol has weak visibility */ #define COFF_REL_386_DIR32 6 /* i386-specific relocation type */ #define COFF_REL_386_SEC 10 /* i386-specific relocation type */ #define COFF_REL_386_SECREL 11 /* i386-specific relocation type */ #define COFF_REL_386_REL32 20 /* i386-specific relocation type */ /* available sections */ typedef enum section_type { sec_null, sec_text, sec_init, sec_fini, sec_data, sec_tls, sec_bss, sec_rodata, sec_gdt, sec_frt, sec_edt, sec_cht, sec_directive, sec_comment, sec_debsym, sec_debtyp, sec_num } section_type; /* structure containing information about each section */ typedef struct section_info { char *name; size_t index; size_t size; size_t targets; size_t relocs; size_t lines; size_t align; size_t default_align; unsigned long flags; } section_info; /* table containing information about each section */ static section_info section_info_table[sec_num] = { {"", 0xFFFFFFFF, 0, 0, 0, 0, 0, 0, 0}, {".text", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_CODE | COFF_SECT_EXEC | COFF_SECT_READ}, {".init", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_CODE | COFF_SECT_EXEC | COFF_SECT_READ}, {".fini", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_CODE | COFF_SECT_EXEC | COFF_SECT_READ}, {".data", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_DATA | COFF_SECT_READ | COFF_SECT_WRITE}, {".tls", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_DATA | COFF_SECT_READ | COFF_SECT_WRITE}, {".bss", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_BSS | COFF_SECT_READ | COFF_SECT_WRITE}, {".rdata", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_DATA | COFF_SECT_READ}, {".ptabs", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_DATA | COFF_SECT_READ | COFF_SECT_WRITE}, {".xtabs$1", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_DATA | COFF_SECT_READ}, {".xtabs$2", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_DATA | COFF_SECT_READ}, {".xtabs$3", 0xFFFFFFFF, 0, 0, 0, 0, 0, 4, COFF_SECT_DATA | COFF_SECT_READ}, {".drectve", 0xFFFFFFFF, 0, 0, 0, 0, 0, 1, COFF_SECT_INFO | COFF_SECT_REMOVE}, {".comment", 0xFFFFFFFF, 0, 0, 0, 0, 0, 1, COFF_SECT_INFO | COFF_SECT_REMOVE}, {".debug$S", 0xFFFFFFFF, 0, 0, 0, 0, 0, 1, COFF_SECT_DATA | COFF_SECT_DISCARD | COFF_SECT_READ}, {".debug$T", 0xFFFFFFFF, 0, 0, 0, 0, 0, 1, COFF_SECT_DATA | COFF_SECT_DISCARD | COFF_SECT_READ}, }; /* available areas */ typedef enum area_type { area_stack, area_code, area_gla, area_plt, area_sst, area_gst, area_diags, area_params, area_iotab, area_bss, area_tls, area_init, area_fini, area_auxbss, area_directive, area_debugtabs, area_debugtabt, area_linetab, area_datagptab, area_bssgptab, area_reginfo, area_comment, area_gdt, area_frt, area_edt, area_cht, area_section, area_symbol, area_string, area_secstring, area_num } area_type; /* structure containing information about each area */ typedef struct area_info { char *area; char *name; section_type section; } area_info; /* table containing information about each area */ static area_info area_info_table[area_num] = { {"LOCALS", NULL, sec_null}, {"CODE", ".epctext", sec_text}, {"GLA", ".epcdata", sec_data}, {"PLT", NULL, sec_null}, {"SST", ".epcrodata", sec_rodata}, {"GST", NULL, sec_null}, {"DIAGS", NULL, sec_null}, {"PARAMS", NULL, sec_null}, {"IOTAB", NULL, sec_null}, {"BSS", ".epcbss", sec_bss}, {"TLS", ".epctls", sec_tls}, {"INIT", ".epcinit", sec_init}, {"FINI", ".epcfini", sec_fini}, {"AUXBSS", NULL, sec_null}, {"DIRECTIVE", ".epcdirect", sec_directive}, {"DEBUGTABS", ".epcdebsym", sec_debsym}, {"DEBUGTABT", ".epcdebtyp", sec_debtyp}, {"LINETAB", NULL, sec_null}, {"DATAGPTAB", NULL, sec_null}, {"BSSGPTAB", NULL, sec_null}, {"REGINFO", NULL, sec_null}, {"COMMENT", ".epccmt", sec_comment}, {"GDT", ".epcgdt", sec_gdt}, {"FRT", ".epcfrt", sec_frt}, {"EDT", ".epcedt", sec_edt}, {"CHT", ".epccht", sec_cht}, {"SECTION", NULL, sec_null}, {"SYMBOL", NULL, sec_null}, {"STRING", NULL, sec_null}, {"SECSTRING", NULL, sec_null} }; /* structure containing information about the assembler */ typedef struct assembler_specification { char *section; char *cursect; char *endsect; char *proc; char *label; char *endproc; char *local; char *global; char *weak; char *external; char *common; char *align; char *byte; char *word; char *zero; char *end; char *comment; } assembler_specification; /* specification for an Intel i386 COFF assembler */ static assembler_specification coff_i386_assembler = { "SEGMENT", "@CurSeg", "ENDS", "PROC", "LABEL", "ENDP", "PRIVATE", "PUBLIC", "WEAK", "EXTERN", "COMM", "ALIGN", "BYTE", "DWORD", "DUP", "END", ";" }; /* structure representing a COFF header */ typedef struct coff_header { unsigned short magic; unsigned short nscns; unsigned long timdat; unsigned long symptr; unsigned long nsyms; unsigned short opthdr; unsigned short flags; } coff_header; /* structure representing a COFF section */ typedef struct coff_section { char name[8]; unsigned long paddr; unsigned long vaddr; unsigned long size; unsigned long scnptr; unsigned long relptr; unsigned long lnnoptr; unsigned short nreloc; unsigned short nlnno; unsigned long flags; } coff_section; /* structure representing a COFF symbol */ typedef struct coff_symbol { union { char name[8]; struct { unsigned long zeroes; unsigned long offset; } y; } x; unsigned long value; unsigned short scnum; unsigned short type; unsigned char sclass; unsigned char numaux; } coff_symbol; /* structure representing a COFF relocation */ typedef struct coff_relocation { unsigned long vaddr; unsigned long symndx; unsigned short type; } coff_relocation; /* structure representing a COFF line number */ typedef struct coff_lineno { unsigned long addr; unsigned short line; } coff_lineno; /* table containing information about the available types */ /* of COFF relocations for the Intel i386 architecture */ static unsigned char coff_i386_relocation_table[14] = { COFF_REL_386_DIR32, COFF_REL_386_DIR32, COFF_REL_386_DIR32, COFF_REL_386_REL32, 0, COFF_REL_386_REL32, 0, 0, 0, 0, COFF_REL_386_SEC, COFF_REL_386_SECREL, 0, 0 }; /* table containing bit-masks for the available types */ /* of COFF relocations for the Intel i386 architecture */ static unsigned long coff_i386_rel_mask_table[20] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; /* available types of linkage */ typedef enum linkage { link_none, link_file, link_section, link_local, link_global, link_weak, link_local_common, link_global_common, link_weak_common, link_global_external, link_weak_external, link_local_function, link_global_function, link_weak_function, link_global_external_function, link_weak_external_function } linkage; /* structure containing information about the current function */ typedef struct function_info { size_t area; size_t address; } function_info; /* structure containing symbol aliasing information */ typedef struct alias { struct alias *next; char *name; linkage link; } alias; /* structure containing line number information */ typedef struct lineno { struct lineno *next; char *file; unsigned long line; union { char *label; size_t offset; } addr; } lineno; /* member list node */ typedef struct member_node { struct member_node *next; struct member_node *prev; size_t offset; unsigned char type; char *name; union { struct { linkage link; size_t size; lineno *lines; size_t symidx; } entry; struct { size_t area; long offset; unsigned long size; } target; } data; } member_node; /* member list header */ typedef struct member_list { struct member_node *head; struct member_node *tail; struct member_node *tlpr; } member_list; /* structure containing information about each declared area */ typedef struct area_header { char *name; char *contents; member_list members; alias *aliases; lineno *lines; linkage link; unsigned char fill; unsigned long flags; size_t parent; size_t offset; size_t align; size_t size; size_t max; } area_header; /* The following version strings should be defined elsewhere and are declared * tentatively in C in order to make them optional. In C++ tentative * declarations do not exist and so we must force their definition elsewhere. */ #ifdef __cplusplus extern char *epc_version_string; extern char *epc_front_end_version; extern char *epc_back_end_version; #else char *epc_version_string; char *epc_front_end_version; char *epc_back_end_version; #endif /* indicates if the put interface has been initialised */ /* and also whether or not it has not been terminated */ static int put_initialised, put_unterminated; /* the old and new output files */ static int old_outfile, new_outfile; /* the source and destination filenames */ static char *source_file, *dest_file; /* the current target assembler */ static assembler_specification *assembler; /* the current target relocation table */ static unsigned char *relocation_table; /* the current target relocation bit-mask table */ static unsigned long *rel_mask_table; /* table containing information about each area */ static area_header *area_table; /* the current size of the area table and the highest area in use */ static size_t area_table_size, highest_area; /* the maximum stack size for the current function */ static size_t max_stack_size; /* stack containing information about the section nesting level */ static long section_stack[SECTION_STACK_SIZE]; /* the current section nesting level */ static unsigned char section_level; /* the current section when generating assembler */ static long current_section; /* stack containing information about the function nesting level */ static function_info function_stack[SCOPE_STACK_SIZE]; /* the current function nesting level */ static unsigned char scope_level; /* the area number of the first defined function */ static size_t first_function; /* the file offset of the symbol table */ static size_t symbol_table_offset; /* the current filename for line number tables */ static char *current_filename; /* the default byte used to fill the uninitialised contents of areas */ static unsigned char area_filler; /* indicates the processor to generate code for */ static unsigned char target_processor; /* indicates the endianness of the host and target architectures */ static unsigned char host_endian, target_endian; /* indicates the debugging format being generated */ static int debug_format; /* indicates if an object file is to be produced instead of an assembler file */ static int object_generating; /* indicates if tracing is to be displayed for the put interface */ /* or for the put memory allocation interface */ static int put_trace, put_memory_trace; /* specifies an optional prefix (and its length in characters) that is */ /* to be prepended to all globally-visible symbol names */ static char *external_prefix; static size_t external_prefix_size; /* indicates if all non-local symbol names should be prepended with an */ /* optional prefix */ static int prepend_prefix; /* indicates if all common symbols should be converted to */ /* defined symbols in the BSS area */ static int no_commons; /* indicates if annotations should appear in the assembler output */ static int annotate_assembler; /* indicates if globally visible functions should be made weak */ /* and automatically given a global alias */ static int auto_weak_functions; /* indicates if the put interface should suppress code generation */ static int suppress_code_gen; /* definition of assert which indicates that an internal error has occurred */ #ifdef NDEBUG #define assert(test) (void) 0 #else #define assert(test) (void) ((test) || (put_assert(#test, __LINE__), 0)) #endif #ifdef __cplusplus extern "C" { #endif /* local function prototypes */ static void write_code(char *, ...); static void put_message(char *, ...); static void put_error(char *, ...); static void put_assert(char *, size_t); static void hex_dump(unsigned char *, size_t); static void string_dump(unsigned char *, size_t); static void byte_dump(size_t, size_t, unsigned char *, size_t); static void zero_dump(size_t, size_t, size_t); static void byte_swap(char *, size_t); static unsigned long log_base_2(unsigned long); static int is_power_of_two(unsigned long); static unsigned long round_up(unsigned long, unsigned long); static size_t base_area(size_t); static int is_function(linkage); static void new_member_list(member_list *); static member_node *add_member(size_t, size_t, unsigned char, char *, unsigned long, unsigned long, unsigned long, int); static void move_members(size_t, size_t, size_t); static void free_members(size_t); static alias *add_alias(size_t, char *, linkage); static void move_aliases(size_t, size_t, size_t); static void free_aliases(size_t); static lineno *add_lineno(size_t, char *, unsigned long, void *); static void free_linenos(lineno *); static void expand_contents(size_t); static void expand_area_table(void); static size_t add_string(area_type, char *); static void add_directive(int, char *); static void set_area_name(size_t, char *, int); static void init_area_table(void); static void free_area_table(void); static char *label_name(char *, size_t, long, int); static void enter_section(long); static size_t add_section(section_type); static size_t add_symbol(char *, linkage, size_t, unsigned long, section_type); static void add_function_aux(size_t, size_t, size_t); static void add_beginend_aux(size_t); static void add_weak_aux(size_t); static void add_file_aux(char *, size_t); static void add_section_aux(size_t, size_t, size_t); static void alter_relocations(size_t, size_t, size_t, size_t); static void optimise_relocations(void); static void count_relocations(void); static void count_lines(void); static void write_addends(void); static void write_relocations(member_list *, char *); static void write_lines(size_t, char *); static void write_header(void); static void write_contents(size_t); static void write_areas(void); static void write_commons(void); static void write_external_objects(void); static void write_external_functions(void); static void write_function_aliases(void); static void move_area(size_t, size_t, linkage); static void move_data_areas(void); static void move_commons(int); static void move_functions(void); static void merge_areas(void); static void patch_symbol_table(void); static void swap_symbol_table(void); static void swap_section_table(void); static void build_section_table(void); static size_t add_line_symbols(lineno **, char **, section_type); static void build_symbol_table(void); static void write_object(void); static char *linkage_name(linkage); static void print_lines(lineno *); static void print_members(size_t); static void print_areas(void); /* writes an assembler statement to the output file */ static void write_code(char *code, ...) { va_list list; va_start(list, code); vfprintf(stdout, code, list); va_end(list); } /* displays a message */ static void put_message(char *message, ...) { va_list list; va_start(list, message); vfprintf(stderr, message, list); va_end(list); } /* displays an error message and then terminates */ static void put_error(char *message, ...) { va_list list; fputs("put error: ", stderr); va_start(list, message); vfprintf(stderr, message, list); va_end(list); fputc('\n', stderr); if (dest_file) premoveobject(); exit(2); } /* displays an assertion failure and then terminates */ static void put_assert(char *assertion, size_t line) { put_message("assertion failed (line %lu): %s\n", line, assertion); put_error("internal error"); } /* displays a hexadecimal dump for the contents of a memory location */ static void hex_dump(unsigned char *address, size_t length) { size_t index; for (index = 0; index < length; index++) { if (index % 16 == 0) put_message("\t0x%08lX: ", address + index); put_message("%02X", address[index]); if ((index % 16 == 15) || (index == length - 1)) put_message("\n"); else put_message(" "); } } /* dumps the contents of a memory location as a string */ static void string_dump(unsigned char *address, size_t length) { size_t index; unsigned char temp; for (index = 0; index < length; index++) { temp = address[index]; switch (temp) { case '\0': if (index == length - 1) write_code("\\0"); else write_code("\\000"); break; case '\b': write_code("\\b"); break; case '\f': write_code("\\f"); break; case '\n': write_code("\\n"); break; case '\r': write_code("\\r"); break; case '\t': write_code("\\t"); break; case '\v': write_code("\\v"); break; case '"': case '\\': write_code("\\%c", temp); break; default: if (isprint(temp)) write_code("%c", temp); else write_code("\\%03o", temp); break; } } } /* dumps the contents of a memory location as a sequence of bytes */ static void byte_dump(size_t area, size_t offset, unsigned char *address, size_t length) { char *name; size_t index; if (area_table[area].link == link_section) name = label_name(NULL, area, 0, 0); else name = area_table[area].name; for (index = 0; index < length; index++) { if (index % 12 == 0) { if (annotate_assembler) { write_code("\t%s [%s", assembler->comment, name); if (offset + index > 0) write_code("+%lu", offset + index); write_code("] \""); if (length - index >= 12) string_dump(address + index, 12); else string_dump(address + index, length - index); write_code("\"\n"); } write_code("\t%s ", assembler->byte); } write_code("%02XH", address[index]); if ((index % 12 == 11) || (index == length - 1)) write_code("\n"); else write_code(","); } } /* dumps a specified number of zero bytes */ static void zero_dump(size_t area, size_t offset, size_t length) { if (area == area_bss) write_code("\t%s %lu %s (?)\n", assembler->byte, length, assembler->zero); else write_code("\t%s %lu %s (0)\n", assembler->byte, length, assembler->zero); } /* performs byte-swapping in a buffer of a specified length */ static void byte_swap(char *buffer, size_t length) { size_t index; char temp; for (index = 0; index < length >> 1; index++) { temp = buffer[index]; buffer[index] = buffer[length - index - 1]; buffer[length - index - 1] = temp; } } /* computes the rounded-down base-2 logarithm of a number */ static unsigned long log_base_2(unsigned long number) { unsigned long value; for (value = 0, number >>= 1; number > 0; value++, number >>= 1); return value; } /* determines if a number is a power of 2 */ static int is_power_of_two(unsigned long number) { return ((number != 0) && ((number & (number - 1)) == 0)); } /* rounds a value up to a specified power-of-2 alignment */ static unsigned long round_up(unsigned long value, unsigned long align) { return ((value - 1) & ~(align - 1)) + align; } /* determines the base area for a data area */ static size_t base_area(size_t area) { while (area_table[area].parent != 0) area = area_table[area].parent; return area; } /* determines if a linkage specifies a function */ static int is_function(linkage link) { if ((link == link_local_function) || (link == link_global_function) || (link == link_weak_function) || (link == link_global_external_function) || (link == link_weak_external_function)) return 1; return 0; } /* allocates a block of bytes for the put interface */ void *getspace(int size) { void *pointer; if (put_memory_trace) put_message("getspace(size = %d)\n", size); assert(size > 0); if ((pointer = (void *) calloc(size, 1)) == NULL) put_error("out of memory"); if (put_memory_trace) put_message("getspace() returns (pointer = 0x%08lX)\n", pointer); return pointer; } /* frees a previously allocated block of bytes */ void freespace(void *pointer) { if (put_memory_trace) put_message("freespace(pointer = 0x%08lX)\n", pointer); assert(pointer != NULL); free(pointer); } /* resizes the number of bytes in a previously allocated block */ void *reallocspace(void *pointer, int size) { if (put_memory_trace) put_message("reallocspace(pointer = 0x%08lX, size = %d)\n", pointer, size); if (pointer == NULL) pointer = getspace(size); else if (size == 0) { freespace(pointer); pointer = NULL; } else { assert(size > 0); if ((pointer = (void *) realloc(pointer, size)) == NULL) put_error("out of memory"); } if (put_memory_trace) put_message("reallocspace() returns (pointer = 0x%08lX)\n", pointer); return pointer; } /* initializes a member list to be an empty list */ static void new_member_list(member_list *list) { list->head = (member_node *) &list->tail; list->tail = NULL; list->tlpr = (member_node *) &list->head; } /* adds a member to the member list of an area */ static member_node *add_member(size_t area, size_t offset, unsigned char type, char *name, unsigned long data1, unsigned long data2, unsigned long data3, int no_prepend) { member_node *next, *node, *previous; for (previous = area_table[area].members.tlpr; previous->prev != NULL; previous = previous->prev) if ((offset > previous->offset) || ((offset == previous->offset) && ((type != 0) || (previous->type == 0)))) break; node = (member_node *) getspace(sizeof(member_node)); next = previous->next; previous->next = node; node->prev = previous; node->next = next; next->prev = node; node->offset = offset; node->type = type; /* Entry names may be local, global or weak, but may also begin with * a dot, which indicates that the name should not normally be placed * in the symbol table of the object file. Names beginning with a dot * or that are locally visible will therefore not be preceded by an * optional prefix. */ if (name != NULL) if (!no_prepend && prepend_prefix && (*name != '.') && (type == 0) && ((linkage) data1 != link_local) && ((linkage) data1 != link_local_common) && ((linkage) data1 != link_local_function)) { node->name = (char *) getspace(strlen(name) + external_prefix_size + 1); strcpy(node->name, external_prefix); strcat(node->name, name); } else { node->name = (char *) getspace(strlen(name) + 1); strcpy(node->name, name); } else node->name = NULL; if (type == 0) { /* Create a data entry with a given linkage and size. */ node->data.entry.link = (linkage) data1; node->data.entry.size = (size_t) data2; node->data.entry.lines = (lineno *) data3; node->data.entry.symidx = 0; } else { /* Create a relocation of a specified size pointing to a given area * and offset. */ node->data.target.area = (size_t) data1; node->data.target.offset = (long) data2; node->data.target.size = data3; } return node; } /* moves all of the members from the member list of one area to another */ static void move_members(size_t area, size_t new_area, size_t new_offset) { member_node *node; for (node = area_table[area].members.head; node->next != NULL; node = node->next) if (node->type == 0) { add_member(new_area, new_offset + node->offset, 0, node->name, node->data.entry.link, node->data.entry.size, (unsigned long) node->data.entry.lines, 1); node->data.entry.lines = NULL; } else add_member(new_area, new_offset + node->offset, node->type, node->name, node->data.target.area, node->data.target.offset, node->data.target.size, 1); free_members(area); } /* frees all of the members from the member list of an area */ static void free_members(size_t area) { member_node *next, *node; for (node = area_table[area].members.head; next = node->next; node = next) { if (node->name != NULL) freespace(node->name); if (node->type == 0) free_linenos(node->data.entry.lines); freespace(node); } new_member_list(&area_table[area].members); } /* adds an alias to the alias list of an area */ static alias *add_alias(size_t area, char *name, linkage link) { alias *anode, *previous; anode = (alias *) getspace(sizeof(alias)); if ((previous = area_table[area].aliases) == NULL) area_table[area].aliases = anode; else { while (previous->next != NULL) previous = previous->next; previous->next = anode; } anode->next = NULL; /* Alias names may be local, global or weak, but may also begin with * a dot, which indicates that the name should not normally be placed * in the symbol table of the object file. Names beginning with a dot * or that are locally visible will therefore not be preceded by an * optional prefix. */ if (prepend_prefix && (*name != '.') && (link != link_local) && (link != link_local_common) && (link != link_local_function)) { anode->name = (char *) getspace(strlen(name) + external_prefix_size + 1); strcpy(anode->name, external_prefix); strcat(anode->name, name); } else { anode->name = (char *) getspace(strlen(name) + 1); strcpy(anode->name, name); } anode->link = link; return anode; } /* moves all of the aliases from the alias list of one area to */ /* the member list of another area */ static void move_aliases(size_t area, size_t new_area, size_t new_offset) { alias *anode; for (anode = area_table[area].aliases; anode != NULL; anode = anode->next) add_member(new_area, new_offset, 0, anode->name, anode->link, area_table[area].size, 0, 1); free_aliases(area); } /* frees all of the aliases from the alias list of an area */ static void free_aliases(size_t area) { alias *anode, *next; for (anode = area_table[area].aliases; anode != NULL; anode = next) { next = anode->next; if (anode->name != NULL) freespace(anode->name); freespace(anode); } area_table[area].aliases = NULL; } /* adds a line number to the line number list of a function */ static lineno *add_lineno(size_t area, char *file, unsigned long line, void *addr) { lineno *lnode, *previous; lnode = (lineno *) getspace(sizeof(lineno)); if ((previous = area_table[area].lines) == NULL) area_table[area].lines = lnode; else { while (previous->next != NULL) previous = previous->next; previous->next = lnode; } lnode->next = NULL; lnode->file = (char *) getspace(strlen((char *) file) + 1); strcpy(lnode->file, file); lnode->line = line; if (object_generating) lnode->addr.offset = (size_t) addr; else { lnode->addr.label = (char *) getspace(strlen((char *) addr) + 1); strcpy(lnode->addr.label, (char *) addr); } return lnode; } /* frees all of the line numbers from a line number list */ static void free_linenos(lineno *lnode) { lineno *next; while (lnode != NULL) { next = lnode->next; freespace(lnode->file); if (!object_generating) freespace(lnode->addr.label); freespace(lnode); lnode = next; } } /* increases the size of the contents of an area */ static void expand_contents(size_t area) { area_header *table; size_t old_size; table = &area_table[area]; old_size = table->max; if (table->contents == NULL) { if (area < area_num) table->max = 2048; else table->max = 256; table->contents = (char *) getspace(table->max); } else { if (area < area_num) table->max *= 2; else table->max *= 2; table->contents = (char *) reallocspace(table->contents, table->max); } memset(table->contents + old_size, table->fill, table->max - old_size); } /* increases the size of the area table to allow for more areas */ static void expand_area_table(void) { area_header *new_area_table, *new_table, *table; size_t index, old_size; old_size = area_table_size; if (area_table == NULL) area_table_size = 32; else area_table_size += 128; new_area_table = (area_header *) getspace(area_table_size * sizeof(area_header)); for (index = 0; index < old_size; index++) { table = &area_table[index]; new_table = &new_area_table[index]; new_table->name = table->name; new_table->contents = table->contents; /* If the old member list is empty then we can simply create * a new empty member list. However, if the old member list * contains members then we must fix the previous pointer of * the head and the next pointer of the tail otherwise there * will be dangling references to freed memory. */ if (table->members.head->next == NULL) new_member_list(&new_table->members); else { new_table->members.head = table->members.head; new_table->members.tail = NULL; new_table->members.tlpr = table->members.tlpr; new_table->members.head->prev = (member_node *) &new_table->members.head; new_table->members.tlpr->next = (member_node *) &new_table->members.tail; } new_table->aliases = table->aliases; new_table->lines = table->lines; new_table->link = table->link; new_table->fill = table->fill; new_table->flags = table->flags; new_table->parent = table->parent; new_table->offset = table->offset; new_table->align = table->align; new_table->size = table->size; new_table->max = table->max; } if (area_table != NULL) freespace(area_table); area_table = new_area_table; for (index = old_size; index < area_table_size; index++) { table = &area_table[index]; table->name = NULL; table->contents = NULL; new_member_list(&table->members); table->aliases = NULL; table->lines = NULL; table->link = link_local; if ((index == area_gla) || (index == area_sst) || (index >= area_num)) table->fill = area_filler; else table->fill = 0; table->flags = 0; table->parent = 0; table->offset = 0; table->align = 0; table->size = 0; table->max = 0; } } /* adds a string to a string table */ static size_t add_string(area_type area, char *name) { area_header *table; size_t offset, size; table = &area_table[area]; offset = table->size; size = offset + strlen(name) + 1; while (size > table->max) expand_contents(area); strcpy(table->contents + offset, name); table->size = size; return offset; } /* adds a directive to the directive table */ static void add_directive(int type, char *dir) { area_header *table; table = &area_table[area_directive]; if (type == DIRECT_EXPORT) { add_string(area_directive, "-export"); table->contents[table->size - 1] = ':'; } else if (type == DIRECT_LIBRARY) { add_string(area_directive, "-defaultlib"); table->contents[table->size - 1] = ':'; } add_string(area_directive, dir); table->contents[table->size - 1] = ' '; } /* sets the name of a specific area */ static void set_area_name(size_t area, char *name, int local) { area_header *table; table = &area_table[area]; /* Area names may be local, global or weak, but may also begin with * a dot, which indicates that the name should not normally be placed * in the symbol table of the object file. Names beginning with a dot * or that are locally visible will therefore not be preceded by an * optional prefix. */ if (table->name != NULL) free(table->name); if (prepend_prefix && (*name != '.') && !local) { table->name = (char *) getspace(strlen(name) + external_prefix_size + 1); strcpy(table->name, external_prefix); strcat(table->name, name); } else { table->name = (char *) getspace(strlen(name) + 1); strcpy(table->name, name); } } /* initialises the area table */ static void init_area_table(void) { size_t area; for (area = 0; area < area_num; area++) { highest_area = area + 1; while (area >= area_table_size) expand_area_table(); if (area_info_table[area].name != NULL) set_area_name(area, area_info_table[area].name, 1); } } /* deletes the entire area table */ static void free_area_table(void) { area_header *table; size_t index; for (index = 0; index < highest_area; index++) { table = &area_table[index]; if (table->name != NULL) freespace(table->name); if (table->contents != NULL) freespace(table->contents); free_members(index); free_aliases(index); free_linenos(table->lines); } if (area_table != NULL) freespace(area_table); area_table = NULL; area_table_size = 0; highest_area = 0; } /* returns the label name for a given area and offset */ static char *label_name(char *name, size_t area, long offset, int end) { static char label[1024]; area_header *table; member_node *node; table = &area_table[area]; /* We first look for an entry within the area. */ for (node = table->members.head; (name == NULL) && (node->next != NULL) && (node->offset <= offset); node = node->next) if ((node->type == 0) && (node->offset + node->data.entry.size > offset)) { name = node->name; offset -= node->offset; } /* If that failed and we are in a user-defined section then we should * try to use the first entry within the area and adjust the offset * accordingly. */ node = table->members.head; if ((name == NULL) && (node->next != NULL) && (table->link == link_section)) { name = node->name; offset -= node->offset; } /* If that failed then we should just use the area name. */ if (name == NULL) name = table->name; /* If that failed then we use the diagnostic name of the area. This * will occur for unused areas that are used elsewhere in the backend * and will most likely produce an undefined symbol, but it is expected * that such results are for tracing purposes only. */ if ((name == NULL) && (area < area_num)) name = area_info_table[area].area; if (end != 0) { if (*name == '.') end = 0; else label[0] = '.'; if (offset != 0) sprintf(label + end, "%s%+ld", name, offset); else strcpy(label + end, name); strcat(label, ".end"); } else if (offset != 0) sprintf(label, "%s%+ld", name, offset); else return name; return label; } /* switches the current assembler section */ static void enter_section(long section) { area_header *user_table; section_info *table; char *name; if (section != sec_null) { if (section != current_section) { if (section < sec_null) { name = area_table[-section].name; /* The following code represents the culmination of a series of * hacks in order to allow data entries to be placed in the text * section. The TEXTDATA section is a user-defined section * which contains all of the data entries for the text section. */ if (strcmp(name, "TEXTDATA") == 0) section = sec_text; } if (section > sec_null) { if (section == sec_text) name = ".CODE"; else if (section == sec_data) name = ".DATA"; else if (section == sec_bss) name = ".DATA?"; else if (section == sec_rodata) name = ".CONST"; else name = section_info_table[section].name; } if (current_section != sec_null) write_code("%s\t%s\n\n", assembler->cursect, assembler->endsect); if ((section == sec_text) || (section == sec_data) || (section == sec_bss) || (section == sec_rodata)) write_code("\t%s\n", name); else { write_code("\t%s\t%s", name, assembler->section); if (section > sec_null) { table = §ion_info_table[section]; if ((table->flags & COFF_SECT_INFO) || (table->flags & COFF_SECT_DISCARD)) write_code(" %s", assembler->byte); else write_code(" %s", assembler->word); if (!(table->flags & COFF_SECT_WRITE)) write_code(" READONLY"); write_code(" %s", assembler->global); if (section == sec_debsym) write_code(" 'DEBSYM'"); else if (section == sec_debtyp) write_code(" 'DEBTYP'"); else if (table->flags & COFF_SECT_CODE) write_code(" 'CODE'"); else write_code(" 'DATA'"); } else { user_table = &area_table[-section]; if (user_table->flags & SECT_DEBUG) write_code(" %s", assembler->byte); else write_code(" %s", assembler->word); if (user_table->flags & SECT_READONLY) write_code(" READONLY"); write_code(" %s", assembler->global); if (user_table->flags & SECT_DEBUG) write_code(" 'DEBSYM'"); else if (user_table->flags & SECT_EXECUTABLE) write_code(" 'CODE'"); else write_code(" 'DATA'"); } write_code("\n"); } write_code("\n"); current_section = section; } section_stack[section_level] = section; } } /* returns the name of a section */ char *psectionname(int section) { char *name; if (suppress_code_gen) return NULL; if (put_trace) put_message("psectionname(section = %d)\n", section); assert(put_initialised); assert(put_unterminated); assert(((section > sec_null) && (section < sec_num)) || ((section > -highest_area) && (section <= -area_num))); if (section > sec_null) name = section_info_table[section].name; else { name = area_table[-section].name; /* The following code represents the culmination of a series of * hacks in order to allow data entries to be placed in the text * section. The TEXTDATA section is a user-defined section * which contains all of the data entries for the text section. */ if (strcmp(name, "TEXTDATA") == 0) name = section_info_table[sec_text].name; } if (put_trace) put_message("psectionname() returns (name = \"%s\")\n", name ? name : "NULL"); return name; } /* returns the section id for a section name */ int pfindsection(char *name) { area_header *table; size_t index; long section; if (suppress_code_gen) return sec_null; if (put_trace) put_message("pfindsection(name = \"%s\")\n", name); assert(put_initialised); assert(put_unterminated); assert((name != NULL) && (*name != '\0')); /* We initially search in the predefined section table for a section that * matches, and if that fails then we look for the first user-defined * section that matches. This ordering is useful for checking that a * user-defined section does not clash with a predefined section. */ section = sec_null; for (index = 1; index < sec_num; index++) if (strcmp(section_info_table[index].name, name) == 0) { section = index; break; } if (section == sec_null) for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->link == link_section) && (strcmp(table->name, name) == 0)) { section = -index; break; } } if (put_trace) put_message("pfindsection() returns (section = %d)\n", section); return section; } /* returns the section associated with an area */ int pareasection(int area) { area_header *table; long section; if (suppress_code_gen) return sec_null; if (put_trace) put_message("pareasection(area = %d)\n", area); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); /* This function provides the only safe and reliable way to obtain * the section id that a given area maps onto. The resulting section * id can then only be used with the ppushsection() and ppopsection() * functions. */ table = &area_table[area]; if (area < area_num) section = area_info_table[area].section; else if (table->link == link_section) section = -area; else if (is_function(table->link)) section = area_info_table[area_code].section; else if ((table->link == link_local) || (table->link == link_global) || (table->link == link_weak)) section = area_info_table[area_gla].section; else if ((table->link == link_local_common) || (table->link == link_global_common) || (table->link == link_weak_common)) section = area_info_table[area_bss].section; else section = sec_null; if (put_trace) put_message("pareasection() returns (section = %d)\n", section); return section; } /* returns the area associated with a section */ int psectionarea(int section) { size_t area, index; if (suppress_code_gen) return 0; if (put_trace) put_message("psectionarea(section = %d)\n", section); assert(put_initialised); assert(put_unterminated); assert(((section > sec_null) && (section < sec_num)) || ((section > -highest_area) && (section <= -area_num))); area = 0; if ((section > sec_null) && (section < sec_num)) /* If the section is a predefined section then there should be a * predefined area that uses it. However, there may be several areas * that use the same section, but we can only return the first one we * come across without any further information. */ for (index = 1; index < area_num; index++) { if (area_info_table[index].section == section) { area = index; break; } } else area = -section; if (put_trace) put_message("psectionarea() returns (area = %d)\n", area); return area; } /* pushes a new section onto the section stack and enters it */ void ppushsection(int section) { if (suppress_code_gen) return; if (put_trace) put_message("ppushsection(section = %d)\n", section); assert(put_initialised); assert(put_unterminated); assert(!object_generating); assert(((section > sec_null) && (section < sec_num)) || ((section > -highest_area) && (section <= -area_num))); /* Avoid calling this function for data sections as there are now * functions available for placing data at any position in a data * area, which will then be dumped at the end of the assembler file. * This function should really only be used for switching between * different text sections. */ if (++section_level == SECTION_STACK_SIZE) put_error("section nesting level too deep"); enter_section(section); } /* pops the current section from the section stack and */ /* enters the previous section before returning it */ int ppopsection(void) { long section; if (suppress_code_gen) return sec_null; if (put_trace) put_message("ppopsection()\n"); assert(put_initialised); assert(put_unterminated); assert(!object_generating); assert(section_level > 0); /* Avoid calling this function for data sections as there are now * functions available for placing data at any position in a data * area, which will then be dumped at the end of the assembler file. * This function should really only be used for switching between * different text sections. */ section = section_stack[--section_level]; enter_section(section); if (put_trace) put_message("ppopsection() returns (section = %d)\n", section); return section; } /* returns the initialisation data for a block of bytes */ /* located within an area at a specific offset */ char *pgetbytes(int area, int offset, int length) { area_header *table; char *buffer; if (suppress_code_gen) return NULL; if (put_trace) put_message("pgetbytes(area = %d, offset = %d, length = %d)\n", area, offset, length); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); assert(!is_function(area_table[area].link)); assert((offset >= 0) && (length > 0)); table = &area_table[area]; /* We must return a valid pointer only if the area contains data and * is large enough to hold data of a given length at offset into the * area. All non-data areas will return NULL. */ buffer = NULL; if ((table->link != link_local_common) && (table->link != link_global_common) && (table->link != link_weak_common) && (offset + length <= table->size)) buffer = table->contents + offset; if (put_trace) { put_message("pgetbytes() returns (buffer = 0x%08lX)\n", buffer); if (buffer != NULL) hex_dump((unsigned char *) buffer, length); } return buffer; } /* allocates a given amount of space at the end of a specified area */ int preservespace(int area, int length, int alignment, int uninitialised) { area_header *table; long offset; if (suppress_code_gen) return 0; if (put_trace) put_message("preservespace(area = %d, length = %d, alignment = %d, " "uninitialised = %d)\n", area, length, alignment, uninitialised); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); assert(!is_function(area_table[area].link)); assert((alignment > 0) && is_power_of_two(alignment)); table = &area_table[area]; if (area == area_stack) { table->size = round_up(table->size + length, alignment); offset = -table->size; if (table->size > max_stack_size) max_stack_size = table->size; } else { table->size = round_up(table->size, alignment); offset = table->size; table->size += length; } if ((area != area_stack) && (area != area_params) && ((area == area_bss) || !uninitialised)) while (table->size > table->max) expand_contents(area); if (put_trace) put_message("preservespace() returns (offset = %ld)\n", offset); return offset; } /* allocates temporary memory from the end of the stack */ int etempspace(int length, int alignment) { long offset; if (suppress_code_gen) return 0; if (put_trace) put_message("etempspace(length = %d, alignment = %d)\n", length, alignment); assert(put_initialised); assert(put_unterminated); offset = preservespace(area_stack, length, alignment, 0); if (put_trace) put_message("etempspace() returns (offset = %ld)\n", offset); return offset; } /* allocates permanent memory from the end of the GLA */ int epermspace(int length, int alignment) { long offset; if (suppress_code_gen) return 0; if (put_trace) put_message("epermspace(length = %d, alignment = %d)\n", length, alignment); assert(put_initialised); assert(put_unterminated); offset = preservespace(area_gla, length, alignment, 0); if (put_trace) put_message("epermspace() returns (offset = %ld)\n", offset); return offset; } /* allocates working memory from the end of a suitable area */ int eworkspace(int length, int alignment, int type, int *area) { long offset; if (suppress_code_gen) return 0; if (put_trace) put_message("eworkspace(length = %d, alignment = %d, type = %d, " "area = 0x%08lX)\n", length, alignment, type, area); assert(put_initialised); assert(put_unterminated); assert(area != NULL); if (type == 0) *area = area_stack; else if (type == 2) *area = area_sst; else *area = area_gla; offset = preservespace(*area, length, alignment, 0); if (put_trace) put_message("eworkspace() returns (area = %d, offset = %ld)\n", *area, offset); return offset; } /* initialises a block of bytes located within an area at a specific offset */ void pdinit(int area, int offset, int length) { area_header *table; size_t total; if (suppress_code_gen) return; if (put_trace) put_message("pdinit(area = %d, offset = %d, length = %d)\n", area, offset, length); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); assert(!is_function(area_table[area].link)); assert((offset >= 0) && (length > 0)); table = &area_table[area]; /* If the area being initialised is common or external data then * we can upgrade the area to become an initialised data area. * However, we must first check that any common areas have been * finalised. */ if ((table->link == link_local_common) || (table->link == link_global_common) || (table->link == link_weak_common)) assert(table->size > 0); if (table->link == link_local_common) table->link = link_local; else if ((table->link == link_global_common) || (table->link == link_global_external)) table->link = link_global; else if ((table->link == link_weak_common) || (table->link == link_weak_external)) table->link = link_weak; total = offset + length; while (total > table->max) expand_contents(area); if (total > table->size) table->size = total; } /* initialises a block of bytes located within the code area */ /* at a specific offset to the contents of a buffer */ void pcbytes(int offset, int length, char *buffer) { if (suppress_code_gen) return; if (put_trace) { put_message("pcbytes(offset = %d, length = %d)\n", offset, length); hex_dump((unsigned char *) buffer, length); } assert(put_initialised); assert(put_unterminated); assert(object_generating); assert(buffer != NULL); pdinit(area_code, offset, length); memcpy(area_table[area_code].contents + offset, buffer, length); } /* initialises a block of bytes located within an area */ /* at a specific offset to the contents of a buffer */ void pdbytes(int area, int offset, int length, char *buffer) { if (suppress_code_gen) return; if (put_trace) { put_message("pdbytes(area = %d, offset = %d, length = %d)\n", area, offset, length); hex_dump((unsigned char *) buffer, length); } assert(put_initialised); assert(put_unterminated); assert((area != area_code) && (area != area_bss)); assert(buffer != NULL); pdinit(area, offset, length); memcpy(area_table[area].contents + offset, buffer, length); } /* initialises multiple consecutive blocks of bytes */ /* located within an area at a specific offset */ void pdpattern(int area, int offset, int repeat, int length, char *buffer) { size_t index; if (suppress_code_gen) return; if (put_trace) { put_message("pdpattern(area = %d, offset = %d, repeat = %d, " "length = %d)\n", area, offset, repeat, length); hex_dump((unsigned char *) buffer, length); } assert(put_initialised); assert(put_unterminated); assert(repeat > 0); for (index = 0; index < repeat; index++, offset += length) pdbytes(area, offset, length, buffer); } /* initialises a byte within an area at a specific offset */ void pd(int area, int offset, int value) { char *buffer; if (suppress_code_gen) return; if (put_trace) put_message("pd(area = %d, offset = %d, value = %d)\n", area, offset, value); assert(put_initialised); assert(put_unterminated); buffer = (char *) &value; if (host_endian == BIG_ENDIAN) buffer += 3; pdbytes(area, offset, 1, buffer); } /* initialises a 2-byte word within an area at a specific offset */ void pd2(int area, int offset, int value) { char *buffer; if (suppress_code_gen) return; if (put_trace) put_message("pd2(area = %d, offset = %d, value = %d)\n", area, offset, value); assert(put_initialised); assert(put_unterminated); buffer = (char *) &value; if (host_endian == BIG_ENDIAN) buffer += 2; pdbytes(area, offset, 2, buffer); } /* initialises a 4-byte word within an area at a specific offset */ void pd4(int area, int offset, int value) { char *buffer; if (suppress_code_gen) return; if (put_trace) put_message("pd4(area = %d, offset = %d, value = %d)\n", area, offset, value); assert(put_initialised); assert(put_unterminated); buffer = (char *) &value; pdbytes(area, offset, 4, buffer); } /* initialises a 4-byte word within the code area at a specific offset */ void pcword(int offset, int value) { if (suppress_code_gen) return; if (put_trace) put_message("pcword(offset = %d, value = %d)\n", offset, value); assert(put_initialised); assert(put_unterminated); assert(object_generating); pcbytes(offset, 4, (char *) &value); } /* inserts a string into the assembler file */ void pasmstring(char *string) { if (suppress_code_gen) return; if (put_trace) put_message("pasmstring(string = \"%s\")\n", string); assert(put_initialised); assert(put_unterminated); assert(!object_generating); assert((string != NULL) && (*string != '\0')); write_code("%s", string); } /* inserts a comment into the assembler file */ void pcomment(char *string) { if (suppress_code_gen) return; if (put_trace) put_message("pcomment(string = \"%s\")\n", string); assert(put_initialised); assert(put_unterminated); assert(!object_generating); assert((string != NULL) && (*string != '\0')); write_code("\t%s %s\n", assembler->comment, string); } /* adds a string to the comment section of an object file */ void pident(char *string) { if (suppress_code_gen) return; if (put_trace) put_message("pident(string = \"%s\")\n", string); assert(put_initialised); assert(put_unterminated); assert((string != NULL) && (*string != '\0')); add_string(area_comment, string); } /* notes the registers used by the compiler */ void preginfo(int int_registers, int float_registers) { } /* sets the current filename for line number tables */ void pfile(char *file) { if (suppress_code_gen) return; if (put_trace) put_message("pfile(\"%s\")\n", file); assert(put_initialised); assert(put_unterminated); assert(file != NULL); current_filename = file; } /* adds a line number to a given function */ void plinestart(int area, int line, int column, int offset) { if (suppress_code_gen) return; if (put_trace) { put_message("plinestart(area = %d, line = %d, column = %d, ", area, line, column); if (object_generating) put_message("offset = %d)\n", offset); else put_message("label = \"%s\")\n", (char *) offset); } assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); assert(is_function(area_table[area].link)); assert((line > 0) && (column >= 0)); if (object_generating) assert(offset >= 0); else assert((char *) offset != NULL); assert(current_filename != NULL); if ((debug_format == DEBUG_COFF) || (debug_format == DEBUG_CODEVIEW)) add_lineno(area, current_filename, (unsigned short) line, (void *) offset); } /* returns the label name for a given area and offset */ char *pgetname(int area, int offset) { char *name; if (put_trace) put_message("pgetname(area = %d, offset = %d)\n", area, offset); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); name = label_name(NULL, area, offset, 0); if (put_trace) put_message("pgetname() returns (name = \"%s\")\n", name); return name; } /* returns the name of a given area */ char *pgivename(int area) { char *name; if (put_trace) put_message("pgivename(area = %d)\n", area); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); name = area_table[area].name; /* If that failed then we use the diagnostic name of the area. This * will occur for unused areas that are used elsewhere in the backend * and will most likely produce an undefined symbol, but it is expected * that such results are for tracing purposes only. */ if ((area < area_num) && (name == NULL)) name = area_info_table[area].area; if (put_trace) put_message("pgivename() returns (name = \"%s\")\n", name); return name; } /* returns the current size of a given area */ int pgivecurrentsize(int area) { int size; if (put_trace) put_message("pgivecurrentsize(area = %d)\n", area); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); size = area_table[area].size; if (put_trace) put_message("pgivecurrentsize() returns (size = %d)\n", size); return size; } /* returns the maximum stack size for the current function */ int pstackmax(void) { if (put_trace) put_message("pstackmax()\n"); assert(put_initialised); assert(put_unterminated); if (put_trace) put_message("pstackmax() returns (max = %d)\n", max_stack_size); return max_stack_size; } /* sets the current stack offset and returns the previous offset */ int pstackoffset(int offset) { long old_offset; if (put_trace) put_message("pstackoffset(offset = %d)\n", offset); assert(put_initialised); assert(put_unterminated); assert(offset <= 0); old_offset = -area_table[area_stack].size; area_table[area_stack].size = -offset; if (-offset > max_stack_size) max_stack_size = -offset; if (put_trace) put_message("pstackoffset() returns (old_offset = %d)\n", old_offset); return old_offset; } /* sets the alignment for a specific area and returns the previous alignment */ int pareaalign(int area, int alignment) { section_type section; size_t old_align; if (put_trace) put_message("pareaalign(area = %d, alignment = %d)\n", area, alignment); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); assert(!is_function(area_table[area].link)); assert((alignment == 0) || is_power_of_two(alignment)); old_align = area_table[area].align; if (alignment > 0) { area_table[area].align = alignment; if (area < area_num) { section = area_info_table[area].section; if ((section != sec_null) && (section_info_table[section].align < alignment)) section_info_table[section].align = alignment; } } if (put_trace) put_message("pareaalign() returns (old_alignment = %d)\n", old_align); return old_align; } /* sets the alignment for a common area */ void paligncommon(int area, int alignment) { linkage link; if (put_trace) put_message("paligncommon(area = %d, alignment = %d)\n", area, alignment); assert(put_initialised); assert(put_unterminated); assert((area >= area_num) && (area < highest_area)); link = area_table[area].link; assert((link == link_local_common) || (link == link_global_common) || (link == link_weak_common)); pareaalign(area, alignment); } /* sets the filler byte for a specific area */ void pfiller(int area, int filler) { if (put_trace) put_message("pfiller(area = %d, filler = 0x%08lX)\n", area, filler); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); assert((area == area_gla) || (area == area_sst) || (area >= area_num)); assert(!is_function(area_table[area].link)); assert((filler >= 0) && (filler < 256)); area_table[area].fill = filler; } /* adds a relocation request of a specified size from a host area to an */ /* assembler label of a specified type */ void pfixlabel3(int area, int offset, int target_area, char *label, int label_offset, int size, int type) { unsigned char fix; if (suppress_code_gen) return; if (put_trace) put_message("pfixlabel3(area = %d, offset = %d, target_area = %d, " "label = \"%s\", label_offset = %d, size = %d, " "type = %d)\n", area, offset, target_area, label, label_offset, size, type); assert(put_initialised); assert(put_unterminated); assert(!object_generating); assert((area != area_code) && (area != area_bss)); assert((target_area >= 0) && (target_area < highest_area)); assert((label != NULL) && (*label != '\0')); /* When the relocation type is positive we cross-reference the * relocation information table to obtain the architecture-dependent * relocation type. If the relocation type is negative then its * positive value will be used without referencing the relocation * information table. */ if (type >= 0) { assert(type < 14); fix = relocation_table[type]; assert(fix > 0); } else fix = -type; if (fix == COFF_REL_386_SEC) if (size == 0) size = 2; else assert(size == 2); else if (size == 0) size = 4; else assert(size == 4); /* This relocation type is for assembler-generating compilers only. * It saves having to create a new area for a label by storing the * label name with the relocation. */ if (size == 2) pd2(area, offset, 0); else pd4(area, offset, 0); add_member(area, offset, fix, label, 0, label_offset, size, 0); /* Ensure that the section of the target area will be given an entry * in the symbol table of the generated object file. */ area_table[target_area].offset++; } /* adds a relocation request of a specified size from a host area to an */ /* assembler label */ void pfixlabel2(int area, int offset, int target_area, char *label, int label_offset, int size) { if (suppress_code_gen) return; if (put_trace) put_message("pfixlabel2(area = %d, offset = %d, target_area = %d, " "label = \"%s\", label_offset = %d, size = %d)\n", area, offset, target_area, label, label_offset, size); assert(put_initialised); assert(put_unterminated); pfixlabel3(area, offset, target_area, label, label_offset, size, 0); } /* adds a relocation request from a host area to an assembler label */ void pfixlabel(int area, int offset, int target_area, char *label, int label_offset) { if (suppress_code_gen) return; if (put_trace) put_message("pfixlabel(area = %d, offset = %d, target_area = %d, " "label = \"%s\", label_offset = %d)\n", area, offset, target_area, label, label_offset); assert(put_initialised); assert(put_unterminated); pfixlabel2(area, offset, target_area, label, label_offset, 4); } /* adds a relocation request from a host area to a target area */ /* of a specified type */ void pfix2(int area, int offset, int target_area, int target_offset, int type) { size_t size; unsigned char fix; if (suppress_code_gen) return; if (put_trace) put_message("pfix2(area = %d, offset = %d, target_area = %d, " "target_offset = %d, type = %d)\n", area, offset, target_area, target_offset, type); assert(put_initialised); assert(put_unterminated); assert(area != area_bss); assert((target_area >= 0) && (target_area < highest_area)); /* When the relocation type is positive we cross-reference the * relocation information table to obtain the architecture-dependent * relocation type. If the relocation type is negative then its * positive value will be used without referencing the relocation * information table. */ if (type >= 0) { assert(type < 14); fix = relocation_table[type]; assert(fix > 0); } else fix = -type; if (fix == COFF_REL_386_SEC) size = 2; else size = 4; if (area == area_code) pdinit(area_code, offset, size); else if (size == 2) pd2(area, offset, 0); else pd4(area, offset, 0); add_member(area, offset, fix, NULL, target_area, target_offset, size, 0); /* When generating assembler, ensure that the section of the target area * will be given an entry in the symbol table of the generated object file. */ if (!object_generating) area_table[target_area].offset++; } /* adds a relocation request from a host area to a target area */ void pfix(int area, int offset, int target_area, int target_offset) { if (suppress_code_gen) return; if (put_trace) put_message("pfix(area = %d, offset = %d, target_area = %d, " "target_offset = %d)\n", area, offset, target_area, target_offset); assert(put_initialised); assert(put_unterminated); pfix2(area, offset, target_area, target_offset, 0); } /* adds a relocation request from a host area to the start of a target area */ void pdxref(int area, int offset, int target_area) { if (suppress_code_gen) return; if (put_trace) put_message("pdxref(area = %d, offset = %d, target_area = %d)\n", area, offset, target_area); assert(put_initialised); assert(put_unterminated); pfix(area, offset, target_area, 0); } /* defines a data symbol at a given area and offset */ /* and with a specified visibility */ void pdataentry2(char *name, int visibility, int area, int length, int offset) { member_node *node; linkage link; if (suppress_code_gen) return; if (put_trace) put_message("pdataentry2(name = \"%s\", visibility = %d, area = %d, " "length = %d, offset = %d)\n", name, visibility, area, length, offset); assert(put_initialised); assert(put_unterminated); assert((name != NULL) && (*name != '\0')); assert(area != area_code); if (visibility > 0) link = link_local; else if (visibility < 0) link = link_weak; else link = link_global; pdinit(area, offset, length); node = add_member(area, offset, 0, name, link, length, 0, 0); if (visibility & 0x80000000) add_directive(DIRECT_EXPORT, node->name); } /* defines a global data symbol at a given area and offset */ void pdataentry(char *name, int area, int length, int offset) { if (suppress_code_gen) return; if (put_trace) put_message("pdataentry(name = \"%s\", area = %d, length = %d, " "offset = %d)\n", name, area, length, offset); assert(put_initialised); assert(put_unterminated); pdataentry2(name, 0, area, length, offset); } /* reserves a new area */ int pnextsymbol(void) { int area; if (suppress_code_gen) return 0; if (put_trace) put_message("pnextsymbol()\n"); assert(put_initialised); assert(put_unterminated); /* This function traditionally allocated a new area for an unnamed * function symbol. However, as we always know the name of a function * symbol, even for a function prototype, we can use pxname() instead * and so this function now allocates an empty local data area. */ area = highest_area++; while (area >= area_table_size) expand_area_table(); if (put_trace) put_message("pnextsymbol() returns (area = %d)\n", area); return area; } /* defines a new section with a specified alignment */ int psection(char *name, int alignment, int flags, char *label) { area_header *table; int area; if (suppress_code_gen) return 0; if (put_trace) put_message("psection(name = \"%s\", alignment = %d, " "flags = 0x%08lX, label = \"%s\")\n", name, alignment, flags, label); assert(put_initialised); assert(put_unterminated); assert((name != NULL) && (*name != '\0')); assert((alignment > 0) && is_power_of_two(alignment)); area = pnextsymbol(); set_area_name(area, name, 1); table = &area_table[area]; table->link = link_section; table->flags = flags; table->align = alignment; add_member(area, 0, 0, label, link_local, 0, 0, 1); if (put_trace) put_message("psection() returns (area = %d)\n", area); return area; } /* defines a data area with a specified visibility and parent data area */ int pcdataarea2(int visibility, int length, int alignment, int parent, char *name) { area_header *table; int area; if (suppress_code_gen) return 0; if (put_trace) put_message("pcdataarea2(visibility = %d, length = %d, alignment = %d, " "parent = %d, name = \"%s\")\n", visibility, length, alignment, parent, name); assert(put_initialised); assert(put_unterminated); assert((alignment > 0) && is_power_of_two(alignment)); assert((parent != area_code) && (parent != area_init) && (parent != area_fini) && !is_function((linkage) parent)); assert((name != NULL) && (*name != '\0')); area = pnextsymbol(); set_area_name(area, name, (visibility > 0)); table = &area_table[area]; while (length > table->max) expand_contents(area); if (visibility > 0) table->link = link_local; else if (visibility < 0) table->link = link_weak; else table->link = link_global; if (visibility & 0x80000000) add_directive(DIRECT_EXPORT, table->name); table->parent = parent; table->size = length; table->align = alignment; if (put_trace) put_message("pcdataarea2() returns (area = %d)\n", area); return area; } /* defines a data area with a specified visibility */ int pcdataarea(int visibility, int length, int alignment, char *name) { int area; if (suppress_code_gen) return 0; if (put_trace) put_message("pcdataarea(visibility = %d, length = %d, alignment = %d, " "name = \"%s\")\n", visibility, length, alignment, name); assert(put_initialised); assert(put_unterminated); area = pcdataarea2(visibility, length, alignment, area_gla, name); if (put_trace) put_message("pcdataarea() returns (area = %d)\n", area); return area; } /* defines a common area with a specified visibility */ int pcommon2(int visibility, char *name) { int area; if (suppress_code_gen) return 0; if (put_trace) put_message("pcommon2(visibility = %d, name = \"%s\")\n", visibility, name); assert(put_initialised); assert(put_unterminated); assert((name != NULL) && (*name != '\0')); area = pnextsymbol(); set_area_name(area, name, (visibility > 0)); /* A common area is unfinalised until pendcommon() or pendcommon2() are * called. This is indicated by the area having a size of zero. */ if (visibility > 0) area_table[area].link = link_local_common; else if (visibility < 0) area_table[area].link = link_weak_common; else area_table[area].link = link_global_common; if (visibility & 0x80000000) add_directive(DIRECT_EXPORT, area_table[area].name); if (put_trace) put_message("pcommon2() returns (area = %d)\n", area); return area; } /* defines a common area with global visibility */ int pcommon(char *name) { int area; if (suppress_code_gen) return 0; if (put_trace) put_message("pcommon(name = \"%s\")\n", name); assert(put_initialised); assert(put_unterminated); area = pcommon2(0, name); if (put_trace) put_message("pcommon() returns (area = %d)\n", area); return area; } /* finalises a common area with a specified alignment */ void pendcommon2(int area, int length, int flags, int alignment) { if (suppress_code_gen) return; if (put_trace) put_message("pendcommon2(area = %d, length = %d, flags = 0x%08lX, " "alignment = %d)\n", area, length, flags, alignment); assert(put_initialised); assert(put_unterminated); assert((area >= area_num) && (area < highest_area)); assert((area_table[area].link == link_local_common) || (area_table[area].link == link_global_common) || (area_table[area].link == link_weak_common)); assert(area_table[area].size == 0); assert(length > 0); assert((alignment > 0) && is_power_of_two(alignment)); area_table[area].size = length; area_table[area].align = alignment; } /* finalises a common area with a default alignment */ void pendcommon(int area, int length) { if (suppress_code_gen) return; if (put_trace) put_message("pendcommon(area = %d, length = %d)\n", area, length); assert(put_initialised); assert(put_unterminated); pendcommon2(area, length, 0, 4); } /* declares an external data symbol */ int pdxname(int visibility, char *name) { int area; if (suppress_code_gen) return 0; if (put_trace) put_message("pdxname(visibility = %d, name = \"%s\")\n", visibility, name); assert(put_initialised); assert(put_unterminated); assert((name != NULL) && (*name != '\0')); area = pnextsymbol(); set_area_name(area, name, 0); if (visibility < 0) area_table[area].link = link_weak_external; else area_table[area].link = link_global_external; area_table[area].align = 4; if (put_trace) put_message("pdxname() returns (area = %d)\n", area); return area; } /* declares an external function symbol */ int pxname(int visibility, char *name) { int area; if (suppress_code_gen) return 0; if (put_trace) put_message("pxname(visibility = %d, name = \"%s\")\n", visibility, name); assert(put_initialised); assert(put_unterminated); assert((name != NULL) && (*name != '\0')); area = pnextsymbol(); set_area_name(area, name, 0); if (visibility < 0) area_table[area].link = link_weak_external_function; else area_table[area].link = link_global_external_function; area_table[area].fill = 0; if (put_trace) put_message("pxname() returns (area = %d)\n", area); return area; } /* declares a new name for an existing function symbol */ void pprocname(int area, char *name) { if (suppress_code_gen) return; if (put_trace) put_message("pprocname(area = %d, name = \"%s\")\n", area, name); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); assert(is_function(area_table[area].link)); assert((name != NULL) && (*name != '\0')); set_area_name(area, name, 0); } /* returns the low PC and high PC of a given function */ void pprocbounds(int area, void **start, void **end) { area_header *table; if (put_trace) put_message("pprocbounds(area = %d)\n", area); assert(put_initialised); assert(put_unterminated); if (area != area_code) { assert((area >= area_num) && (area < highest_area)); assert((area_table[area].link == link_local_function) || (area_table[area].link == link_global_function) || (area_table[area].link == link_weak_function)); } table = &area_table[area]; if (object_generating) { if (area == area_code) { *start = (void *) 0; *end = (void *) table->size; } else { assert(table->size > 0); *start = (void *) table->offset; *end = (void *) (table->offset + table->size); } if (put_trace) put_message("pprocbounds() returns (start = %d, end = %d)\n", (int) *start, (int) *end); } else { if (area == area_code) if (first_function == 0) { *start = NULL; *end = NULL; } else { *start = (void *) table->name; *end = (void *) label_name(table->name, area_code, 0, 1); } else { *start = (void *) table->name; *end = (void *) label_name(table->name, area, 0, 1); } if (put_trace) put_message("pprocbounds() returns (start = %s, end = %s)\n", (char *) *start, (char *) *end); } } /* returns the next function that has been defined */ int pprocnext(int area) { area_header *table; assert(put_initialised); assert(put_unterminated); if (area < area_num - 1) area = area_num - 1; while (++area < highest_area) { table = &area_table[area]; if ((table->link == link_local_function) || (table->link == link_global_function) || (table->link == link_weak_function)) return area; } return 0; } /* defines a function with a specified visibility */ void pproc(char *name, int visibility, int offset, int *area) { if (suppress_code_gen) return; if (put_trace) put_message("pproc(name = \"%s\", visibility = %d, offset = %d, " "area = %d)\n", name, visibility, offset, *area); assert(put_initialised); assert(put_unterminated); assert((name != NULL) && (*name != '\0')); assert(offset >= 0); assert(area != NULL); /* If the supplied area identifier is -1 then we are creating a new * area to represent a function. Otherwise, the area identifier * represents an existing area allocated by pxname() which we are * going to upgrade to a function definition. */ if (*area == -1) { *area = pnextsymbol(); set_area_name(*area, name, (visibility > 0)); area_table[*area].fill = 0; } else { /* An area identifier has already been allocated for this function. * We must ensure that the identifier refers to this function and * the easiest way to do this is to compare the names. */ assert((*area >= area_num) && (*area < highest_area)); assert((area_table[*area].link == link_global_external_function) || (area_table[*area].link == link_weak_external_function)); } if (scope_level == SCOPE_STACK_SIZE) put_error("function nesting level too deep"); function_stack[scope_level].area = *area; if (object_generating) function_stack[scope_level].address = offset; scope_level++; if (visibility > 0) area_table[*area].link = link_local_function; else if (visibility < 0) area_table[*area].link = link_weak_function; else area_table[*area].link = link_global_function; if (visibility & 0x80000000) add_directive(DIRECT_EXPORT, area_table[*area].name); if (!object_generating) { if (first_function == 0) { if (annotate_assembler) write_code("%s functions:\n\n", assembler->comment); first_function = *area; } if (annotate_assembler) write_code("%s start of %s {\n", assembler->comment, area_table[*area].name); write_code("%s\t%s", area_table[*area].name, assembler->proc); /* If the function name begins with a dot then we do not wish * to place its symbol in the symbol table of the object file. */ if (*area_table[*area].name == '.') write_code(" SYSCALL %s\n", assembler->local); else if (visibility > 0) write_code(" %s\n", assembler->local); else if (visibility < 0) write_code(" %s\n", assembler->weak); else write_code(" %s\n", assembler->global); } } /* finalises the definition of a function */ void pprocend(int offset, int frame) { area_header *table; char *name; size_t area; if (suppress_code_gen) return; if (put_trace) put_message("pprocend(offset = %d, frame = %d)\n", offset, frame); assert(put_initialised); assert(put_unterminated); assert(scope_level > 0); scope_level--; area = function_stack[scope_level].area; assert((area >= area_num) && (area < highest_area)); assert((area_table[area].link == link_local_function) || (area_table[area].link == link_global_function) || (area_table[area].link == link_weak_function)); assert((offset >= 0) && (frame >= 0)); max_stack_size = 0; area_table[area_stack].size = 0; area_table[area_params].size = 0; table = &area_table[area]; if (auto_weak_functions && (*table->name != '.') && (table->link == link_global_function)) { /* Change the linkage of the function to be weak and then define * it to have a globally visible alias of the same name except * beginning with an underscore. */ table->link = link_weak_function; name = (char *) getspace(strlen(table->name) + 2); strcpy(name, "_"); strcat(name, table->name); add_alias(area, name, link_global_function); freespace(name); } if (object_generating) { table->offset = function_stack[scope_level].address; assert(offset > table->offset); table->size = offset - table->offset; } else { write_code("%s\t%s %s\n", label_name(table->name, area, 0, 1), assembler->label, assembler->byte); write_code("%s\t%s\n", table->name, assembler->endproc); if (annotate_assembler) write_code("%s } end of %s\n", assembler->comment, table->name); write_code("\n"); } } /* defines a side entry with a specified visibility */ int pentry2(int visibility, int offset, int area, char *name) { area_header *table; if (suppress_code_gen) return 0; if (put_trace) put_message("pentry2(visibility = %d, offset = %d, area = %d, " "name = \"%s\")\n", visibility, offset, area, name); assert(put_initialised); assert(put_unterminated); assert(offset >= 0); assert((name != NULL) && (*name != '\0')); /* If the supplied area identifier is -1 then we are creating a new * area to represent a side entry. Otherwise, the area identifier * represents an existing area allocated by pxname() which we are * going to upgrade to a function definition. */ if (area == -1) { area = pnextsymbol(); set_area_name(area, name, (visibility > 0)); area_table[area].fill = 0; } else { assert((area >= area_num) && (area < highest_area)); assert((area_table[area].link == link_global_external_function) || (area_table[area].link == link_weak_external_function)); } table = &area_table[area]; if (visibility > 0) table->link = link_local_function; else if (visibility < 0) table->link = link_weak_function; else table->link = link_global_function; if (visibility & 0x80000000) add_directive(DIRECT_EXPORT, table->name); if (auto_weak_functions && (*table->name != '.') && (table->link == link_global_function)) { /* Change the linkage of the side entry to be weak and then define * it to have a globally visible alias of the same name except * beginning with an underscore. */ table->link = link_weak_function; name = (char *) getspace(strlen(table->name) + 2); strcpy(name, "_"); strcat(name, table->name); add_alias(area, name, link_global_function); freespace(name); } if (object_generating) table->offset = offset; else { write_code("%s\t%s %s\n", table->name, assembler->label, assembler->byte); /* If the side entry name begins with a dot then we do not wish * to place its symbol in the symbol table of the object file. */ if (*table->name != '.') if (table->link == link_global_function) write_code("\t%s %s\n", assembler->global, table->name); else if (table->link == link_weak_function) write_code("\t%s %s\n", assembler->weak, table->name); } if (put_trace) put_message("pentry2() returns (area = %d)\n", area); return area; } /* defines a new side entry with a specified visibility */ int pentry(int visibility, int offset, char *name) { int area; if (suppress_code_gen) return 0; if (put_trace) put_message("pentry(visibility = %d, offset = %d, name = \"%s\")\n", visibility, offset, name); assert(put_initialised); assert(put_unterminated); area = pentry2(visibility, offset, -1, name); if (put_trace) put_message("pentry() returns (area = %d)\n", area); return area; } /* defines an alias to another symbol at a given area */ void palias(int area, char *name, int visibility) { area_header *table; alias *anode; linkage link; if (suppress_code_gen) return; if (put_trace) put_message("palias(area = %d, name = \"%s\", visibility = %d)\n", area, name, visibility); assert(put_initialised); assert(put_unterminated); assert((area >= 0) && (area < highest_area)); assert((name != NULL) && (*name != '\0')); table = &area_table[area]; /* Aliases may be used to associate more than one name with an entry in * the symbol table and each name can also have different visibilities in * order to provide a different functionality, such as the use of weak * aliases to global symbols. The type of linkage that the alias should * be given depends on the type of symbol that is being referred to. No * aliases may be given to common areas. */ if ((table->link == link_local) || (table->link == link_global) || (table->link == link_weak)) if (visibility > 0) link = link_local; else if (visibility < 0) link = link_weak; else link = link_global; else if ((table->link == link_global_external) || (table->link == link_weak_external)) if (visibility < 0) link = link_weak_external; else link = link_global_external; else if ((table->link == link_local_function) || (table->link == link_global_function) || (table->link == link_weak_function)) if (visibility > 0) link = link_local_function; else if (visibility < 0) link = link_weak_function; else link = link_global_function; else if ((table->link == link_global_external_function) || (table->link == link_weak_external_function)) if (visibility < 0) link = link_weak_external_function; else link = link_global_external_function; else put_error("attempted to alias the wrong type of symbol"); /* Add the name to the list of aliases for this area. Note that this * cannot be done for data entries, but can be achieved by adding another * data entry at the same area and offset. */ anode = add_alias(area, name, link); if (visibility & 0x80000000) add_directive(DIRECT_EXPORT, anode->name); } /* adds a section to the section table */ /* note that some of the fields may have to be filled in later */ static size_t add_section(section_type index) { area_header *table; section_info *section_table; coff_section *section; size_t align, length; table = &area_table[area_section]; section_table = §ion_info_table[index]; length = table->size + sizeof(coff_section); while (length > table->max) expand_contents(area_section); section = (coff_section *) (table->contents + table->size); table->size = length; memset(section->name, 0, 8); if (strlen(section_table->name) <= 8) strncpy(section->name, section_table->name, 8); else sprintf(section->name, "/%s", add_string(area_string, section_table->name) + 4); section->paddr = 0; section->vaddr = 0; section->size = section_table->size; section->scnptr = 0; section->relptr = 0; section->lnnoptr = 0; if (section_table->relocs > USHRT_MAX) put_error("too many relocations"); section->nreloc = section_table->relocs; if (section_table->lines > USHRT_MAX) put_error("too many line numbers"); section->nlnno = section_table->lines; if (section_table->align > 0) align = section_table->align; else align = section_table->default_align; section->flags = section_table->flags | ((log_base_2(align) + 1) << 20); return ((char *) section - table->contents) / sizeof(coff_section); } /* adds a symbol to the symbol table */ static size_t add_symbol(char *name, linkage link, size_t numaux, unsigned long value, section_type section) { area_header *table; coff_symbol symbol; size_t length; if (strlen(name) <= 8) { memset(symbol.x.name, 0, 8); strncpy(symbol.x.name, name, 8); } else { symbol.x.y.zeroes = 0; symbol.x.y.offset = add_string(area_string, name) + 4; } symbol.value = value; if ((link == link_local_common) || (link == link_global_common) || (link == link_weak_common) || (link == link_global_external) || (link == link_weak_external) || (link == link_global_external_function) || (link == link_weak_external_function)) symbol.scnum = sec_null; else if (link == link_file) symbol.scnum = COFF_SYMBOL_DEBUG; else symbol.scnum = section + 1; if (is_function(link)) symbol.type = COFF_SYMBOL_FUNC; else symbol.type = COFF_SYMBOL_OBJECT; if (link == link_none) symbol.sclass = COFF_SYMBOL_FUNCAUX; else if (link == link_file) symbol.sclass = COFF_SYMBOL_FILE; else if ((link == link_section) || (link == link_local) || (link == link_local_common) || (link == link_local_function)) symbol.sclass = COFF_SYMBOL_LOCAL; else if ((link == link_global) || (link == link_global_common) || (link == link_global_external) || (link == link_global_function) || (link == link_global_external_function)) symbol.sclass = COFF_SYMBOL_GLOBAL; else symbol.sclass = COFF_SYMBOL_WEAK; symbol.numaux = numaux; table = &area_table[area_symbol]; length = table->size + 18; while (length > table->max) expand_contents(area_symbol); memcpy(table->contents + table->size, &symbol, 18); table->size = length; return (length / 18) - 1; } /* adds an auxilliary function symbol to the symbol table */ static void add_function_aux(size_t index, size_t size, size_t lines) { area_header *table; coff_symbol symbol; size_t length; memset(&symbol, 0, 18); *((unsigned long *) &symbol) = index; *((unsigned long *) ((char *) &symbol + 4)) = size; *((unsigned long *) ((char *) &symbol + 8)) = lines; table = &area_table[area_symbol]; length = table->size + 18; while (length > table->max) expand_contents(area_symbol); memcpy(table->contents + table->size, &symbol, 18); table->size = length; } /* adds an auxilliary .bf or .ef symbol to the symbol table */ static void add_beginend_aux(size_t line) { area_header *table; coff_symbol symbol; size_t length; memset(&symbol, 0, 18); *((unsigned short *) ((char *) &symbol + 4)) = line; table = &area_table[area_symbol]; length = table->size + 18; while (length > table->max) expand_contents(area_symbol); memcpy(table->contents + table->size, &symbol, 18); table->size = length; } /* adds an auxilliary weak symbol to the symbol table */ static void add_weak_aux(size_t index) { area_header *table; coff_symbol symbol; size_t length; memset(&symbol, 0, 18); *((unsigned long *) &symbol) = index; *((unsigned short *) ((char *) &symbol + 4)) = 3; table = &area_table[area_symbol]; length = table->size + 18; while (length > table->max) expand_contents(area_symbol); memcpy(table->contents + table->size, &symbol, 18); table->size = length; } /* adds an auxilliary file symbol to the symbol table */ static void add_file_aux(char *name, size_t count) { area_header *table; char *symbol; size_t length; symbol = (char *) getspace(count * 18); memset(symbol, 0, count * 18); strncpy(symbol, name, count * 18); table = &area_table[area_symbol]; length = table->size + (count * 18); while (length > table->max) expand_contents(area_symbol); memcpy(table->contents + table->size, symbol, count * 18); table->size = length; freespace(symbol); } /* adds an auxilliary section symbol to the symbol table */ static void add_section_aux(size_t size, size_t relocs, size_t lines) { area_header *table; coff_symbol symbol; size_t length; memset(&symbol, 0, 18); *((unsigned long *) &symbol) = size; *((unsigned short *) ((char *) &symbol + 4)) = relocs; *((unsigned short *) ((char *) &symbol + 6)) = lines; table = &area_table[area_symbol]; length = table->size + 18; while (length > table->max) expand_contents(area_symbol); memcpy(table->contents + table->size, &symbol, 18); table->size = length; } /* alters all relocations targetting a particular area */ static void alter_relocations(size_t area, size_t offset, size_t new_area, size_t new_offset) { member_node *node; size_t index; for (index = 0; index < highest_area; index++) for (node = area_table[index].members.head; node->next != NULL; node = node->next) if ((node->type > 0) && (node->data.target.area == area)) { node->data.target.area = new_area; node->data.target.offset += new_offset - offset; } } /* removes any relocations resulting from relative calls to local functions */ static void optimise_relocations(void) { member_node *next, *node; unsigned long call; long offset; for (node = area_table[area_code].members.head; next = node->next; node = next) if ((node->type == relocation_table[3]) && (area_table[node->data.target.area].link == link_local_function)) { offset = area_table[node->data.target.area].offset + node->data.target.offset - node->offset - 4; memcpy(&call, area_table[area_code].contents + node->offset, 4); if (host_endian != target_endian) byte_swap((char *) &call, 4); call = (call & 0x80000000) | offset; if (host_endian != target_endian) byte_swap((char *) &call, 4); memcpy(area_table[area_code].contents + node->offset, &call, 4); if (put_trace) put_message("internal call to \"%s\" from code offset %lu " "optimised\n", area_table[node->data.target.area].name, node->offset); node->prev->next = next; next->prev = node->prev; freespace(node); } } /* counts the number of relocations in each section */ static void count_relocations(void) { member_node *node; section_info *section; size_t index, target; for (index = 0; index < area_num; index++) { section = §ion_info_table[area_info_table[index].section]; for (node = area_table[index].members.head; node->next != NULL; node = node->next) if (node->type > 0) { target = node->data.target.area; if (target < area_num) section_info_table[area_info_table[target].section].targets++; section->relocs++; } } } /* counts the number of line numbers in each section */ static void count_lines(void) { area_header *table; member_node *node; lineno *lnode; section_info *section; size_t index; for (index = 0; index < area_num; index++) { table = &area_table[index]; section = §ion_info_table[area_info_table[index].section]; for (lnode = table->lines; lnode != NULL; lnode = lnode->next) section->lines++; for (node = table->members.head; node->next != NULL; node = node->next) if (node->type == 0) for (lnode = node->data.entry.lines; lnode != NULL; lnode = lnode->next) section->lines++; } } /* fills in an addend for every relocation */ static void write_addends(void) { area_header *table; member_node *node; size_t index; unsigned long contents4; long offset; unsigned short contents2; for (index = 0; index < area_num; index++) { table = &area_table[index]; for (node = table->members.head; node->next != NULL; node = node->next) if ((node->type > 0) && (node->type != COFF_REL_386_SEC)) { offset = node->data.target.offset; if (node->data.target.size == 2) { memcpy(&contents2, table->contents + node->offset, 2); if (host_endian != target_endian) byte_swap((char *) &contents2, 2); contents2 = (contents2 & ~rel_mask_table[node->type - 1]) | (offset & rel_mask_table[node->type - 1]); if (host_endian != target_endian) byte_swap((char *) &contents2, 2); memcpy(table->contents + node->offset, &contents2, 2); } else { memcpy(&contents4, table->contents + node->offset, 4); if (host_endian != target_endian) byte_swap((char *) &contents4, 4); contents4 = (contents4 & ~rel_mask_table[node->type - 1]) | (offset & rel_mask_table[node->type - 1]); if (host_endian != target_endian) byte_swap((char *) &contents4, 4); memcpy(table->contents + node->offset, &contents4, 4); } } } } /* fills in the entries for a relocation table */ static void write_relocations(member_list *list, char *buffer) { member_node *node; coff_relocation reloc; size_t symbol; for (node = list->head; node->next != NULL; node = node->next) if (node->type > 0) { symbol = area_table[node->data.target.area].offset; reloc.vaddr = node->offset; reloc.symndx = symbol; reloc.type = node->type; if (host_endian != target_endian) { byte_swap((char *) &reloc.vaddr, 4); byte_swap((char *) &reloc.symndx, 4); byte_swap((char *) &reloc.type, 2); } memcpy(buffer, &reloc, 10); buffer += 10; } } /* fills in the entries for a line number table */ static void write_lines(size_t area, char *buffer) { area_header *table; member_node *node; lineno *lnode; coff_lineno line; unsigned long current_line; table = &area_table[area]; for (lnode = table->lines; lnode != NULL; lnode = lnode->next) { if (lnode == table->lines) { line.addr = table->offset; line.line = 0; current_line = lnode->line; } else { line.addr = lnode->addr.offset; line.line = (unsigned short) ((lnode->line - current_line) & 0xFFFF); /* If, through some amazing feat of optimisation, we end * up back at the first line of the function again, then * we need to add a hack to prevent the linker interpreting * this entry as the start of a new function. The easiest * way to do this is to increment the line number by one. */ if (line.line == 0) line.line++; } if (host_endian != target_endian) { byte_swap((char *) &line.addr, 4); byte_swap((char *) &line.line, 2); } memcpy(buffer, &line, 6); buffer += 6; } for (node = table->members.head; node->next != NULL; node = node->next) if (node->type == 0) for (lnode = node->data.entry.lines; lnode != NULL; lnode = lnode->next) { if (lnode == node->data.entry.lines) { line.addr = node->data.entry.symidx; line.line = 0; current_line = lnode->line; } else { line.addr = lnode->addr.offset; line.line = (unsigned short) ((lnode->line - current_line) & 0xFFFF); /* If, through some amazing feat of optimisation, we end * up back at the first line of the function again, then * we need to add a hack to prevent the linker interpreting * this entry as the start of a new function. The easiest * way to do this is to increment the line number by one. */ if (line.line == 0) line.line++; } if (host_endian != target_endian) { byte_swap((char *) &line.addr, 4); byte_swap((char *) &line.line, 2); } memcpy(buffer, &line, 6); buffer += 6; } } /* writes out the header information */ static void write_header(void) { write_code("\tTITLE %s\n", source_file); write_code("\t.386\n"); write_code("\t.MODEL FLAT, C\n"); write_code("\tOPTION DOTNAME\n"); write_code("\tOPTION NOSCOPED\n\n"); } /* writes out the contents of a data area */ static void write_contents(size_t area) { area_header *table; member_node *node; size_t offset, size; table = &area_table[area]; node = table->members.head; if (node->next == NULL) size = table->size; else size = node->offset; if (size > 0) if (area == area_bss) zero_dump(area, 0, size); else byte_dump(area, 0, (unsigned char *) table->contents, size); for (offset = size; node->next != NULL; offset += size) { if (node->type == 0) { size = node->data.entry.size; if (annotate_assembler && (size > 0)) write_code("\t%s [%s]\n", assembler->comment, label_name(table->name, area, offset, 0)); write_code("%s\t%s %s\n", node->name, assembler->label, assembler->byte); /* If the entry name begins with a dot then we do not wish * to place its symbol in the symbol table of the object file. */ if (*node->name != '.') { if (node->data.entry.link == link_global) write_code("\t%s %s\n", assembler->global, node->name); else if (node->data.entry.link == link_weak) write_code("\t%s %s\n", assembler->weak, node->name); } /* A data entry can contain fixups as well as data and so we * must allow for the case when the offset of the next member * overlaps the current data entry. The following check ensures * that the size is correctly modified when this occurs. */ if ((node->next->next != NULL) && (size > node->next->offset - offset)) size = node->next->offset - offset; if (size > 0) if (area == area_bss) zero_dump(area, offset, size); else byte_dump(area, offset, (unsigned char *) table->contents + offset, size); } else { /* Generate a fixup to an area and offset. */ size = node->data.target.size; if ((node->next->next != NULL) && (node->next->offset < offset + size)) put_error("relocation overwritten in `%s' at offset %lu", table->name, offset); if (annotate_assembler) write_code("\t%s [%s]\n", assembler->comment, label_name(table->name, area, offset, 0)); if (node->type == COFF_REL_386_SEC) write_code("\t%s SECTION:%s\n", assembler->word, label_name(node->name, node->data.target.area, node->data.target.offset, 0)); else if (node->type == COFF_REL_386_SECREL) write_code("\t%s OFFSET:%s\n", assembler->word, label_name(node->name, node->data.target.area, node->data.target.offset, 0)); else write_code("\t%s %s\n", assembler->word, label_name(node->name, node->data.target.area, node->data.target.offset, 0)); } offset += size; node = node->next; if (node->next == NULL) size = table->size - offset; else size = node->offset - offset; if (size > 0) if (area == area_bss) zero_dump(area, offset, size); else byte_dump(area, offset, (unsigned char *) table->contents + offset, size); } } /* writes out the details of any data areas */ static void write_areas(void) { area_header *table; alias *anode; size_t align, base, index; long section; enter_section(sec_data); if (annotate_assembler) write_code("%s data:\n\n", assembler->comment); for (index = 0; index < highest_area; index++) { table = &area_table[index]; if ((index != area_code) && (table->name != NULL) && ((table->size > 0) || (table->offset > 0)) && ((table->link == link_section) || (table->link == link_local) || (table->link == link_global) || (table->link == link_weak))) { base = base_area(index); if (base < area_num) section = area_info_table[base].section; else if (area_table[base].link == link_section) section = -base; else section = sec_data; enter_section(section); if (table->align > 0) align = table->align; else align = section_info_table[section].default_align; write_code("\t%s %lu\n", assembler->align, align); if (table->link != link_section) write_code("%s\t%s %s\n", table->name, assembler->label, assembler->byte); /* If the area name begins with a dot then we do not wish * to place its symbol in the symbol table of the object file. */ if ((table->link != link_section) && (*table->name != '.')) { if (table->link == link_global) write_code("\t%s %s\n", assembler->global, table->name); else if (table->link == link_weak) write_code("\t%s %s\n", assembler->weak, table->name); } /* We now write out any symbols that are aliased to this area. */ for (anode = table->aliases; anode != NULL; anode = anode->next) { write_code("%s\t%s %s\n", anode->name, assembler->label, assembler->byte); if (anode->link == link_global) write_code("\t%s %s\n", assembler->global, anode->name); else if (anode->link == link_weak) write_code("\t%s %s\n", assembler->weak, anode->name); } if (table->size > 0) write_contents(index); } } enter_section(sec_data); } /* writes out the details of any common symbols */ static void write_commons(void) { area_header *table; size_t index; int found; found = 0; for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->link == link_local_common) || (table->link == link_global_common) || (table->link == link_weak_common)) { assert(table->aliases == NULL); assert(table->size > 0); if (!found) { write_code("\n"); if (annotate_assembler) write_code("%s common symbols:\n\n", assembler->comment); found = 1; } write_code("\t%s %s:%s:%lu\n", assembler->common, table->name, assembler->byte, table->size); if (table->link == link_local) write_code("\t%s %s\n", assembler->local, table->name); else if (table->link == link_weak) write_code("\t%s %s\n", assembler->weak, table->name); } } } /* writes out the details of any external data symbols */ static void write_external_objects(void) { area_header *table; alias *anode; size_t index; int found; /* Most assemblers will not complain about any undefined symbols * at the end of assembly. However, they will simply describe the * symbol in the symbol table of the object file as having no type. * We can be more specific by explicitly specifying the type at * this point. */ found = 0; for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->link == link_global_external) || (table->link == link_weak_external)) { if (!found) { write_code("\n"); if (annotate_assembler) write_code("%s external data symbols:\n\n", assembler->comment); found = 1; } write_code("\t%s %s:%s\n", assembler->external, table->name, assembler->byte); if (table->link == link_weak_external) write_code("\t%s %s\n", assembler->weak, table->name); /* We now write out any symbols that are aliased to this area. */ for (anode = table->aliases; anode != NULL; anode = anode->next) { write_code("\t%s %s:%s\n", assembler->external, anode->name, assembler->byte); if (anode->link == link_weak_external) write_code("\t%s %s\n", assembler->weak, anode->name); } } } } /* writes out the details of any external function symbols */ static void write_external_functions(void) { area_header *table; alias *anode; size_t index; int found; /* Most assemblers will not complain about any undefined symbols * at the end of assembly. However, they will simply describe the * symbol in the symbol table of the object file as having no type. * We can be more specific by explicitly specifying the type at * this point. */ found = 0; for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->link == link_global_external_function) || (table->link == link_weak_external_function)) { if (!found) { write_code("\n"); if (annotate_assembler) write_code("%s external function symbols:\n\n", assembler->comment); found = 1; } write_code("\t%s %s:%s\n", assembler->external, table->name, assembler->byte); if (table->link == link_weak_external_function) write_code("\t%s %s\n", assembler->weak, table->name); /* We now write out any symbols that are aliased to this area. */ for (anode = table->aliases; anode != NULL; anode = anode->next) { write_code("\t%s %s:%s\n", assembler->external, anode->name, assembler->byte); if (anode->link == link_weak_external_function) write_code("\t%s %s\n", assembler->weak, anode->name); } } } } /* writes out the details of any aliases for defined functions */ static void write_function_aliases(void) { area_header *table; alias *anode; size_t index; int found; /* Because function aliases may have been declared after the functions * that they referred to were defined we must write out the list of * function aliases to the assembler file at this point. */ found = 0; for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->link == link_local_function) || (table->link == link_global_function) || (table->link == link_weak_function)) for (anode = table->aliases; anode != NULL; anode = anode->next) { if (!found) { write_code("\n"); if (annotate_assembler) write_code("%s function aliases:\n\n", assembler->comment); found = 1; } write_code("\t%s=%s\n", anode->name, table->name); if (anode->link == link_global_function) write_code("\t%s %s\n", assembler->global, anode->name); else if (anode->link == link_weak_function) write_code("\t%s %s\n", assembler->weak, anode->name); } } } /* moves one area to the end of another area */ static void move_area(size_t area, size_t new_area, linkage link) { area_header *new_table, *table; section_info *section; size_t align, index, offset, size; table = &area_table[area]; new_table = &area_table[new_area]; align = table->align; if (new_table->align == 0) { /* The area being targetted has no specified alignment but we should * use the alignment of its section just to be safe. */ section = §ion_info_table[area_info_table[new_area].section]; if (section->align > 0) new_table->align = section->align; else new_table->align = section->default_align; } if (align == 0) { /* The area being moved has no specified alignment but we should * use the alignment of its section just to be safe. */ section = §ion_info_table[area_info_table[area].section]; if (section->align > 0) align = section->align; else align = section->default_align; } /* If the area being moved has a specified alignment then we must * ensure that the destination area and destination section have at * least that alignment as well. */ if ((new_table->align > 0) && (new_table->align < align)) new_table->align = align; if (new_area < area_num) { section = §ion_info_table[area_info_table[new_area].section]; if (section->align < align) section->align = align; } offset = round_up(new_table->size, align); size = table->size + offset; while (size > new_table->max) expand_contents(new_area); if (table->contents != NULL) memcpy(new_table->contents + offset, table->contents, table->size); new_table->size = size; if (link != link_none) add_member(new_area, offset, 0, table->name, link, table->size, (unsigned long) table->lines, 1); move_members(area, new_area, offset); move_aliases(area, new_area, offset); alter_relocations(area, 0, new_area, offset); for (index = 0; index < highest_area; index++) if (area_table[index].parent == area) area_table[index].parent = new_area; if (!object_generating) new_table->offset += table->offset; freespace(table->name); table->name = NULL; if (table->contents != NULL) { freespace(table->contents); table->contents = NULL; } table->link = link_local; table->lines = NULL; table->fill = 0; table->flags = 0; table->parent = 0; table->offset = 0; table->align = 0; table->size = 0; table->max = 0; } /* moves any data areas that were defined with the pcdataarea function */ static void move_data_areas(void) { area_header *table; size_t index, parent; /* Any data areas defined with the pcdataarea function must be merged * into their parent areas before generating an object file. This step * does not need to be performed when generating assembler code as the * assembler will automatically deal with them. */ for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->name != NULL) && ((table->link == link_local) || (table->link == link_global) || (table->link == link_weak))) { if (table->parent != 0) parent = table->parent; else parent = area_gla; move_area(index, parent, table->link); } } } /* moves common symbols to the BSS area */ static void move_commons(int local_only) { area_header *table; linkage link; size_t index; /* Local common symbols cause problems for linkers on some platforms * so we try to remove them here by converting them to local data symbols * in the BSS area. Note that if local_only is not set then all common * symbols will be converted to symbols in the BSS area. */ for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->link == link_local_common) || (!local_only && ((table->link == link_global_common) || (table->link == link_weak_common)))) { assert(table->size > 0); if (table->link == link_local_common) link = link_local; else if (table->link == link_global_common) link = link_global; else link = link_weak; move_area(index, area_bss, link); } } } /* moves function symbols to the code area */ static void move_functions(void) { area_header *table; size_t index; /* When generating object files, all defined functions logically belong * in the code area and not in their own area so we perform this * translation here. This also means that any relocations to an area * representing a function can also be converted to a code offset and will * also result in all functions being sorted in the order of their offset * into the code area. None of this requires to be done when generating * assembler code. */ for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->link == link_local_function) || (table->link == link_global_function) || (table->link == link_weak_function)) { add_member(area_code, table->offset, 0, table->name, table->link, table->size, (unsigned long) table->lines, 1); move_aliases(index, area_code, table->offset); alter_relocations(index, 0, area_code, table->offset); freespace(table->name); table->name = NULL; table->link = link_local; table->lines = NULL; table->offset = 0; table->size = 0; } } } /* merges any predefined areas that belong to the same section */ static void merge_areas(void) { area_header *table; section_info *section; size_t index, new_index; for (index = 0; index < area_num; index++) { table = &area_table[index]; if (table->size > 0) { section = §ion_info_table[area_info_table[index].section]; if (section->size > 0) { for (new_index = 0; new_index < index; new_index++) if (area_info_table[new_index].section == area_info_table[index].section) break; assert(new_index != index); move_area(index, new_index, link_local); section->size = area_table[new_index].size; } else section->size = table->size; } } } /* patches the symbol table */ static void patch_symbol_table(void) { coff_symbol *begin_symbol, *file_symbol, *func_symbol, *symbol; coff_symbol new_symbol; size_t count, index; begin_symbol = file_symbol = func_symbol = NULL; count = area_table[area_symbol].size / 18; symbol = (coff_symbol *) area_table[area_symbol].contents; for (index = 0; index < count; index += new_symbol.numaux + 1) { memcpy(&new_symbol, symbol, 18); if (new_symbol.sclass == COFF_SYMBOL_FILE) { if (file_symbol != NULL) memcpy((char *) &file_symbol->value, &index, 4); file_symbol = symbol; } symbol = (coff_symbol *) ((char *) symbol + 18); if (new_symbol.numaux != 0) { if ((new_symbol.type == COFF_SYMBOL_FUNC) && (new_symbol.scnum != sec_null)) { if (func_symbol != NULL) memcpy((char *) func_symbol + 12, &index, 4); func_symbol = symbol; } else if ((new_symbol.sclass == COFF_SYMBOL_FUNCAUX) && (strcmp(new_symbol.x.name, ".bf") == 0)) { if (begin_symbol != NULL) memcpy((char *) begin_symbol + 12, &index, 4); begin_symbol = symbol; } symbol = (coff_symbol *) ((char *) symbol + (new_symbol.numaux * 18)); } } } /* byte-swaps the symbol table */ static void swap_symbol_table(void) { coff_symbol *symbol; coff_symbol new_symbol; size_t count, index; count = area_table[area_symbol].size / 18; symbol = (coff_symbol *) area_table[area_symbol].contents; for (index = 0; index < count; index += new_symbol.numaux + 1) { memcpy(&new_symbol, symbol, 18); if (new_symbol.x.y.zeroes == 0) byte_swap((char *) &symbol->x.y.offset, 4); byte_swap((char *) &symbol->value, 4); byte_swap((char *) &symbol->scnum, 2); byte_swap((char *) &symbol->type, 2); symbol = (coff_symbol *) ((char *) symbol + 18); if (new_symbol.numaux != 0) { if ((new_symbol.type == COFF_SYMBOL_FUNC) && (new_symbol.scnum != sec_null)) { /* function auxilliary */ assert(new_symbol.numaux == 1); byte_swap((char *) symbol, 4); byte_swap((char *) symbol + 4, 4); byte_swap((char *) symbol + 8, 4); byte_swap((char *) symbol + 12, 4); } else if ((new_symbol.sclass == COFF_SYMBOL_FUNCAUX) && ((strcmp(new_symbol.x.name, ".bf") == 0) || (strcmp(new_symbol.x.name, ".ef") == 0))) { /* .bf or .ef auxilliary */ assert(new_symbol.numaux == 1); byte_swap((char *) symbol + 4, 2); byte_swap((char *) symbol + 12, 4); } else if ((new_symbol.sclass == COFF_SYMBOL_GLOBAL) && (new_symbol.scnum == sec_null)) { /* weak auxilliary */ assert(new_symbol.numaux == 1); byte_swap((char *) symbol, 4); byte_swap((char *) symbol + 4, 4); } else if ((new_symbol.type == COFF_SYMBOL_OBJECT) && (new_symbol.sclass == COFF_SYMBOL_LOCAL) && (new_symbol.scnum != sec_null)) { /* section auxilliary */ assert(new_symbol.numaux == 1); byte_swap((char *) symbol, 4); byte_swap((char *) symbol + 4, 2); byte_swap((char *) symbol + 6, 2); } symbol = (coff_symbol *) ((char *) symbol + (new_symbol.numaux * 18)); } } } /* byte-swaps the section header table */ static void swap_section_table(void) { coff_section *section; size_t count, index; count = area_table[area_section].size / sizeof(coff_section); section = (coff_section *) area_table[area_section].contents; for (index = 0; index < count; index++) { byte_swap((char *) §ion->paddr, 4); byte_swap((char *) §ion->vaddr, 4); byte_swap((char *) §ion->size, 4); byte_swap((char *) §ion->scnptr, 4); byte_swap((char *) §ion->relptr, 4); byte_swap((char *) §ion->lnnoptr, 4); byte_swap((char *) §ion->nreloc, 2); byte_swap((char *) §ion->nlnno, 2); byte_swap((char *) §ion->flags, 4); section = (coff_section *) ((char *) section + sizeof(coff_section)); } } /* adds most of the sections to the object file section header table */ static void build_section_table(void) { section_info *section_table; coff_section *section; size_t count, index, offset; /* This will add any required sections to the section header table. */ for (index = 1; index < sec_num; index++) { section_table = §ion_info_table[index]; if ((section_table->size > 0) || (section_table->targets > 0)) section_table->index = add_section((section_type) index); } /* We now work out the file offsets of every section. */ section = (coff_section *) area_table[area_section].contents; count = area_table[area_section].size / sizeof(coff_section); offset = sizeof(coff_header) + area_table[area_section].size; for (index = 0; index < count; index++) { /* BSS sections occupy no space in the object file and their * file offset should always be zero. */ if (!(section->flags & COFF_SECT_BSS) && (section->size > 0)) { /* It is recommended that the file offsets for section contents * are a multiple of four. */ section->scnptr = round_up(offset, 4); offset = section->scnptr + section->size; /* It is recommended that the file offsets for section relocations * are a multiple of four. */ if (section->nreloc != 0) { section->relptr = round_up(offset, 4); offset = section->relptr + (section->nreloc * 10); } /* It is recommended that the file offsets for section line numbers * are a multiple of four. */ if (section->nlnno != 0) { section->lnnoptr = round_up(offset, 4); offset = section->lnnoptr + (section->nlnno * 6); } } section = (coff_section *) ((char *) section + sizeof(coff_section)); } symbol_table_offset = round_up(offset, 4); } /* adds any line number symbols to the object file symbol table */ static size_t add_line_symbols(lineno **lnode, char **file, section_type section) { lineno *node; size_t count, lcount, scount; lcount = scount = 0; node = *lnode; do { if (strcmp(*file, node->file) != 0) { add_symbol(".lf", link_none, 0, scount, section); scount = 0; count = (strlen(node->file) + 17) / 18; add_symbol(".file", link_file, count, 0, sec_null); add_file_aux(node->file, count); *file = node->file; } *lnode = node; lcount++; scount++; } while (node = node->next); add_symbol(".lf", link_none, 0, scount, section); return lcount; } /* adds all of the symbols to the object file symbol table */ static void build_symbol_table(void) { area_header *table; member_node *node; alias *anode; lineno *lnode; section_info *section; coff_section *sect; char *file; size_t count, debugging, index, lines, new_index; /* This function adds all relevant symbols to the symbol table of the * object file in sorted order. The symbols are sorted by section index * and by their offset into that section, with function symbols coming * first, followed by object symbols, common symbols and finally external * symbols. */ file = source_file; if ((debug_format == DEBUG_COFF) || (debug_format == DEBUG_CODEVIEW)) { count = (strlen(source_file) + 17) / 18; add_symbol(".file", link_file, count, 0, sec_null); add_file_aux(source_file, count); debugging = 1; } else debugging = 0; for (index = 1; index < sec_num; index++) { section = §ion_info_table[index]; if ((section->size > 0) || (section->targets > 0)) { for (new_index = 0; new_index < area_num; new_index++) if (area_info_table[new_index].section == index) break; assert(new_index != area_num); table = &area_table[new_index]; table->offset = add_symbol(section->name, link_section, 1, 0, (section_type) section->index); add_section_aux(section->size, section->relocs, section->lines); } } for (index = 0; index < area_num; index++) { section = §ion_info_table[area_info_table[index].section]; if (debugging && (section->index != 0xFFFFFFFF)) { sect = ((coff_section *) area_table[area_section].contents) + section->index; lines = sect->lnnoptr; } else { sect = NULL; lines = 0; } for (anode = area_table[index].aliases; anode != NULL; anode = anode->next) if ((*anode->name != '.') && ((anode->link == link_local_function) || (anode->link == link_global_function) || (anode->link == link_weak_function))) { if (debugging) { if (sect != NULL) lines = sect->lnnoptr; else lines = 0; lnode = area_table[index].lines; } else lnode = NULL; if ((lnode != NULL) && (strcmp(file, lnode->file) != 0)) { count = (strlen(lnode->file) + 17) / 18; add_symbol(".file", link_file, count, 0, sec_null); add_file_aux(lnode->file, count); file = lnode->file; } if (anode->link == link_weak_function) new_index = add_symbol(anode->name, link_local_function, debugging, 0, (section_type) section->index); else new_index = add_symbol(anode->name, anode->link, debugging, 0, (section_type) section->index); if (debugging) { add_function_aux(new_index + 2, area_table[index].size, lines); add_symbol(".bf", link_none, 1, 0, (section_type) section->index); if (lnode != NULL) { add_beginend_aux(lnode->line); lines += add_line_symbols(&lnode, &file, (section_type) section->index) * 6; } else { add_beginend_aux(0); add_symbol(".lf", link_none, 0, 0, (section_type) section->index); } add_symbol(".ef", link_none, 1, area_table[index].size, (section_type) section->index); if (lnode != NULL) add_beginend_aux(lnode->line); else add_beginend_aux(0); } if (anode->link == link_weak_function) { add_symbol(anode->name, link_global_external_function, 1, 0, sec_null); add_weak_aux(new_index); } } for (node = area_table[index].members.head; node->next != NULL; node = node->next) if ((node->type == 0) && (*node->name != '.') && ((node->data.entry.link == link_local_function) || (node->data.entry.link == link_global_function) || (node->data.entry.link == link_weak_function))) { if (debugging) lnode = node->data.entry.lines; else lnode = NULL; if ((lnode != NULL) && (strcmp(file, lnode->file) != 0)) { count = (strlen(lnode->file) + 17) / 18; add_symbol(".file", link_file, count, 0, sec_null); add_file_aux(lnode->file, count); file = lnode->file; } if (node->data.entry.link == link_weak_function) new_index = add_symbol(node->name, link_local_function, debugging, node->offset, (section_type) section->index); else new_index = add_symbol(node->name, node->data.entry.link, debugging, node->offset, (section_type) section->index); node->data.entry.symidx = new_index; if (debugging) { add_function_aux(new_index + 2, node->data.entry.size, lines); add_symbol(".bf", link_none, 1, node->offset, (section_type) section->index); if (lnode != NULL) { add_beginend_aux(lnode->line); lines += add_line_symbols(&lnode, &file, (section_type) section->index) * 6; } else { add_beginend_aux(0); add_symbol(".lf", link_none, 0, 0, (section_type) section->index); } add_symbol(".ef", link_none, 1, node->offset + node->data.entry.size, (section_type) section->index); if (lnode != NULL) add_beginend_aux(lnode->line); else add_beginend_aux(0); } if (node->data.entry.link == link_weak_function) { add_symbol(node->name, link_global_external_function, 1, 0, sec_null); add_weak_aux(new_index); } } } for (index = 0; index < area_num; index++) { section = §ion_info_table[area_info_table[index].section]; for (anode = area_table[index].aliases; anode != NULL; anode = anode->next) if ((*anode->name != '.') && ((anode->link == link_local) || (anode->link == link_global) || (anode->link == link_weak))) if (anode->link == link_weak) { new_index = add_symbol(anode->name, link_local, 0, 0, (section_type) section->index); add_symbol(anode->name, link_global_external, 1, 0, sec_null); add_weak_aux(new_index); } else add_symbol(anode->name, anode->link, 0, 0, (section_type) section->index); for (node = area_table[index].members.head; node->next != NULL; node = node->next) if ((node->type == 0) && (*node->name != '.') && ((node->data.entry.link == link_local) || (node->data.entry.link == link_global) || (node->data.entry.link == link_weak))) { if (node->data.entry.link == link_weak) { new_index = add_symbol(node->name, link_local, 0, node->offset, (section_type) section->index); add_symbol(node->name, link_global_external, 1, 0, sec_null); add_weak_aux(new_index); } else new_index = add_symbol(node->name, node->data.entry.link, 0, node->offset, (section_type) section->index); node->data.entry.symidx = new_index; } } for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->name != NULL) && ((table->link == link_local_common) || (table->link == link_global_common) || (table->link == link_weak_common))) if (table->link == link_weak_common) { table->offset = add_symbol(table->name, link_local_common, 0, table->size, sec_null); add_symbol(table->name, link_global_external, 1, 0, sec_null); add_weak_aux(table->offset); } else table->offset = add_symbol(table->name, table->link, 0, table->size, sec_null); } for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->name != NULL) && ((table->link == link_global_external_function) || (table->link == link_weak_external_function))) { table->offset = add_symbol(table->name, table->link, 0, 0, sec_null); for (anode = table->aliases; anode != NULL; anode = anode->next) add_symbol(anode->name, anode->link, 0, 0, sec_null); } } for (index = area_num; index < highest_area; index++) { table = &area_table[index]; if ((table->name != NULL) && ((table->link == link_global_external) || (table->link == link_weak_external))) { table->offset = add_symbol(table->name, table->link, 0, 0, sec_null); for (anode = table->aliases; anode != NULL; anode = anode->next) add_symbol(anode->name, anode->link, 0, 0, sec_null); } } patch_symbol_table(); if (host_endian != target_endian) swap_symbol_table(); } /* writes out the entire object file */ static void write_object(void) { coff_section *section; char *buffer; coff_header header; size_t area_index, count, dummy, index, offset; dummy = 0; count = area_table[area_section].size / sizeof(coff_section); header.magic = COFF_ARCH_386; header.nscns = count; header.timdat = (unsigned long) time(NULL); header.symptr = symbol_table_offset; header.nsyms = area_table[area_symbol].size / 18; header.opthdr = 0; header.flags = 0; if (host_endian != target_endian) { byte_swap((char *) &header.magic, 2); byte_swap((char *) &header.nscns, 2); byte_swap((char *) &header.timdat, 4); byte_swap((char *) &header.symptr, 4); byte_swap((char *) &header.nsyms, 4); byte_swap((char *) &header.opthdr, 2); byte_swap((char *) &header.flags, 2); } write(new_outfile, &header, sizeof(coff_header)); /* We now put out the section table, but that may need to be byte-swapped * before it is written. However, we'll be using the section table again * so we may need to un-swap it after writing it to the object file. */ if (host_endian != target_endian) swap_section_table(); write(new_outfile, area_table[area_section].contents, area_table[area_section].size); if (host_endian != target_endian) swap_section_table(); section = (coff_section *) area_table[area_section].contents; offset = sizeof(coff_header) + area_table[area_section].size; for (index = 0; index < count; index++) { if (!(section->flags & COFF_SECT_BSS) && (section->size > 0)) { for (area_index = 1; area_index < area_num; area_index++) if (section_info_table[area_info_table[area_index].section].index == index) break; assert(area_index != area_num); /* It is recommended that the file offsets for section contents * are a multiple of four. */ if (offset % 4) write(new_outfile, &dummy, 4 - (offset % 4)); offset = round_up(offset, 4) + section->size; assert(area_table[area_index].contents != NULL); write(new_outfile, area_table[area_index].contents, section->size); /* It is recommended that the file offsets for section relocations * are a multiple of four. */ if (section->nreloc != 0) { if (offset % 4) write(new_outfile, &dummy, 4 - (offset % 4)); offset = round_up(offset, 4) + (section->nreloc * 10); buffer = (char *) getspace(section->nreloc * 10); write_relocations(&area_table[area_index].members, buffer); write(new_outfile, buffer, section->nreloc * 10); freespace(buffer); } /* It is recommended that the file offsets for section line numbers * are a multiple of four. */ if (section->nlnno != 0) { if (offset % 4) write(new_outfile, &dummy, 4 - (offset % 4)); offset = round_up(offset, 4) + (section->nlnno * 6); buffer = (char *) getspace(section->nlnno * 6); write_lines(area_index, buffer); write(new_outfile, buffer, section->nlnno * 6); freespace(buffer); } } section = (coff_section *) ((char *) section + sizeof(coff_section)); } /* Write out the symbol table. */ if (offset % 4) write(new_outfile, &dummy, 4 - (offset % 4)); write(new_outfile, area_table[area_symbol].contents, area_table[area_symbol].size); /* Write out the string table plus four bytes for the length. */ dummy = area_table[area_string].size + 4; if (host_endian != target_endian) byte_swap((char *) &dummy, 4); write(new_outfile, &dummy, 4); write(new_outfile, area_table[area_string].contents, area_table[area_string].size); } /* returns the name associated with a specific type of linkage */ static char *linkage_name(linkage link) { static char *type; switch (link) { case link_none: type = "none"; break; case link_file: type = "file"; break; case link_section: type = "section"; break; case link_global: type = "global data"; break; case link_weak: type = "weak data"; break; case link_local_common: type = "local common"; break; case link_global_common: type = "global common"; break; case link_weak_common: type = "weak common"; break; case link_global_external: type = "global ext data"; break; case link_weak_external: type = "weak ext data"; break; case link_local_function: type = "local func"; break; case link_global_function: type = "global func"; break; case link_weak_function: type = "weak func"; break; case link_global_external_function: type = "global ext func"; break; case link_weak_external_function: type = "weak ext func"; break; default: type = "local data"; break; } return type; } /* displays a summary of all the line numbers in a list */ static void print_lines(lineno *node) { char *file; file = ""; while (node != NULL) { if (strcmp(file, node->file) != 0) { put_message(" %s:\n", node->file); file = node->file; } if (object_generating) put_message(" %6lu ", node->addr.offset); else put_message(" %s: ", node->addr.label); put_message("line %lu\n", node->line); node = node->next; } } /* displays a summary of all the members in an area */ static void print_members(size_t area) { member_node *node; for (node = area_table[area].members.head; node->next != NULL; node = node->next) { put_message(" %6lu ", node->offset); if (node->type == 0) { put_message("entry (\"%s\", %s, %lu)\n", node->name, linkage_name(node->data.entry.link), node->data.entry.size); print_lines(node->data.entry.lines); } else { put_message("fixup ("); if (node->name != NULL) put_message("%s", node->name); else put_message("%s", area_table[node->data.target.area].name); if (node->data.target.offset) { if (node->data.target.offset > 0) put_message("+"); put_message("%ld", node->data.target.offset); } put_message(", %u)\n", node->type); } } } /* displays a summary of all allocated areas */ static void print_areas(void) { area_header *table; alias *anode; size_t index; put_message("area disp size align fill type name\n"); for (index = 0; index < highest_area; index++) { table = &area_table[index]; if (table->name != NULL) { put_message("%4lu %4lu %4lu %4lu 0x%02X %-15s %s\n", index, table->offset, table->size, table->align, table->fill, linkage_name(table->link), table->name); for (anode = table->aliases; anode != NULL; anode = anode->next) put_message(" (alias %-15s %s)\n", linkage_name(anode->link), anode->name); print_members(index); print_lines(table->lines); } } } /* sets up the source and destination filenames for the put interface */ /* and then returns the output file handle */ int psetfiles2(char *source_name, char *dest_name, int no_file) { int oflags; if (put_trace) put_message("psetfiles2(source_name = \"%s\", dest_name = \"%s\", " "no_file = %d)\n", source_name, dest_name, no_file); assert(!put_unterminated); assert((source_file == NULL) && (dest_file == NULL)); assert((source_name != NULL) && (*source_name != '\0')); assert((dest_name != NULL) && (*dest_name != '\0')); source_file = source_name; dest_file = dest_name; oflags = O_WRONLY | O_CREAT | O_TRUNC; #ifdef _WIN32 oflags |= O_BINARY; #endif /* _WIN32 */ fflush(stdout); if (no_file) new_outfile = fileno(stdout); else if ((new_outfile = open(dest_name, oflags, 0664)) == -1) put_error("cannot create output file `%s'", dest_name); if (!object_generating) { /* Because the backend writes all of the generated assembler to the * standard output, we need to redirect the standard output to a * file in order to pass it to the assembler. This can be prevented * by setting no_file to a non-zero value, in which case the generated * assembler will be sent to the standard output. */ if ((old_outfile = dup(fileno(stdout))) == -1) put_error("cannot duplicate output file handle"); if (dup2(new_outfile, fileno(stdout)) == -1) put_error("cannot reassign output file handle"); } if (put_trace) put_message("psetfiles2() returns (new_outfile = %d)\n", new_outfile); return new_outfile; } /* sets up the source and destination filenames for the put interface */ void psetfiles(char *source_name, char *dest_name, int no_file) { if (put_trace) put_message("psetfiles(source_name = \"%s\", dest_name = \"%s\", " "no_file = %d)\n", source_name, dest_name, no_file); assert(!put_unterminated); psetfiles2(source_name, dest_name, no_file); } /* initialises the put interface */ void pinitialise(int version, int release, int language) { char buffer[256]; unsigned long dummy; int ver; if (put_trace) put_message("pinitialise(version = %d, release = %d, " "language = 0x%08lX)\n", version, release, language); assert(!put_initialised); assert(!put_unterminated); assert((source_file != NULL) && (dest_file != NULL)); /* At this point we can perform a once-only initialisation of * target-specific features as these will not change on subsequent * calls to preinitialise. This is also done to prevent conditional * compilation and so facilitate cross-compilation. Note that this * module should have no dependencies on other modules and should * provide only a functional interface without sharing data. */ target_processor = (language & 0xFF00) >> 8; if (target_processor == TARGET_386) { assembler = &coff_i386_assembler; relocation_table = coff_i386_relocation_table; rel_mask_table = coff_i386_rel_mask_table; } else put_error("unsupported target processor `%u'", target_processor); dummy = 1; if (*((unsigned char *) &dummy) == 1) host_endian = LITTLE_ENDIAN; else host_endian = BIG_ENDIAN; target_endian = LITTLE_ENDIAN; /* Set the default external prefix to be an underscore. However, this will * not be enabled unless the prepend_prefix global variable is set. */ if (external_prefix == NULL) pexternalprefix("_"); init_area_table(); if (!object_generating && !suppress_code_gen) { write_header(); enter_section(sec_text); } put_initialised = 1; put_unterminated = 1; /* If any of the following version strings exist then place them * in the ident section of the object file. They serve as a * way to better identify the compiler which produced the output. */ ver = 0; if (epc_version_string) { sscanf(epc_version_string, "%[^\n]", buffer); pident(buffer); ver = 1; } if (epc_front_end_version) { sscanf(epc_front_end_version, "%[^\n]", buffer); pident(buffer); ver = 1; } if (epc_back_end_version) { sscanf(epc_back_end_version, "%[^\n]", buffer); pident(buffer); ver = 1; } if (ver && !object_generating) write_code("\n"); } /* reinitialises the put interface */ void preinitialise(void) { char buffer[256]; int ver; if (put_trace) put_message("preinitialise()\n"); assert(put_initialised); assert(!put_unterminated); assert((source_file != NULL) && (dest_file != NULL)); init_area_table(); if (!object_generating && !suppress_code_gen) { write_header(); enter_section(sec_text); } put_unterminated = 1; /* If any of the following version strings exist then place them * in the ident section of the object file. They serve as a * way to better identify the compiler which produced the output. */ ver = 0; if (epc_version_string) { sscanf(epc_version_string, "%[^\n]", buffer); pident(buffer); ver = 1; } if (epc_front_end_version) { sscanf(epc_front_end_version, "%[^\n]", buffer); pident(buffer); ver = 1; } if (epc_back_end_version) { sscanf(epc_back_end_version, "%[^\n]", buffer); pident(buffer); ver = 1; } if (ver && !object_generating) write_code("\n"); } /* terminates the put interface */ void pterminate(int *sizes) { size_t area, index; if (put_trace) put_message("pterminate(sizes = 0x%08lX)\n", sizes); assert(put_initialised); assert(put_unterminated); assert((source_file != NULL) && (dest_file != NULL)); assert((section_level == 0) && (scope_level == 0)); assert(sizes != NULL); assert(max_stack_size == 0); assert((area_table[area_stack].size == 0) && (area_table[area_params].size == 0)); /* Display and check the area sizes passed in by the backend. The * code area is dealt with by the backend and so its size is ignored. * The final size of the BSS area is set from here since we would not * normally initialise any space in this area. */ if (!suppress_code_gen) { if (put_trace) put_message("\n"); for (index = 1; index <= 10; index++, sizes++) { if (put_trace) put_message("%3lu %5lu %s\n", index, *sizes, area_info_table[index].area); if (index == area_bss) { if (area_table[area_bss].size >= *sizes) area_table[area_bss].size = *sizes; else pdinit(area_bss, 0, *sizes); } else if ((index != area_code) && (*sizes != area_table[index].size)) put_error("mismatching sizes for the length of the %s area", area_info_table[index].area); } if (put_trace) put_message("\n"); } /* Any required library directives get specified here. */ add_directive(DIRECT_LIBRARY, "libc"); /* The following code is used to plant a dummy local common symbol into * the symbol table of the object file to indicate that it was compiled * using an EPC compiler. */ area = pcommon2(1, "epc.compiled"); pendcommon2(area, 1, 0, 1); /* These are hacks to work with the Microsoft C library and are not * required for other libraries. */ pxname(0, "__fltused"); if (area_table[area_tls].size != 0) pxname(0, "__tls_used"); put_unterminated = 0; } /* finishes writing the destination output file */ void pgenerateobject(char *dest_name) { size_t index; if (put_trace) put_message("pgenerateobject(dest_name = \"%s\")\n", dest_name); assert(put_initialised); assert(!put_unterminated); assert((source_file != NULL) && (dest_file != NULL)); assert((dest_name != NULL) && (*dest_name != '\0')); assert(strcmp(dest_file, dest_name) == 0); if (!suppress_code_gen) { if (put_trace) { put_message("\narea table before processing:\n\n"); print_areas(); } /* We must first move all user-defined data areas into their * corresponding parent areas. */ move_data_areas(); /* We may now have to move certain common areas into the BSS area if * required, depending on the no_commons flag and the format of file * being generated. */ if (no_commons) move_commons(0); else move_commons(1); /* We now try to remove any unnecessary relocations to local functions * and convert all functions to entries in the code area if we are * generating an object file. */ if (object_generating) { optimise_relocations(); move_functions(); } merge_areas(); if (put_trace) { put_message("\narea table after processing:\n\n"); print_areas(); } /* Apart from the section headers and function code when generating * assembler, all of the contents of the output file is written here. */ if (object_generating) { count_relocations(); count_lines(); build_section_table(); build_symbol_table(); write_addends(); write_object(); } else { /* If there have been references to the .epctext label but there * were no functions in the text section then we must output the * .epctext label anyway and generate an empty text section. */ if ((first_function == 0) && (area_table[area_code].offset > 0)) write_code("%s\t%s %s\n", area_table[area_code].name, assembler->label, assembler->byte); else if (first_function != 0) write_code("%s\t%s %s\n", label_name(area_table[area_code].name, area_code, 0, 1), assembler->label, assembler->byte); write_function_aliases(); write_external_functions(); write_areas(); write_commons(); write_external_objects(); write_code("\t%s\n", assembler->end); } } /* At this point we can reset all of the global variables, free up the * entire area table and reset some of the fields in the section table, * just in case we need to generate another output file in the same * compilation. */ section_stack[0] = sec_null; current_section = sec_null; symbol_table_offset = 0; current_filename = NULL; first_function = 0; free_area_table(); for (index = sec_null; index < sec_num; index++) { section_info_table[index].index = 0xFFFFFFFF; section_info_table[index].size = 0; section_info_table[index].targets = 0; section_info_table[index].relocs = 0; section_info_table[index].lines = 0; section_info_table[index].align = 0; } fflush(stdout); if (!object_generating) { /* We now need to restore the standard output file stream to its * previous value as we may wish to write another assembler file * or display a message on the original standard output. */ if (dup2(old_outfile, fileno(stdout)) == -1) put_error("cannot reassign output file handle"); close(old_outfile); } if (new_outfile != fileno(stdout)) close(new_outfile); if (suppress_code_gen) premoveobject(); source_file = NULL; dest_file = NULL; } /* removes the resulting output file */ void premoveobject(void) { if (put_trace) put_message("premoveobject()\n"); assert(dest_file != NULL); remove(dest_file); } /* controls the put interface options and indicates if */ /* the output should be an assembler or object file and if */ /* position-independent code should be produced */ void psetoptions2(int options, int object, int debug, int pic) { if (put_trace) put_message("psetoptions2(options = 0x%08lX, object = %d, debug = %d, " "pic = %d)\n", options, object, debug, pic); assert(!put_initialised); assert(!put_unterminated); if (options & 0x10) annotate_assembler = 1; if (options & 0x20) no_commons = 1; if (options & 0x40) prepend_prefix = 1; if (options & 0x80) put_memory_trace = 1; if (options & 0x1000) auto_weak_functions = 1; object_generating = object; if ((debug == DEBUG_COFF) || (debug == DEBUG_CODEVIEW)) debug_format = debug; else put_error("unsupported debugging format `%u'", debug); } /* controls the put interface options */ void psetoptions(int options) { if (put_trace) put_message("psetoptions(options = 0x%08lX)\n", options); assert(!put_initialised); assert(!put_unterminated); psetoptions2(options, 1, 0, 0); } /* returns 1 if data initialiser constants need to be byte-swapped before */ /* being passed to pd2(), pd4() and pdbytes(), else returns 0 */ int pdswapneeded(void) { int r; if (put_trace) put_message("pdswapneeded()\n"); assert(put_initialised); assert(put_unterminated); r = 0; if (put_trace) put_message("pdswapneeded() returns %d\n", r); return r; } /* sets the prefix to be prepended to externally-visible symbol names */ void pexternalprefix(char *prefix) { if (put_trace) put_message("pexternalprefix(prefix = \"%s\")\n", prefix); assert(prefix != NULL); external_prefix = prefix; external_prefix_size = strlen(prefix); } /* sets the default filler byte for all areas */ void pdefaultfiller(int filler) { if (put_trace) put_message("pdefaultfiller(filler = 0x%08lX)\n", filler); assert((filler >= 0) && (filler < 256)); area_filler = filler; } /* enforces the suppression of code generation */ void pfaulty(void) { if (put_trace) put_message("pfaulty()\n"); suppress_code_gen = 1; } /* turns on put tracing */ void pmonon(void) { put_trace = 1; } /* turns off put tracing */ void pmonoff(void) { put_trace = 0; } #ifdef __cplusplus } #endif