This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[patch gas]: Add pseudos for generation of runtime function entries by gas
- From: Kai Tietz <ktietz70 at googlemail dot com>
- To: Binutils <binutils at sourceware dot org>
- Cc: Dave Korn <dave dot korn dot cygwin at googlemail dot com>
- Date: Tue, 11 Aug 2009 12:28:40 +0200
- Subject: [patch gas]: Add pseudos for generation of runtime function entries by gas
Hello,
I would like to show the patch for .pdata and .xdata generation of
pe-coff targets via gas, and to get some feed-back.
This patch includes support for arm, ppc, arm, sh (3&4), mips, and
x64. As for x86 there is no OS support for runtime function
information, I spared this part. It would just increase executable
size for x86 PE and there is no real gain for this target.
Short overview:
There are at the moment three different function entry formats preset.
The first is the MIPS one (could be also used here for x86). The
second version is for ARM, PPC, SH3, and SH4 mainly for Windows CE.
The third is the IA64 and x64 version. Note, the IA64 isn't
implemented yet, but to find information about it, please see
specification about IA64 on
http://download.intel.com/design/Itanium/Downloads/245358.pdf file.
The first version has just entries in the pdata section:
BeginAddress, EndAddress, ExceptionHandler, HandlerData, and
PrologueEndAddress. Each value is a pointer to the corresponding data
and has size of 4 bytes.
The second variant has the following entries in the pdata section.
BeginAddress, PrologueLength (8 bits), EndAddress (22 bits),
Use-32-bit-instruction (1 bit), and Exception-Handler-Exists (1 bit).
If the FunctionLength is zero, or the Exception-Handler-Exists bit is
true, a PDATA_EH block is placed directly before function entry
(expressed by .seh_eh pseudo).
The third version has a function entry block of BeginAddress (RVA),
EndAddress (RVA), and UnwindData (RVA). The description of the
prologue, excepetion-handler, and additional SEH data is stored within
the UNWIND_DATA field in the xdata section.
The pseudos:
.seh_proc <fct_name>
This specifies, that a SEH block begins for the function
<fct_name>. This is valid for all targets.
.seh_endprologue
By this pseudo the location of the prologue end-address (taken by
the current code address of the appearance of this pseudo). Valid for
all targets.
.seh_handler <handler>[,<handler-data>]
This pseudo specifies the handler function to be used. For version
2 the handler-data field specifies the user optional data block. For
version 3 the handler-data field can be a rva to user-data (for
FHANDLER), if the name is @unwind the UHANDLER unwind block is
generated, and if it is @except (or not specified at all) EHANDLER
exception block is generated.
.seh_eh
This pseudo is used for version 2 to indicate the location of the
function begin in assembly. Here the PDATA_EH data is may stored to.
.seh_32/.seh_no32
This pseudos are just used for version 2 (see above for
description). At the moment it defaults to no32, if not specified.
.seh_endproc
By this pseudo the end of the SEH block is specified.
.seh_setframe <reg>,<offset>
By this pseudo the frame-register and the offset (value between
0-240 with 16-byte alignment) can be specified. This is just used by
version 3.
.seh_stackalloc <size>
By this stack allocation in code is described for version 3.
.seh_pushreg <reg>
By this a general register push in code is described for version 3.
.seh_savereg <reg>
By this a general register save to memory in code is described for
version 3.
.seh_savemm <mm>
By this a mm register save to memory in code is described for version 3.
.seh_savexmm
By this a xmm register save to memory in code is described for version 3.
.seh_pushframe
By this information about entry kind can be described for version 3.
.seh_scope <begin>,<end>,<handler>,<jump>
By this SCOPED entries for unwind or exceptions can be specified
for version 3. This is just valid for UHANDLE and EHANDLER xdata
descriptor and a global handler has to be specified. For handler and
jump arguments, names of @1,@0, and @null can be used and they are
specifying that a constant instead of a rva has to be used.
2009-08-11 Kai Tietz <kai.tietz@onevision.com>
* config/obj-coff-seh.c: New file.
* config/obj-coff-seh.h: Likewise.
* config/obj-coff.c (obj-coff-seh.c): Add include.
(coff_pseudo_table): Add new .seh... commands.
* config/obj-coff.h (obj_coff_seh_do_final): Add new
function prototype.
(obj_coff_generate_pdata): New obj-coff hook.
* gas/write.c (size_seg): Avoid sizing of already sized
sections.
(write_object_file): Call conditional hook
objc_coff_generate_pdata.
* Makefile.in: Add dependencies for new files.
* Makefile.am: Regenerated.
Tested for x86_64-pc-mingw32, i686-pc-linux, x86_64-pc-linux,
i686-pc-mingw32, and for cygwin. Base test I did for mips and arm.
This patch needs additional documentation in gas, and a lot of tests,
but first I would like to get response about it.
Cheers,
Kai
--
| (\_/) This is Bunny. Copy and paste
| (='.'=) Bunny into your signature to help
| (")_(") him gain world domination
Index: src/gas/config/obj-coff-seh.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gas/config/obj-coff-seh.c 2009-08-11 10:51:53.557000000 +0200
@@ -0,0 +1,1304 @@
+#include "obj-coff-seh.h"
+
+/* Forward declarations. */
+static seh_kind seh_get_target_kind (void);
+static int seh_symbol (bfd *, const char *, const char *, const char *, asection *, int, int);
+static void seh_reloc (bfd *, bfd_size_type, int, int);
+static void save_relocs (asection *sec);
+static asection *quick_section (bfd *abfd, const char *name, int flags, int align);
+static void seh_symbol_init (bfd *abfd, unsigned int added);
+static void seh_emit_rva (const char *);
+static void seh_emit_long (const char *);
+static void seh_make_globl (char *);
+static segT seh_make_section (void);
+static segT seh_make_section2 (const char *section_name, unsigned flags);
+static char *seh_make_xlbl_name (seh_context *);
+static char *make_seh_text_label (seh_context *c, symbolS **addr);
+
+static void seh_write_text_eh_data (const char *hnd, const char *hnd_data);
+static void seh_emit_rva (const char *name);
+static int seh_needed_unwind_info (seh_context *);
+static void seh_fill_pcsyms (const seh_context *c, char **, int *);
+static size_t seh_getelm_data_size (const seh_context *, int, int);
+static size_t seh_getsize_of_unwind_entry (seh_context *, int, int, int);
+static void seh_make_unwind_entry (const seh_context *, char *, int, int, int, unsigned char *, size_t *, int);
+static size_t seh_getsize_unwind_data (seh_context *);
+static void seh_create_unwind_data (seh_context *, unsigned char *, size_t);
+static void seh_make_function_entry_xdata (seh_context *, char *, char *, char *, unsigned char *, size_t *,int);
+static seh_scope_elem *seh_x64_makescope_elem (seh_context *, const char *, const char *, const char *, const char *);
+
+/* Local data. */
+static asymbol **symtab;
+static int symptr;
+static arelent *reltab = 0;
+static int relcount = 0, relsize = 0;
+
+static seh_context *seh_ctx_root = NULL;
+static seh_context *seh_ctx = NULL;
+static seh_context *seh_ctx_cur = NULL;
+
+/* Write xdata for arm, sh3, sh4, and ppc. */
+static void
+seh_write_text_eh_data (const char *hnd, const char *hnd_data)
+{
+ if (!hnd || *hnd==0)
+ return;
+ if (hnd[0] == '@')
+ seh_emit_long ("0");
+ else
+ seh_emit_long (hnd);
+ if (!hnd_data || hnd_data[0] == '@')
+ seh_emit_long ("0");
+ else
+ seh_emit_long (hnd_data);
+}
+
+/* Generate initial pdata for x64 and mips. */
+static void
+make_function_entry_pdata (seh_context *c)
+{
+ segT sec = NULL;
+ segT current_seg = now_seg;
+ subsegT current_subseg = now_subseg;
+
+ sec = seh_make_section ();
+ switch (seh_get_target_kind ())
+ {
+ case seh_kind_x64:
+ subseg_set (sec, 0);
+ seh_emit_rva (c->func_name);
+ seh_emit_rva (c->end_symbol);
+ seh_emit_rva (c->xdata_first);
+ break;
+ case seh_kind_mips:
+ subseg_set (sec, 0);
+ seh_emit_long (c->func_name);
+ seh_emit_long (c->end_symbol);
+ if (c->handler_name == NULL)
+ seh_emit_long ("0");
+ else if (c->handler_name[0] == '@')
+ {
+ if (strcasecmp (c->handler_name, "@1") == 0)
+ seh_emit_long ("1");
+ else
+ seh_emit_long ("0");
+ }
+ else
+ seh_emit_long (c->handler_name);
+ if (c->handler_data_name == NULL || c->handler_data_name[0] == '@')
+ seh_emit_long ("0");
+ else
+ seh_emit_long (c->handler_data_name);
+ seh_emit_long (c->endprologue_symbol ? c->endprologue_symbol : c->func_name);
+ break;
+ default:
+ break;
+ }
+ subseg_set (current_seg, current_subseg);
+}
+
+static void
+seh_x64_write_xdata (void)
+{
+ seh_context *h;
+ size_t xdata_size = 0, count_syms = 0;
+ size_t xdata_offs = 0;
+ unsigned char *data;
+ segT seg_xdata;
+ bfd *abfd = stdoutput;
+
+ h = seh_ctx_root;
+ if (h->done)
+ return;
+ while (h != NULL)
+ {
+ h->xdata_offset = xdata_size;
+ xdata_size += seh_getsize_unwind_data (h);
+ count_syms += h->count_syms;
+ h = h->next;
+ }
+
+ if (xdata_size == 0)
+ return;
+
+ seh_symbol_init (abfd, count_syms);
+ data = xmalloc (xdata_size);
+ seg_xdata = quick_section (abfd, ".xdata", SEC_HAS_CONTENTS, 3);
+ seg_xdata->contents = data;
+ memset (data, 0, xdata_size);
+ bfd_set_section_size (abfd, seg_xdata, xdata_size);
+ h = seh_ctx_root;
+ while (h != NULL)
+ {
+ xdata_offs = h->xdata_offset;
+ h->section = seg_xdata;
+ h->abfd = abfd;
+ if (h->done == 0)
+ {
+ h->done = 1;
+ seh_create_unwind_data (h, data, xdata_offs);
+ h->done = 1;
+ }
+ h = h->next;
+ }
+ save_relocs (seg_xdata);
+ bfd_set_symtab (abfd, symtab, symptr);
+ bfd_set_section_contents (abfd, seg_xdata, data, 0, xdata_size);
+}
+
+static void
+seh_arm_create_pdata (seh_context *c, unsigned char *data, size_t pdata_offs)
+{
+ int idx;
+ unsigned int val;
+ valueT func_len = 0;
+ valueT prolog_len = 0;
+ valueT start_len = 0;
+ func_len = resolve_symbol_value (c->end_addr);
+ start_len = resolve_symbol_value (c->start_addr);
+ if (c->endprologue_addr)
+ prolog_len = resolve_symbol_value (c->endprologue_addr);
+ else
+ prolog_len = start_len;
+ func_len -= start_len;
+ prolog_len -= start_len;
+ if (!c || !data)
+ return;
+ /* $$$$ */
+ idx = seh_symbol (c->abfd, c->start_symbol, "", "", UNDSEC, BSF_GLOBAL, 0);
+ seh_reloc (c->abfd, pdata_offs, BFD_RELOC_32, idx);
+ val = (unsigned int) func_len;
+ val <<= 8;
+ val |= ((unsigned int) prolog_len & 0xffU);
+ if (c->use_instruction_32)
+ val |= 0x40000000U;
+ if (c->handler_written)
+ val |= 0x80000000U;
+ bfd_put_32 (c->abfd, (bfd_vma) val, data + pdata_offs + 4);
+}
+
+static void
+seh_arm_write_pdata (void)
+{
+ seh_context *h;
+ size_t pdata_size = 0, count_syms = 0;
+ size_t pdata_offs = 0;
+ unsigned char *data;
+ segT seg_pdata;
+ bfd *abfd = stdoutput;
+
+ h = seh_ctx_root;
+ if (h->done)
+ return;
+ while (h != NULL)
+ {
+ h->xdata_offset = pdata_size;
+ pdata_size += 8;
+ count_syms += 1;
+ h = h->next;
+ }
+
+ if (pdata_size == 0)
+ return;
+
+ seh_symbol_init (abfd, count_syms);
+ data = xmalloc (pdata_size);
+ seg_pdata = quick_section (abfd, ".pdata", SEC_HAS_CONTENTS, 3);
+ seg_pdata->contents = data;
+ memset (data, 0, pdata_size);
+ bfd_set_section_size (abfd, seg_pdata, pdata_size);
+ h = seh_ctx_root;
+ while (h != NULL)
+ {
+ pdata_offs = h->xdata_offset;
+ h->section = seg_pdata;
+ h->abfd = abfd;
+ if (h->done != 0)
+ {
+ seh_arm_create_pdata (h, data, pdata_offs);
+ h->done = 1;
+ }
+ h = h->next;
+ }
+ save_relocs (seg_pdata);
+ bfd_set_symtab (abfd, symtab, symptr);
+ bfd_set_section_contents (abfd, seg_pdata, data, 0, pdata_size);
+}
+
+void
+obj_coff_seh_do_final (void)
+{
+ switch (seh_get_target_kind ())
+ {
+ case seh_kind_mips:
+ default:
+ break;
+ case seh_kind_arm:
+ seh_arm_write_pdata ();
+ break;
+ case seh_kind_x64:
+ seh_x64_write_xdata ();
+ break;
+ }
+}
+
+static void
+seh_x64_make_prologue_element (int kind, int reg, bfd_vma off)
+{
+ seh_prologue_element *n;
+ if (seh_ctx_cur == NULL)
+ return;
+ if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max)
+ {
+ seh_ctx_cur->elems = (seh_prologue_element *)
+ xrealloc (seh_ctx_cur->elems,
+ ((seh_ctx_cur->elems_max + 8) * sizeof (seh_prologue_element)));
+ seh_ctx_cur->elems_max += 8;
+ }
+ n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count];
+ memset (n, 0, sizeof (seh_prologue_element));
+ n->kind = kind;
+ n->reg = reg;
+ n->offset = off;
+ n->pc_symbol = make_seh_text_label (seh_ctx_cur, &(n->pc_addr));
+ seh_ctx_cur->elems_count += 1;
+}
+
+static int
+seh_x64_read_reg (const char *tok, int kind, int *regno)
+{
+ static const char *frame_regs[16] =
+ { "cfa", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi",
+ "r8","r9","r10","r11","r12","r13","r14","r15" };
+ static const char *int_regs[16] =
+ { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi",
+ "r8","r9","r10","r11","r12","r13","r14","r15" };
+ static const char *xmm_regs[16] =
+ { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
+ "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" };
+ static const char *mm_regs[16] =
+ { "xmm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+ "xmm8", "mm9", "mm10","mm11","mm12","mm13","mm14","mm15" };
+ const char **p = NULL;
+ char name_end;
+ char *symbol_name = NULL;
+ int i;
+
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
+ input_line_pointer++;
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
+ input_line_pointer++;
+ switch (kind)
+ {
+ case 0:
+ p = frame_regs;
+ break;
+ case 1:
+ p = int_regs;
+ break;
+ case 2:
+ p = mm_regs;
+ break;
+ case 3:
+ p = xmm_regs;
+ break;
+ default:
+ abort ();
+ }
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ return 0;
+ }
+ if (*input_line_pointer == '%')
+ ++input_line_pointer;
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ for (i = 0; i < 16; i++)
+ {
+ if (! strcasecmp (p[i], symbol_name))
+ break;
+ }
+ if (i == 16)
+ {
+ as_warn (_("In %s we found the invalid register name %s.\n"),
+ tok, symbol_name);
+ }
+ *input_line_pointer = name_end;
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
+ input_line_pointer++;
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
+ input_line_pointer++;
+ *regno = i;
+ return i != 16;
+}
+
+static int
+seh_read_offset (const char *tok, bfd_vma *off)
+{
+ bfd_vma r, v = 0, base = 10;
+ int had_one = 0;
+
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
+ input_line_pointer++;
+ if (*input_line_pointer == '0')
+ {
+ ++input_line_pointer;
+ had_one = 1;
+ base = 8;
+ switch ((*input_line_pointer))
+ {
+ case 'x':
+ case 'X':
+ base = 16;
+ ++input_line_pointer;
+ break;
+ case 'd':
+ case 'D':
+ base = 10;
+ input_line_pointer++;
+ break;
+ case 'o':
+ case 'O':
+ base = 8;
+ input_line_pointer++;
+ break;
+ }
+ }
+ while (*input_line_pointer != 0)
+ {
+ if (input_line_pointer[0] >= '0' && input_line_pointer[0] <='9')
+ r = (bfd_vma) (input_line_pointer[0] - '0');
+ else if (base == 16 && input_line_pointer[0] >= 'a' && input_line_pointer[0] <='f')
+ r = (bfd_vma) ((input_line_pointer[0] - 'a') + 10);
+ else if (base == 16 && input_line_pointer[0] >= 'A' && input_line_pointer[0] <='F')
+ r = (bfd_vma) ((input_line_pointer[0] - 'A') + 10);
+ else
+ break;
+ input_line_pointer++;
+ v *= base;
+ v += r;
+ had_one = 1;
+ }
+ *off = v;
+ if (had_one == 0)
+ {
+ as_warn (_("In %s we expect a number.\n"),
+ tok);
+ }
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
+ input_line_pointer++;
+ if (*input_line_pointer == ',')
+ ++input_line_pointer;
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t')
+ input_line_pointer++;
+ return had_one != 0;
+}
+
+static void
+obj_coff_seh_32 (int what)
+{
+ if (seh_ctx_cur == NULL)
+ {
+ as_fatal (_(".seh_eh requires to be in .seh_proc/.seh_endproc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ seh_ctx_cur->use_instruction_32 = (what ? 1 : 0);
+ if (seh_get_target_kind () == seh_kind_arm)
+ as_warn (_(".seh_%s32 is ignored for this target."), (what ? "" : "no"));
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_seh_eh (int what ATTRIBUTE_UNUSED)
+{
+ if (seh_ctx_cur == NULL)
+ {
+ as_fatal (_(".seh_eh requires to be in .seh_proc/.seh_endproc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (seh_get_target_kind () == seh_kind_arm)
+ {
+ seh_ctx_cur->handler_written = 1;
+ /* write block to .text if exception handler is set. */
+ seh_write_text_eh_data (seh_ctx_cur->handler_name, seh_ctx_cur->handler_data_name);
+ }
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_seh_handler (int what ATTRIBUTE_UNUSED)
+{
+ char *symbol_name;
+ char name_end;
+
+ if (seh_ctx_cur == NULL)
+ {
+ as_fatal (_(".seh_handler requires to be in .seh_proc/.seh_endproc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ as_fatal (_(".seh_handler requires a handler lable name.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
+ input_line_pointer++;
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ seh_ctx->handler_name = xstrdup (symbol_name);
+ if (symbol_name[0] == '@')
+ {
+ if (strcasecmp (symbol_name, "@0") != 0 && strcasecmp (symbol_name, "@1") != 0
+ && strcasecmp (symbol_name, "@null") != 0)
+ as_warn (_("Unknown constant value ,%s' for handler."), symbol_name);
+ }
+ *input_line_pointer = name_end;
+ seh_ctx->handler_data_name = NULL;
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
+ input_line_pointer++;
+ symbol_name = input_line_pointer;
+ if (*input_line_pointer != '\n' && *input_line_pointer != 0)
+ {
+ name_end = get_symbol_end ();
+ seh_ctx->handler_data_name = xstrdup (symbol_name);
+ if (symbol_name[0] == '@')
+ {
+ if (seh_get_target_kind () != seh_kind_x64)
+ as_fatal (_("For this target .seh_handler doesn't support constant user-data."));
+ else if (strcasecmp (symbol_name, "@unwind") != 0 &&
+ strcasecmp (symbol_name, "@except") != 0)
+ as_warn (_("For .seh_handler the constant ,%s' is ignored."), symbol_name);
+ }
+ *input_line_pointer = name_end;
+ }
+ if (seh_ctx_cur->handler_written)
+ as_warn (_(".seh_handler is ignored as .seh_eh was seen before."));
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_seh_scope (int what ATTRIBUTE_UNUSED)
+{
+ char *symbol_name,*beg = NULL,*end = NULL, *handl = NULL, *jmp = NULL;
+ char name_end;
+
+ if (seh_ctx_cur == NULL)
+ {
+ as_fatal (_(".seh_scope requires to be in .seh_proc/.seh_endproc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
+ input_line_pointer++;
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ as_fatal (_(".seh_scope requires four symbol names.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ beg = xstrdup (symbol_name);
+ *input_line_pointer = name_end;
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
+ input_line_pointer++;
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ as_fatal (_(".seh_scope requires three more symbol names.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ end = xstrdup (symbol_name);
+ *input_line_pointer = name_end;
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
+ input_line_pointer++;
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ as_fatal (_(".seh_scope requires two more symbol names.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ handl = xstrdup (symbol_name);
+ *input_line_pointer = name_end;
+ if (*handl == '@')
+ {
+ if (strcasecmp (handl, "@0") != 0 && strcasecmp (handl, "@1") != 0
+ && strcasecmp (handl, "@null") != 0)
+ as_warn (_("Unknown constant for handler ,%s'."), handl);
+ }
+
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
+ input_line_pointer++;
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ as_fatal (_(".seh_scope requires one more symbol names.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+ jmp = xstrdup (symbol_name);
+ *input_line_pointer = name_end;
+ if (*jmp == '@')
+ {
+ if (strcasecmp (jmp, "@0") != 0 && strcasecmp (handl, "@null") != 0)
+ as_warn (_("Unknown constant for jump ,%s'."), jmp);
+ }
+
+ if (seh_get_target_kind () != seh_kind_x64)
+ as_warn (_(".seh_scope is ignored for this target."));
+ else
+ seh_x64_makescope_elem (seh_ctx_cur, beg, end, handl, jmp);
+ if (beg)
+ free (beg);
+ if (end)
+ free (end);
+ if (handl)
+ free (handl);
+ if (jmp)
+ free (jmp);
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_seh_proc (int what ATTRIBUTE_UNUSED)
+{
+ char *symbol_name;
+ char name_end;
+
+ if (seh_ctx_cur != NULL)
+ {
+ as_warn (_(".seh_proc has to be closed by .seh_endprog\n"));
+ obj_coff_seh_endproc (0);
+ }
+
+ if (*input_line_pointer == 0 || *input_line_pointer == '\n')
+ {
+ as_fatal (_(".seh_proc requires function lable name.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
+ input_line_pointer++;
+ symbol_name = input_line_pointer;
+ name_end = get_symbol_end ();
+
+ if (seh_ctx == NULL)
+ seh_ctx_root = seh_ctx = (seh_context *) xmalloc (sizeof (seh_context));
+ else
+ {
+ seh_ctx->next = (seh_context *) xmalloc (sizeof (seh_context));
+ seh_ctx = seh_ctx->next;
+ }
+ seh_ctx_cur = seh_ctx;
+ memset (seh_ctx, 0, sizeof (seh_context));
+
+ seh_ctx->func_name = xstrdup (symbol_name);
+ *input_line_pointer = name_end;
+ while (*input_line_pointer == ' ' || *input_line_pointer == '\t' || *input_line_pointer == ',')
+ input_line_pointer++;
+ seh_ctx->start_symbol = make_seh_text_label (seh_ctx_cur, &(seh_ctx_cur->start_addr));
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_seh_endproc (int what ATTRIBUTE_UNUSED)
+{
+ if (seh_ctx_cur == NULL)
+ {
+ as_warn (_(".seh_endprog without prior .seh_proc (ignored)\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ seh_ctx->end_symbol = make_seh_text_label (seh_ctx, &(seh_ctx->end_addr));
+ seh_ctx->xdata_first = seh_make_xlbl_name (seh_ctx);
+ make_function_entry_pdata (seh_ctx);
+ seh_ctx_cur = NULL;
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_seh_push (int what)
+{
+ int reg = 0;
+ int kind = -1;
+ if (seh_ctx_cur == NULL)
+ {
+ as_warn (_(".seh_push used outside of .seh_proc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ /* what 0:reg, 1:pushframe. */
+ switch (what)
+ {
+ case 0:
+ if (seh_x64_read_reg (".seh_push", 1, ®))
+ kind = UWOP_PUSH_NONVOL;
+ else
+ as_warn (_(".seh_pushreg expects register argument."));
+ break;
+ case 1:
+ kind = UWOP_PUSH_MACHFRAME;
+ break;
+ default:
+ abort ();
+ }
+ if (seh_get_target_kind () != seh_kind_x64)
+ as_warn (_(".seh_save... is ignored for this target.\n"));
+ else if (kind != -1)
+ seh_x64_make_prologue_element (kind, reg, 0);
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_seh_save (int what)
+{
+ int reg;
+ bfd_vma off;
+ int kind;
+ int ok = 1;
+
+ /* what 0:reg, 1:mm, 2:xmm. */
+ switch (what)
+ {
+ case 0:
+ ok &= seh_x64_read_reg (".seh_savereg", 1, ®);
+ kind = UWOP_SAVE_NONVOL;
+ break;
+ case 1:
+ ok &= seh_x64_read_reg (".seh_savemm", 2, ®);
+ kind = UWOP_SAVE_XMM;
+ break;
+ case 2:
+ ok &= seh_x64_read_reg (".seh_savexmm", 3, ®);
+ kind = UWOP_SAVE_XMM128;
+ break;
+ default:
+ abort ();
+ }
+ ok &= seh_read_offset (".seh_save", &off);
+ if (seh_ctx_cur == NULL)
+ {
+ as_warn (_(".seh_save used outside of .seh_proc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (seh_get_target_kind () != seh_kind_x64)
+ as_warn (_(".seh_save... is ignored for this target.\n"));
+ else
+ seh_x64_make_prologue_element (kind, reg, off);
+ demand_empty_rest_of_line ();
+}
+
+static void
+obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED)
+{
+ if (seh_ctx_cur == NULL)
+ {
+ as_warn (_(".seh_endprologue used outside of .seh_proc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (seh_ctx_cur->endprologue_symbol != NULL)
+ as_warn (_(".seh_endprologue used more then once in .seh_proc block.\n"));
+ else
+ seh_ctx_cur->endprologue_symbol = make_seh_text_label (seh_ctx_cur, &seh_ctx_cur->endprologue_addr);
+}
+
+static void
+obj_coff_seh_stack_alloc (int what ATTRIBUTE_UNUSED)
+{
+ bfd_vma size;
+ if (seh_ctx_cur == NULL)
+ {
+ as_warn (_(".seh_stackalloc used outside of .seh_proc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (seh_read_offset (".seh_stackalloc", &size))
+ {
+ if (seh_get_target_kind () != seh_kind_x64)
+ as_warn (_(".seh_stackalloc is ignored for this target.\n"));
+ else
+ seh_x64_make_prologue_element (UWOP_ALLOC_LARGE, 0, size);
+ }
+}
+
+static void
+obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED)
+{
+ int reg;
+ int ok = 1;
+ bfd_vma off;
+
+ ok &= seh_x64_read_reg (".seh_setframe", 0, ®);
+ ok &= seh_read_offset (".seh_setframe", &off);
+ if (seh_ctx_cur == NULL)
+ {
+ as_warn (_(".seh_setframe used outside of .seh_proc block.\n"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ if (ok)
+ {
+ seh_ctx_cur->framereg = reg;
+ seh_ctx_cur->frameoff = off;
+ }
+ if (seh_get_target_kind () != seh_kind_x64)
+ as_warn (_(".seh_setframe is ignored for this target.\n"));
+ demand_empty_rest_of_line ();
+}
+
+/* Misc function helpers. */
+static void
+seh_reloc (bfd *abfd, bfd_size_type address, int which_howto, int symidx)
+{
+ if (relcount >= relsize - 1)
+ {
+ relsize += 10;
+ if (reltab)
+ reltab = xrealloc (reltab, relsize * sizeof (arelent));
+ else
+ reltab = xmalloc (relsize * sizeof (arelent));
+ }
+ reltab[relcount].address = address;
+ reltab[relcount].addend = 0;
+ reltab[relcount].howto = bfd_reloc_type_lookup (abfd, which_howto);
+ reltab[relcount].sym_ptr_ptr = symtab + symidx;
+ relcount++;
+}
+
+static void
+save_relocs (asection *sec)
+{
+ int i;
+
+ sec->relocation = reltab;
+ sec->reloc_count = relcount;
+ sec->orelocation = xmalloc ((relcount + 1) * sizeof (arelent *));
+ for (i = 0; i < relcount; i++)
+ sec->orelocation[i] = sec->relocation + i;
+ sec->orelocation[relcount] = 0;
+ sec->flags |= SEC_RELOC;
+ reltab = 0;
+ relcount = relsize = 0;
+}
+
+static void
+seh_symbol_init (bfd *abfd, unsigned int added)
+{
+ unsigned int oldcount;
+ oldcount = bfd_get_symcount (abfd);
+ symptr = oldcount;
+ symtab = xmalloc ((oldcount + added + 6) * sizeof (asymbol *));
+ if (oldcount > 0)
+ memcpy (symtab, bfd_get_outsymbols (abfd), sizeof (asymbol *) * oldcount);
+}
+
+static int
+seh_symbol (bfd *abfd, const char *n1, const char *n2, const char *n3,
+ asection *sec, int flags, int addr)
+{
+ asymbol *sym;
+ char *name = xmalloc (strlen (n1) + strlen (n2) + strlen (n3) + 1);
+ int ret = symptr;
+ strcpy (name, n1);
+ strcat (name, n2);
+ strcat (name, n3);
+ sym = bfd_make_empty_symbol (abfd);
+ sym->name = name;
+ sym->section = sec;
+ sym->flags = flags;
+ sym->value = addr;
+ symtab[symptr++] = sym;
+ return ret;
+}
+
+static asection *
+quick_section (bfd *abfd, const char *name, int flags, int align)
+{
+ asection *sec;
+ asymbol *sym;
+ sec = seh_make_section2 (name, flags);
+ bfd_set_section_alignment (abfd, sec, align);
+ /* Remember to undo this before trying to link internally! */
+
+ sym = bfd_make_empty_symbol (abfd);
+ symtab[symptr++] = sym;
+ sym->name = sec->name;
+ sym->section = sec;
+ sym->flags = BSF_LOCAL;
+ sym->value = 0;
+
+ return sec;
+}
+
+static seh_kind
+seh_get_target_kind (void)
+{
+ if (!stdoutput)
+ return seh_kind_unknown;
+ switch (bfd_get_arch (stdoutput))
+ {
+ case bfd_arch_arm:
+ case bfd_arch_powerpc:
+ case bfd_arch_sh:
+ return seh_kind_arm;
+ case bfd_arch_i386:
+ switch (bfd_get_mach (stdoutput))
+ {
+ case bfd_mach_x86_64:
+ case bfd_mach_x86_64_intel_syntax:
+ return seh_kind_x64;
+ default:
+ break;
+ }
+ /* FALL THROUGH. */
+ case bfd_arch_mips:
+ return seh_kind_mips;
+ case bfd_arch_ia64:
+ /* Should return seh_kind_x64. But not implemented yet. */
+ return seh_kind_unknown;
+ default:
+ break;
+ }
+ return seh_kind_unknown;
+}
+
+static void
+seh_emit_rva (const char *name)
+{
+ char *p = (char *) xmalloc (strlen (name) + 1);
+ char *s = input_line_pointer;
+
+ strcpy (p, name);
+ input_line_pointer = p;
+ s_rva (4);
+ input_line_pointer = s;
+}
+
+static void
+seh_emit_long (const char *name)
+{
+ char *p = (char *) xmalloc (strlen (name) + 1);
+ char *s = input_line_pointer;
+
+ strcpy (p, name);
+ input_line_pointer = p;
+ cons (4);
+ input_line_pointer = s;
+}
+
+static void
+seh_make_globl (char *sym_name)
+{
+ char *s = input_line_pointer;
+
+ input_line_pointer = sym_name;
+ s_globl (4);
+ input_line_pointer = s;
+}
+
+static segT
+seh_make_section2 (const char *section_name, unsigned flags)
+{
+ char *name;
+ segT sec;
+
+ name = xmalloc (strlen (section_name) + 1);
+ strcpy (name, section_name);
+
+ sec = subseg_new (name, (subsegT) 0);
+ bfd_set_section_flags (stdoutput, sec,
+ ((SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA | flags)
+ & bfd_applicable_section_flags (stdoutput)));
+
+ return sec;
+}
+
+static segT
+seh_make_section (void)
+{
+ static segT seg_pdata = NULL;
+ segT sec = NULL;
+
+ if (!seg_pdata)
+ seg_pdata = seh_make_section2 (".pdata", 0);
+ sec = seg_pdata;
+ return sec;
+}
+
+static char *
+seh_make_xlbl_name (seh_context *c)
+{
+ size_t len = strlen (".seh_xlbl_") + strlen (c->func_name) + 9 + 1;
+ char *ret = (char*) xmalloc (len);
+ if (!ret)
+ as_fatal (_("Out of memory for xdata lable for %s"), c->func_name);
+ else
+ sprintf (ret, ".seh_xlbl_%s_%x", c->func_name, + c->xlbl_count);
+ c->xlbl_count += 1;
+ return ret;
+}
+
+static char *
+make_seh_text_label (seh_context *c, symbolS **addr)
+{
+ char *sym_name;
+ size_t len = strlen (".seh_tlbl_") + strlen (c->func_name) + 9 + 1;
+ sym_name = (char *) xmalloc (len);
+ if (!sym_name)
+ as_fatal (_("Allocating memory for SEH's text symbol for %s failed"), c->func_name);
+ sprintf (sym_name, ".seh_tlbl_%s_%x", c->func_name, c->tlbl_count);
+ c->tlbl_count += 1;
+ if (addr)
+ {
+ seh_make_globl (sym_name);
+ *addr = colon (sym_name);
+ }
+ return sym_name;
+}
+
+/* x64 secific functions. */
+
+static void
+seh_fill_pcsyms (const seh_context *c, char **names, int *idx)
+{
+ size_t i;
+ int count = 1;
+ valueT start_off = resolve_symbol_value (c->start_addr);
+ valueT un_off;
+ seh_prologue_element *e = c->elems;
+ names[0] = c->start_symbol;
+ idx[0] = 0;
+ if (c->elems_count == 0)
+ return;
+ for (i = 0; i < c->elems_count; i++)
+ {
+ un_off = resolve_symbol_value (e[i].pc_addr);
+ if ((un_off - start_off) > 255)
+ {
+ names[count] = e[i].pc_symbol;
+ idx[count] = (int) i;
+ count++;
+ start_off = un_off;
+ }
+ }
+}
+
+static int
+seh_needed_unwind_info (seh_context *c)
+{
+ size_t i;
+ int count = 1;
+ valueT start_off = resolve_symbol_value (c->start_addr);
+ valueT un_off;
+ seh_prologue_element *e = c->elems;
+ if (c->elems_count == 0)
+ return count;
+ for (i = 0; i < c->elems_count; i++)
+ {
+ un_off = resolve_symbol_value (e[i].pc_addr);
+ if ((un_off - start_off) > 255)
+ {
+ count++;
+ start_off = un_off;
+ }
+ }
+ return count;
+}
+
+static size_t
+seh_getelm_data_size (const seh_context *c, int elm_start, int elm_end)
+{
+ size_t ret = PEX64_UWI_SIZEOF_UWCODE_ARRAY (elm_end - elm_start);
+ while (elm_start < elm_end)
+ {
+ switch (c->elems[elm_start].kind)
+ {
+ case UWOP_PUSH_NONVOL:
+ case UWOP_PUSH_MACHFRAME:
+ ret += 2;
+ break;
+ case UWOP_SAVE_NONVOL:
+ case UWOP_SAVE_XMM:
+ case UWOP_SAVE_XMM128:
+ if ((c->elems[elm_start].offset & 7) != 0 ||
+ ((c->elems[elm_start].offset / 8) > 0xffff))
+ ret += 6;
+ else
+ ret += 4;
+ break;
+ case UWOP_ALLOC_LARGE:
+ ret += 4;
+ break;
+ default:
+ break;
+ }
+ elm_start++;
+ }
+ return ret;
+}
+
+static size_t
+seh_getsize_of_unwind_entry (seh_context *c, int elm_start, int elm_end, int bechain)
+{
+ size_t ret = seh_getelm_data_size(c, elm_start, elm_end);
+ c->count_syms += 1;
+ if (bechain)
+ {
+ ret += 4 + 4;
+ c->count_syms += 1;
+ c->count_reloc += 1;
+ }
+ else
+ {
+ ret += 4;
+ if (c->handler_name != NULL)
+ {
+ if (c->handler_data_name != NULL
+ && c->handler_data_name[0] != '@')
+ {
+ ret += 4;
+ c->count_syms += 2;
+ c->count_reloc += 2;
+ }
+ else
+ {
+ ret += 8 + (c->scope_count * 4) * 4;
+ c->count_syms += (c->scope_count * 4) + 1;
+ c->count_reloc += (c->scope_count * 4) + 1;
+ }
+ }
+ }
+ return ret;
+}
+
+static void
+seh_make_unwind_entry (const seh_context *c, char *name, int elm_start, int elm_end, int bechain,
+ unsigned char *data, size_t *poffs, int no)
+{
+ size_t off = *poffs;
+ size_t it;
+ valueT start_off = resolve_symbol_value (c->start_addr);
+ valueT end_prologue;
+ size_t uwcodes = seh_getelm_data_size(c, elm_start, elm_end);
+ unsigned int flag = UNW_FLAG_NHANDLER;
+ int idx;
+
+ if (c->handler_name != NULL)
+ {
+ flag = UNW_FLAG_EHANDLER;
+ if (c->handler_data_name != NULL && c->handler_data_name[0] != '@')
+ flag = UNW_FLAG_FHANDLER;
+ else if (c->handler_data_name != NULL &&
+ strcasecmp (c->handler_data_name, "@unwind") == 0)
+ flag = UNW_FLAG_UHANDLER;
+ }
+ if (!c->endprologue_addr)
+ end_prologue = start_off;
+ else
+ end_prologue = resolve_symbol_value (c->endprologue_addr);
+ seh_symbol (c->abfd, name, "", "", c->section, BSF_GLOBAL, (int) off);
+ data[off++] = (1 | ((bechain ? UNW_FLAG_CHAININFO : flag) << 3));
+ if (elm_start != 0)
+ start_off = (valueT) c->elems[elm_start].offset;
+ end_prologue -= start_off;
+ if (end_prologue > 255)
+ end_prologue = 255;
+ data[off++] = (unsigned char) end_prologue;
+ data[off++] = (unsigned char) (uwcodes / 2);
+ data[off] = (unsigned char) c->framereg;
+ data[off++] |= (unsigned char) ((c->frameoff / 16) << 4);
+ off += uwcodes;
+ if (bechain)
+ {
+ char n[100];
+ sprintf (n,"%x", no);
+ idx = seh_symbol (c->abfd, ".xdata_fct", c->func_name, n, UNDSEC, BSF_GLOBAL, (int) off);
+ seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
+ off += 4;
+ }
+ else if (c->handler_name != NULL)
+ {
+ if (flag == UNW_FLAG_FHANDLER)
+ {
+ if (strcasecmp (c->handler_name, "@1") == 0)
+ bfd_put_32 (c->abfd, (bfd_vma) 1, &data[off]);
+ else if (c->handler_name[0] != '@')
+ {
+ idx = seh_symbol (c->abfd, c->handler_name, "", "", UNDSEC, BSF_GLOBAL, 0);
+ seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
+ }
+ off += 4;
+ idx = seh_symbol (c->abfd, c->handler_data_name, "", "", UNDSEC, BSF_GLOBAL, 0);
+ seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
+ off += 4;
+ }
+ else if (flag == UNW_FLAG_UHANDLER || flag == UNW_FLAG_EHANDLER)
+ {
+ if (strcasecmp (c->handler_name, "@1") == 0)
+ bfd_put_32 (c->abfd, (bfd_vma) 1, &data[off]);
+ else if (c->handler_name[0] != '@')
+ {
+ idx = seh_symbol (c->abfd, c->handler_name, "", "", UNDSEC, BSF_GLOBAL, 0);
+ seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
+ }
+ off += 4;
+ bfd_put_32 (c->abfd, (bfd_vma) c->scope_count, &data[off]);
+ off += 4;
+ for (it = 0; it < c->scope_count; it++)
+ {
+ idx = seh_symbol (c->abfd, c->scopes[it].begin_addr, "", "", UNDSEC, BSF_GLOBAL, 0);
+ seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
+ off += 4;
+ idx = seh_symbol (c->abfd, c->scopes[it].end_addr, "", "", UNDSEC, BSF_GLOBAL, 0);
+ seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
+ off += 4;
+ if (c->scopes[it].handler_addr[0] == '@')
+ {
+ if (strcasecmp (c->scopes[it].handler_addr, "@1") == 0)
+ bfd_put_32 (c->abfd, (bfd_vma) 1, &data[off]);
+ }
+ else
+ {
+ idx = seh_symbol (c->abfd, c->scopes[it].handler_addr, "", "", UNDSEC, BSF_GLOBAL, 0);
+ seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
+ }
+ off += 4;
+ if (c->scopes[it].jump_addr[0] == '@')
+ {
+ if (strcasecmp (c->scopes[it].jump_addr, "@1") == 0)
+ bfd_put_32 (c->abfd, (bfd_vma) 1, &data[off]);
+ }
+ else
+ {
+ idx = seh_symbol (c->abfd, c->scopes[it].jump_addr, "", "", UNDSEC, BSF_GLOBAL, 0);
+ seh_reloc (c->abfd, off, BFD_RELOC_RVA, idx);
+ }
+ off += 4;
+ }
+ }
+ }
+ *poffs = off;
+}
+
+static size_t
+seh_getsize_unwind_data (seh_context *c)
+{
+ int need = seh_needed_unwind_info (c);
+ int i;
+ char **names = (char **) xmalloc (sizeof (char *) * need);
+ char **pc_syms = (char **) xmalloc (sizeof (char *) * need);
+ int *elm_start = (int *) xmalloc (sizeof (int) * (need + 1));
+ size_t xdata_sz = 0;
+
+ seh_fill_pcsyms (c, pc_syms, elm_start);
+ elm_start[need] = c->elems_count;
+
+ xdata_sz += ((12 * (size_t) need));
+ c->count_syms += 5 * need;
+ xdata_sz += (seh_getsize_of_unwind_entry (c, elm_start[0], elm_start[1], 1 != need) + 7) & ~7;
+ for (i = 1; i < need; i++)
+ {
+ xdata_sz += (seh_getsize_of_unwind_entry (c, elm_start[i], elm_start[i + 1], 1 != need) + 7) & ~7;
+ }
+
+ /* Create lable names for .xdata unwind info. */
+ names[0] = c->xdata_first;
+ for (i = 1; i < need; i++)
+ names[i] = seh_make_xlbl_name (c);
+ c->xdata_names = names;
+ c->xdata_pcsyms = pc_syms;
+ c->xdata_elm_start = elm_start;
+ c->xdata_sz = xdata_sz;
+ return xdata_sz;
+}
+
+static void
+seh_create_unwind_data (seh_context *c, unsigned char *data, size_t offs)
+{
+ int need = seh_needed_unwind_info (c);
+ int i;
+ char **names = c->xdata_names;
+ char **pc_syms = c->xdata_pcsyms;
+ int *elm_start = c->xdata_elm_start;
+
+ for (i = 1; i < need; i++)
+ {
+ seh_make_function_entry_xdata (c, pc_syms[i], c->end_symbol, names[i], data, &offs, i);
+ }
+ /* Generate the function entry. Remark, that just
+ first is in .pdata section and already emitted. */
+ seh_make_unwind_entry (c, c->xdata_first, elm_start[0], elm_start[1], 1 != need, data, &offs, 1);
+ for (i = 1; i < need; i++)
+ {
+ seh_make_unwind_entry (c, names[i], elm_start[i], elm_start[i + 1], (i + 1) != need, data, &offs, i + 1);
+ }
+ for (i = 1; i < need; i++)
+ free (names[i]);
+ free (names);
+ free (pc_syms);
+ free (elm_start);
+ c->xdata_names = NULL;
+ c->xdata_pcsyms = NULL;
+ c->xdata_elm_start = NULL;
+}
+
+static void
+seh_make_function_entry_xdata (seh_context *c, char *pc_start, char *pc_end, char *pc_xdata, unsigned char *data, size_t *poffs,int no)
+{
+ bfd_vma addr = (bfd_vma) *poffs;
+ int idx;
+ char s[100];
+ if (!data)
+ return;
+ sprintf (s,"%x",no);
+ seh_symbol (c->abfd, ".xdata_fct",c->func_name, s, c->section, BSF_GLOBAL, (int) poffs[0]);
+ idx = seh_symbol (c->abfd, pc_start,"","", UNDSEC, BSF_GLOBAL,0);
+ seh_reloc (c->abfd, addr, BFD_RELOC_RVA, idx);
+ idx = seh_symbol (c->abfd, pc_end,"","", UNDSEC, BSF_GLOBAL,0);
+ seh_reloc (c->abfd, addr + 4, BFD_RELOC_RVA, idx);
+ idx = seh_symbol (c->abfd, pc_xdata,"","", UNDSEC, BSF_GLOBAL,0);
+ seh_reloc (c->abfd, addr + 8, BFD_RELOC_RVA, idx);
+ poffs[0] += 12;
+}
+
+static seh_scope_elem *
+seh_x64_makescope_elem (seh_context *c, const char *begin, const char *end,
+ const char *handler, const char *jmp)
+{
+ seh_scope_elem *r;
+ if (!end || !begin)
+ return NULL;
+ if (c->scope_count >= c->scope_max)
+ {
+ seh_scope_elem *h = (seh_scope_elem *) xmalloc (sizeof (seh_scope_elem) * (c->scope_max + 8));
+ memset (h, 0, sizeof (seh_scope_elem) * (c->scope_max + 8));
+ if (c->scopes != NULL)
+ memcpy (h, c->scopes, sizeof (seh_scope_elem) * c->scope_max);
+ if (c->scopes != NULL)
+ free (c->scopes);
+ c->scopes = h;
+ c->scope_max += 8;
+ }
+ r = &c->scopes[c->scope_count++];
+ r->begin_addr = xstrdup (begin);
+ r->end_addr = xstrdup (end);
+ r->handler_addr = (!handler ? NULL : xstrdup (handler));
+ r->jump_addr = (!jmp ? NULL : xstrdup (jmp));
+ return r;
+}
Index: src/gas/config/obj-coff-seh.h
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
+++ src/gas/config/obj-coff-seh.h 2009-08-11 10:45:51.090000000 +0200
@@ -0,0 +1,161 @@
+/* Short overview:
+ There are at the moment three different function entry formats preset.
+ The first is the MIPS one. The second version
+ is for ARM, PPC, SH3, and SH4 mainly for Windows CE.
+ The third is the IA64 and x64 version. Note, the IA64 isn't implemented yet,
+ but to find information about it, please see specification about IA64 on
+ http://download.intel.com/design/Itanium/Downloads/245358.pdf file.
+
+ The first version has just entries in the pdata section: BeginAddress,
+ EndAddress, ExceptionHandler, HandlerData, and PrologueEndAddress. Each
+ value is a pointer to the corresponding data and has size of 4 bytes.
+
+ The second variant has the following entries in the pdata section.
+ BeginAddress, PrologueLength (8 bits), EndAddress (22 bits),
+ Use-32-bit-instruction (1 bit), and Exception-Handler-Exists (1 bit).
+ If the FunctionLength is zero, or the Exception-Handler-Exists bit
+ is true, a PDATA_EH block is placed directly before function entry.
+
+ The third version has a function entry block of BeginAddress (RVA),
+ EndAddress (RVA), and UnwindData (RVA). The description of the
+ prologue, excepetion-handler, and additional SEH data is stored
+ within the UNWIND_DATA field in the xdata section.
+
+ The pseudos:
+ .seh_proc <fct_name>
+ .seh_endprologue
+ .seh_handler <handler>[,<handler-data>]]
+ .seh_eh
+ .seh_32/.seh_no32
+ .seh_endproc
+ .seh_setframe <reg>,<offset>
+ .seh_stackalloc
+ .seh_pushreg
+ .seh_savereg
+ .seh_savemm
+ .seh_savexmm
+ .seh_pushframe
+ .seh_scope
+ */
+
+/* architecture specific pdata/xdata handling. */
+#define SEH_CMDS \
+ {"seh_proc", obj_coff_seh_proc, 0}, \
+ {"seh_endproc", obj_coff_seh_endproc, 0}, \
+ {"seh_pushreg", obj_coff_seh_push, 0}, \
+ {"seh_savereg", obj_coff_seh_save, 0}, \
+ {"seh_savemm", obj_coff_seh_save, 1}, \
+ {"seh_savexmm", obj_coff_seh_save, 2}, \
+ {"seh_pushframe", obj_coff_seh_push, 1}, \
+ {"seh_endprologue", obj_coff_seh_endprologue, 0}, \
+ {"seh_setframe", obj_coff_seh_setframe, 0}, \
+ {"seh_stackalloc", obj_coff_seh_stack_alloc, 0}, \
+ {"seh_handler", obj_coff_seh_handler, 0}, \
+ {"seh_eh", obj_coff_seh_eh, 0}, \
+ {"seh_32", obj_coff_seh_32, 1}, \
+ {"seh_no32", obj_coff_seh_32, 0}, \
+ {"seh_scope", obj_coff_seh_scope, 0},
+
+/* Type definitions. */
+
+typedef struct seh_prologue_element
+{
+ symbolS *pc_addr;
+ char *pc_symbol;
+ int kind;
+ int reg;
+ bfd_vma offset;
+} seh_prologue_element;
+
+typedef struct seh_scope_elem {
+ char *begin_addr;
+ char *end_addr;
+ char *handler_addr;
+ char *jump_addr;
+} seh_scope_elem;
+
+typedef struct seh_context
+{
+ struct seh_context *next;
+ /* Was record alread processed. */
+ int done;
+ /* Function name. */
+ char *func_name;
+ /* BeginAddress. */
+ char *start_symbol;
+ symbolS *start_addr;
+ bfd_vma start_offset;
+ /* EndAddress. */
+ char *end_symbol;
+ symbolS *end_addr;
+ bfd_vma end_offset;
+ /* PrologueEnd. */
+ char *endprologue_symbol;
+ symbolS *endprologue_addr;
+ bfd_vma endprologue_offset;
+ /* ExceptionHandler. */
+ char *handler_name;
+ /* ExceptionHandlerData. */
+ char *handler_data_name;
+ int handler_written;
+ /* WinCE specific data. */
+ int use_instruction_32;
+
+ /* the bfd to store data within. */
+ bfd *abfd;
+ /* the current section to generate data within. */
+ asection *section;
+ /* Relocations for section. */
+ unsigned int count_reloc;
+ /* Symbols within section. */
+ unsigned int count_syms;
+ /* Iterator for text lable generation. */
+ unsigned int tlbl_count;
+ /* Iterator for xdata lable generation. */
+ unsigned int xlbl_count;
+ /* The name of the first xdata label. */
+ char *xdata_first;
+ /* FIelds used for x64 generation of chained information. */
+ char **xdata_names;
+ char **xdata_pcsyms;
+ int *xdata_elm_start;
+ /* Size and offset within current generated xdata section. */
+ size_t xdata_sz;
+ size_t xdata_offset;
+ /* x64 framereg and frame offset information. */
+ int framereg;
+ bfd_vma frameoff;
+ /* Information about x64 specific unwind data fields. */
+ size_t elems_count;
+ size_t elems_max;
+ seh_prologue_element *elems;
+ size_t scope_max;
+ size_t scope_count;
+ seh_scope_elem *scopes;
+} seh_context;
+
+typedef enum seh_kind {
+ seh_kind_unknown = 0,
+ seh_kind_mips = 1, /* Used for MIPS and x86 pdata generation. */
+ seh_kind_arm = 2, /* Used for ARM, PPC, SH3, and SH4 pdata (PDATA_EH) generation. */
+ seh_kind_x64 = 3 /* Used for IA64 and x64 pdata/xdata generation. */
+} seh_kind;
+
+/* Forward declarations. */
+static void obj_coff_seh_stack_alloc (int);
+static void obj_coff_seh_setframe (int);
+static void obj_coff_seh_endprologue (int);
+static void obj_coff_seh_save (int);
+static void obj_coff_seh_push (int);
+static void obj_coff_seh_endproc (int);
+static void obj_coff_seh_eh (int);
+static void obj_coff_seh_32 (int);
+static void obj_coff_seh_proc (int);
+static void obj_coff_seh_handler (int);
+static void obj_coff_seh_scope (int);
+static int seh_read_offset (const char *, bfd_vma *);
+static int seh_x64_read_reg (const char *, int, int *);
+static void seh_x64_make_prologue_element (int, int, bfd_vma);
+static void make_function_entry_pdata (seh_context *c);
+
+#define UNDSEC (asection *) &bfd_und_section
Index: src/gas/config/obj-coff.c
===================================================================
--- src.orig/gas/config/obj-coff.c 2009-07-15 21:27:52.843674000 +0200
+++ src/gas/config/obj-coff.c 2009-08-11 11:43:25.043000000 +0200
@@ -53,6 +53,8 @@
static const char weak_altprefix[] = ".weak.";
#endif /* TE_PE */
+#include "obj-coff-seh.c"
+
typedef struct
{
unsigned long chunk_size;
@@ -1768,7 +1770,6 @@
SF_SET_STATICS (secsym);
SA_SET_SCN_SCNLEN (secsym, size);
}
-
/* FIXME: These should be in a "stabs.h" file, or maybe as.h. */
#ifndef STAB_SECTION_NAME
#define STAB_SECTION_NAME ".stab"
@@ -1877,6 +1878,9 @@
/* The tic4x uses sdef instead of def. */
{"sdef", obj_coff_def, 0},
#endif
+#if defined(SEH_CMDS)
+ SEH_CMDS
+#endif
{NULL, NULL, 0}
};
Index: src/gas/write.c
===================================================================
--- src.orig/gas/write.c 2009-07-15 21:27:52.847674000 +0200
+++ src/gas/write.c 2009-08-11 10:45:17.505000000 +0200
@@ -560,6 +560,9 @@
size = 0;
flags = bfd_get_section_flags (abfd, sec);
+ if (size == 0 && bfd_get_section_size (sec) != 0 &&
+ (flags & SEC_HAS_CONTENTS) != 0)
+ return;
if (size > 0 && ! seginfo->bss)
flags |= SEC_HAS_CONTENTS;
@@ -1886,7 +1889,9 @@
#ifdef obj_frob_file
obj_frob_file ();
#endif
-
+#ifdef obj_coff_generate_pdata
+ obj_coff_generate_pdata ();
+#endif
bfd_map_over_sections (stdoutput, write_relocs, (char *) 0);
#ifdef tc_frob_file_after_relocs
@@ -1895,7 +1900,6 @@
#ifdef obj_frob_file_after_relocs
obj_frob_file_after_relocs ();
#endif
-
bfd_map_over_sections (stdoutput, write_contents, (char *) 0);
}
Index: src/gas/Makefile.in
===================================================================
--- src.orig/gas/Makefile.in 2009-07-15 21:28:21.000000000 +0200
+++ src/gas/Makefile.in 2009-08-11 11:27:00.955000000 +0200
@@ -38,6 +38,7 @@
build_triplet = @build@
host_triplet = @host@
target_triplet = @target@
+LIBOBJDIR =
noinst_PROGRAMS = as-new$(EXEEXT)
EXTRA_PROGRAMS = itbl-test$(EXEEXT)
DIST_COMMON = $(srcdir)/../config.guess $(srcdir)/../config.sub NEWS \
@@ -4295,15 +4296,15 @@
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
- echo ' cd $(srcdir) && $(AUTOMAKE) --cygnus '; \
- cd $(srcdir) && $(AUTOMAKE) --cygnus \
+ echo ' cd $(srcdir) && $(AUTOMAKE) --foreign '; \
+ cd $(srcdir) && $(AUTOMAKE) --foreign \
&& exit 0; \
exit 1;; \
esac; \
done; \
- echo ' cd $(top_srcdir) && $(AUTOMAKE) --cygnus Makefile'; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \
cd $(top_srcdir) && \
- $(AUTOMAKE) --cygnus Makefile
+ $(AUTOMAKE) --foreign Makefile
.PRECIOUS: Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
@@ -4389,7 +4390,7 @@
-rm -rf .libs _libs
distclean-libtool:
- -rm -f libtool
+ -rm -f libtool config.lt
uninstall-info-am:
# This directory's subdirectories are mostly independent; you can cd
Index: src/gas/config/obj-coff.h
===================================================================
--- src.orig/gas/config/obj-coff.h 2009-05-20 21:57:27.000000000 +0200
+++ src/gas/config/obj-coff.h 2009-08-11 10:45:03.891000000 +0200
@@ -413,4 +413,11 @@
extern void c_section_header (struct internal_scnhdr *,
char *, long, long, long, long,
long, long, long, long);
+extern void obj_coff_seh_do_final (void);
+
+#ifndef obj_coff_generate_pdata
+#define obj_coff_generate_pdata obj_coff_seh_do_final
+#endif
+
+
#endif /* OBJ_FORMAT_H */