From 8b8c7c9f49992750f66f81b4601d593a3858d98c Mon Sep 17 00:00:00 2001 From: "Jose E. Marchesi" Date: Fri, 17 Jun 2016 02:13:30 -0700 Subject: [PATCH] gas: sparc: fix collision of registers and pseudo-ops. The current sparc assembler breaks when the name of an ancillary-state register, privileged register or hyperprivileged register has a %-pseudo-operation name as a prefix. For example, %hmcdper and %hm(), or %hintp and %hi(). This patch fixes it by introducing a new table `perc_table' (for %-table) that contains an entry for every %name supported by the assembler, other than the general registers. This table is used to detect name collisions when the assembler tries to detect a %-pseudo-op. This patch also fixes a related bug, making sure that v9a_asr_table and hpriv_reg_table are sorted in reverse lexicographic order, as otherwise the search code may fail. gas/ChangeLog: 2016-06-17 Jose E. Marchesi * config/tc-sparc.c (priv_reg_table): Use NULL instead of the empty string to mark the end of the array. (hpriv_reg_table): Likewise. (v9a_asr_table): Likewise. (cmp_reg_entry): Handle entries with NULL names. (F_POP_V9): Define. (F_POP_PCREL): Likewise. (F_POP_TLS_CALL): Likewise. (F_POP_POSTFIX): Likewise. (struct pop_entry): New type. (pop_table): New variable. (enum pop_entry_type): New type. (struct perc_entry): Likewise. (NUM_PERC_ENTRIES): Define. (perc_table): New variable. (cmp_perc_entry): New function. (md_begin): Sort hpriv_reg_table and v9a_asr_table, and initialize perc_table. (sparc_ip): Handle entries with NULL names in priv_reg_table, hpriv_reg_table and v9a_asr_table. Use perc_table to handle %-pseudo-ops. --- gas/ChangeLog | 24 +++ gas/config/tc-sparc.c | 381 ++++++++++++++++++++++++++---------------- 2 files changed, 264 insertions(+), 141 deletions(-) diff --git a/gas/ChangeLog b/gas/ChangeLog index 64b62af1d8..e183c112f3 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,27 @@ +2016-06-17 Jose E. Marchesi + + * config/tc-sparc.c (priv_reg_table): Use NULL instead of the + empty string to mark the end of the array. + (hpriv_reg_table): Likewise. + (v9a_asr_table): Likewise. + (cmp_reg_entry): Handle entries with NULL names. + (F_POP_V9): Define. + (F_POP_PCREL): Likewise. + (F_POP_TLS_CALL): Likewise. + (F_POP_POSTFIX): Likewise. + (struct pop_entry): New type. + (pop_table): New variable. + (enum pop_entry_type): New type. + (struct perc_entry): Likewise. + (NUM_PERC_ENTRIES): Define. + (perc_table): New variable. + (cmp_perc_entry): New function. + (md_begin): Sort hpriv_reg_table and v9a_asr_table, and initialize + perc_table. + (sparc_ip): Handle entries with NULL names in priv_reg_table, + hpriv_reg_table and v9a_asr_table. Use perc_table to handle + %-pseudo-ops. + 2016-06-15 Nick Clifton * config/tc-ft32.c (md_assemble): Call dwarf2_emit_insn with the diff --git a/gas/config/tc-sparc.c b/gas/config/tc-sparc.c index 1114afa139..c8076bf191 100644 --- a/gas/config/tc-sparc.c +++ b/gas/config/tc-sparc.c @@ -807,7 +807,7 @@ struct priv_reg_entry priv_reg_table[] = {"gl", 16}, {"pmcdper", 23}, {"ver", 31}, - {"", -1}, /* End marker. */ + {NULL, -1}, /* End marker. */ }; struct priv_reg_entry hpriv_reg_table[] = @@ -820,11 +820,10 @@ struct priv_reg_entry hpriv_reg_table[] = {"hstick_offset", 28}, {"hstick_enable", 29}, {"hstick_cmpr", 31}, - {"", -1}, /* End marker. */ + {NULL, -1}, /* End marker. */ }; -/* v9a specific asrs. This table is ordered by initial - letter, in reverse. */ +/* v9a or later specific ancillary state registers. */ struct priv_reg_entry v9a_asr_table[] = { @@ -845,7 +844,7 @@ struct priv_reg_entry v9a_asr_table[] = {"dcr", 18}, {"cfr", 26}, {"clear_softint", 21}, - {"", -1}, /* End marker. */ + {NULL, -1}, /* End marker. */ }; static int @@ -854,7 +853,131 @@ cmp_reg_entry (const void *parg, const void *qarg) const struct priv_reg_entry *p = (const struct priv_reg_entry *) parg; const struct priv_reg_entry *q = (const struct priv_reg_entry *) qarg; - return strcmp (q->name, p->name); + if (p->name == q->name) + return 0; + else if (p->name == NULL) + return 1; + else if (q->name == NULL) + return -1; + else + return strcmp (q->name, p->name); +} + +/* sparc %-pseudo-operations. */ + + +#define F_POP_V9 0x1 /* The pseudo-op is for v9 only. */ +#define F_POP_PCREL 0x2 /* The pseudo-op can be used in pc-relative + contexts. */ +#define F_POP_TLS_CALL 0x4 /* The pseudo-op marks a tls call. */ +#define F_POP_POSTFIX 0x8 /* The pseudo-op should appear after the + last operand of an + instruction. (Generally they can appear + anywhere an immediate operand is + expected. */ +struct pop_entry +{ + /* The name as it appears in assembler. */ + const char *name; + /* The reloc this pseudo-op translates to. */ + int reloc; + /* Flags. See F_POP_* above. */ + int flags; +}; + +struct pop_entry pop_table[] = +{ + { "hix", BFD_RELOC_SPARC_HIX22, F_POP_V9 }, + { "lox", BFD_RELOC_SPARC_LOX10, F_POP_V9 }, + { "hi", BFD_RELOC_HI22, F_POP_PCREL }, + { "lo", BFD_RELOC_LO10, F_POP_PCREL }, + { "pc22", BFD_RELOC_SPARC_PC22, F_POP_PCREL }, + { "pc10", BFD_RELOC_SPARC_PC10, F_POP_PCREL }, + { "hh", BFD_RELOC_SPARC_HH22, F_POP_V9|F_POP_PCREL }, + { "hm", BFD_RELOC_SPARC_HM10, F_POP_V9|F_POP_PCREL }, + { "lm", BFD_RELOC_SPARC_LM22, F_POP_V9|F_POP_PCREL }, + { "h34", BFD_RELOC_SPARC_H34, F_POP_V9 }, + { "l34", BFD_RELOC_SPARC_L44, F_POP_V9 }, + { "h44", BFD_RELOC_SPARC_H44, F_POP_V9 }, + { "m44", BFD_RELOC_SPARC_M44, F_POP_V9 }, + { "l44", BFD_RELOC_SPARC_L44, F_POP_V9 }, + { "uhi", BFD_RELOC_SPARC_HH22, F_POP_V9 }, + { "ulo", BFD_RELOC_SPARC_HM10, F_POP_V9 }, + { "tgd_hi22", BFD_RELOC_SPARC_TLS_GD_HI22, 0 }, + { "tgd_lo10", BFD_RELOC_SPARC_TLS_GD_LO10, 0 }, + { "tldm_hi22", BFD_RELOC_SPARC_TLS_LDM_HI22, 0 }, + { "tldm_lo10", BFD_RELOC_SPARC_TLS_LDM_LO10, 0 }, + { "tldo_hix22", BFD_RELOC_SPARC_TLS_LDO_HIX22, 0 }, + { "tldo_lox10", BFD_RELOC_SPARC_TLS_LDO_LOX10, 0 }, + { "tie_hi22", BFD_RELOC_SPARC_TLS_IE_HI22, 0 }, + { "tie_lo10", BFD_RELOC_SPARC_TLS_IE_LO10, 0 }, + { "tle_hix22", BFD_RELOC_SPARC_TLS_LE_HIX22, 0 }, + { "tle_lox10", BFD_RELOC_SPARC_TLS_LE_LOX10, 0 }, + { "gdop_hix22", BFD_RELOC_SPARC_GOTDATA_OP_HIX22, 0 }, + { "gdop_lox10", BFD_RELOC_SPARC_GOTDATA_OP_LOX10, 0 }, + { "tgd_add", BFD_RELOC_SPARC_TLS_GD_ADD, F_POP_POSTFIX }, + { "tgd_call", BFD_RELOC_SPARC_TLS_GD_CALL, F_POP_POSTFIX|F_POP_TLS_CALL }, + { "tldm_add", BFD_RELOC_SPARC_TLS_LDM_ADD, F_POP_POSTFIX }, + { "tldm_call", BFD_RELOC_SPARC_TLS_LDM_CALL, F_POP_POSTFIX|F_POP_TLS_CALL }, + { "tldo_add", BFD_RELOC_SPARC_TLS_LDO_ADD, F_POP_POSTFIX }, + { "tie_ldx", BFD_RELOC_SPARC_TLS_IE_LDX, F_POP_POSTFIX }, + { "tie_ld", BFD_RELOC_SPARC_TLS_IE_LD, F_POP_POSTFIX }, + { "tie_add", BFD_RELOC_SPARC_TLS_IE_ADD, F_POP_POSTFIX }, + { "gdop", BFD_RELOC_SPARC_GOTDATA_OP, F_POP_POSTFIX }, + { NULL, 0, 0 }, +}; + +/* Table of %-names that can appear in a sparc assembly program. This + table is initialized in md_begin and contains entries for each + privileged/hyperprivileged/alternate register and %-pseudo-op. */ + +enum perc_entry_type +{ + perc_entry_none = 0, + perc_entry_reg, + perc_entry_post_pop, + perc_entry_imm_pop +}; + +struct perc_entry +{ + /* Entry type. */ + enum perc_entry_type type; + /* Name of the %-entity. */ + const char *name; + /* strlen (name). */ + int len; + /* Value. Either a pop or a reg depending on type.*/ + union + { + struct pop_entry *pop; + struct priv_reg_entry *reg; + }; +}; + +#define NUM_PERC_ENTRIES \ + (((sizeof (priv_reg_table) / sizeof (priv_reg_table[0])) - 1) \ + + ((sizeof (hpriv_reg_table) / sizeof (hpriv_reg_table[0])) - 1) \ + + ((sizeof (v9a_asr_table) / sizeof (v9a_asr_table[0])) - 1) \ + + ((sizeof (pop_table) / sizeof (pop_table[0])) - 1) \ + + 1) + +struct perc_entry perc_table[NUM_PERC_ENTRIES]; + +static int +cmp_perc_entry (const void *parg, const void *qarg) +{ + const struct perc_entry *p = (const struct perc_entry *) parg; + const struct perc_entry *q = (const struct perc_entry *) qarg; + + if (p->name == q->name) + return 0; + else if (p->name == NULL) + return 1; + else if (q->name == NULL) + return -1; + else + return strcmp (q->name, p->name); } /* This function is called once, at assembler startup time. It should @@ -932,7 +1055,11 @@ md_begin (void) qsort (priv_reg_table, sizeof (priv_reg_table) / sizeof (priv_reg_table[0]), sizeof (priv_reg_table[0]), cmp_reg_entry); - + qsort (hpriv_reg_table, sizeof (hpriv_reg_table) / sizeof (hpriv_reg_table[0]), + sizeof (hpriv_reg_table[0]), cmp_reg_entry); + qsort (v9a_asr_table, sizeof (v9a_asr_table) / sizeof (v9a_asr_table[0]), + sizeof (v9a_asr_table[0]), cmp_reg_entry); + /* If -bump, record the architecture level at which we start issuing warnings. The behaviour is different depending upon whether an architecture was explicitly specified. If it wasn't, we issue warnings @@ -964,6 +1091,50 @@ md_begin (void) current_max_architecture)) break; } + + /* Prepare the tables of %-pseudo-ops. */ + { + struct priv_reg_entry *reg_tables[] + = {priv_reg_table, hpriv_reg_table, v9a_asr_table, NULL}; + struct priv_reg_entry **reg_table; + int entry = 0; + + /* Add registers. */ + for (reg_table = reg_tables; reg_table[0]; reg_table++) + { + struct priv_reg_entry *reg; + for (reg = *reg_table; reg->name; reg++) + { + struct perc_entry *p = &perc_table[entry++]; + p->type = perc_entry_reg; + p->name = reg->name; + p->len = strlen (reg->name); + p->reg = reg; + } + } + + /* Add %-pseudo-ops. */ + { + struct pop_entry *pop; + + for (pop = pop_table; pop->name; pop++) + { + struct perc_entry *p = &perc_table[entry++]; + p->type = (pop->flags & F_POP_POSTFIX + ? perc_entry_post_pop : perc_entry_imm_pop); + p->name = pop->name; + p->len = strlen (pop->name); + p->pop = pop; + } + } + + /* Last entry is the centinel. */ + perc_table[entry].type = perc_entry_none; + + qsort (perc_table, sizeof (perc_table) / sizeof (perc_table[0]), + sizeof (perc_table[0]), cmp_perc_entry); + + } } /* Called after all assembly has been done. */ @@ -1709,20 +1880,19 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn) /* Parse a sparc64 privileged register. */ if (*s == '%') { - struct priv_reg_entry *p = priv_reg_table; + struct priv_reg_entry *p; unsigned int len = 9999999; /* Init to make gcc happy. */ s += 1; - while (p->name[0] > s[0]) - p++; - while (p->name[0] == s[0]) - { - len = strlen (p->name); - if (strncmp (p->name, s, len) == 0) - break; - p++; - } - if (p->name[0] != s[0]) + for (p = priv_reg_table; p->name; p++) + if (p->name[0] == s[0]) + { + len = strlen (p->name); + if (strncmp (p->name, s, len) == 0) + break; + } + + if (!p->name) { error_message = _(": unrecognizable privileged register"); goto error; @@ -1745,20 +1915,19 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn) /* Parse a sparc64 hyperprivileged register. */ if (*s == '%') { - struct priv_reg_entry *p = hpriv_reg_table; + struct priv_reg_entry *p; unsigned int len = 9999999; /* Init to make gcc happy. */ s += 1; - while (p->name[0] > s[0]) - p++; - while (p->name[0] == s[0]) - { - len = strlen (p->name); - if (strncmp (p->name, s, len) == 0) - break; - p++; - } - if (p->name[0] != s[0]) + for (p = hpriv_reg_table; p->name; p++) + if (p->name[0] == s[0]) + { + len = strlen (p->name); + if (strncmp (p->name, s, len) == 0) + break; + } + + if (!p->name) { error_message = _(": unrecognizable hyperprivileged register"); goto error; @@ -1781,20 +1950,19 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn) /* Parse a v9a/v9b ancillary state register. */ if (*s == '%') { - struct priv_reg_entry *p = v9a_asr_table; + struct priv_reg_entry *p; unsigned int len = 9999999; /* Init to make gcc happy. */ s += 1; - while (p->name[0] > s[0]) - p++; - while (p->name[0] == s[0]) - { - len = strlen (p->name); - if (strncmp (p->name, s, len) == 0) - break; - p++; - } - if (p->name[0] != s[0]) + for (p = v9a_asr_table; p->name; p++) + if (p->name[0] == s[0]) + { + len = strlen (p->name); + if (strncmp (p->name, s, len) == 0) + break; + } + + if (!p->name) { error_message = _(": unrecognizable v9a or v9b ancillary state register"); goto error; @@ -2062,67 +2230,45 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn) case '\0': /* End of args. */ if (s[0] == ',' && s[1] == '%') { - static const struct ops - { - /* The name as it appears in assembler. */ - const char *name; - /* strlen (name), precomputed for speed */ - int len; - /* The reloc this pseudo-op translates to. */ - int reloc; - /* 1 if tls call. */ - int tls_call; - } - ops[] = - { - { "tgd_add", 7, BFD_RELOC_SPARC_TLS_GD_ADD, 0 }, - { "tgd_call", 8, BFD_RELOC_SPARC_TLS_GD_CALL, 1 }, - { "tldm_add", 8, BFD_RELOC_SPARC_TLS_LDM_ADD, 0 }, - { "tldm_call", 9, BFD_RELOC_SPARC_TLS_LDM_CALL, 1 }, - { "tldo_add", 8, BFD_RELOC_SPARC_TLS_LDO_ADD, 0 }, - { "tie_ldx", 7, BFD_RELOC_SPARC_TLS_IE_LDX, 0 }, - { "tie_ld", 6, BFD_RELOC_SPARC_TLS_IE_LD, 0 }, - { "tie_add", 7, BFD_RELOC_SPARC_TLS_IE_ADD, 0 }, - { "gdop", 4, BFD_RELOC_SPARC_GOTDATA_OP, 0 }, - { NULL, 0, 0, 0 } - }; - const struct ops *o; char *s1; int npar = 0; + const struct perc_entry *p; - for (o = ops; o->name; o++) - if (strncmp (s + 2, o->name, o->len) == 0) - break; - if (o->name == NULL) - break; + for (p = perc_table; p->type != perc_entry_none; p++) + if ((p->type == perc_entry_post_pop || p->type == perc_entry_reg) + && strncmp (s + 2, p->name, p->len) == 0) + break; + if (p->type == perc_entry_none || p->type == perc_entry_reg) + break; - if (s[o->len + 2] != '(') + if (s[p->len + 2] != '(') { - as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name); + as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name); return special_case; } - if (! o->tls_call && the_insn.reloc != BFD_RELOC_NONE) + if (! (p->pop->flags & F_POP_TLS_CALL) + && the_insn.reloc != BFD_RELOC_NONE) { as_bad (_("Illegal operands: %%%s cannot be used together with other relocs in the insn ()"), - o->name); + p->name); return special_case; } - if (o->tls_call + if ((p->pop->flags & F_POP_TLS_CALL) && (the_insn.reloc != BFD_RELOC_32_PCREL_S2 || the_insn.exp.X_add_number != 0 || the_insn.exp.X_add_symbol != symbol_find_or_make ("__tls_get_addr"))) { as_bad (_("Illegal operands: %%%s can be only used with call __tls_get_addr"), - o->name); + p->name); return special_case; } - the_insn.reloc = o->reloc; + the_insn.reloc = p->pop->reloc; memset (&the_insn.exp, 0, sizeof (the_insn.exp)); - s += o->len + 3; + s += p->len + 3; for (s1 = s; *s1 && *s1 != ',' && *s1 != ']'; s1++) if (*s1 == '(') @@ -2136,7 +2282,7 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn) if (*s1 != ')') { - as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name); + as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name); return special_case; } @@ -2525,72 +2671,25 @@ sparc_ip (char *str, const struct sparc_opcode **pinsn) /* Check for %hi, etc. */ if (*s == '%') { - static const struct ops { - /* The name as it appears in assembler. */ - const char *name; - /* strlen (name), precomputed for speed */ - int len; - /* The reloc this pseudo-op translates to. */ - int reloc; - /* Non-zero if for v9 only. */ - int v9_p; - /* Non-zero if can be used in pc-relative contexts. */ - int pcrel_p;/*FIXME:wip*/ - } ops[] = { - /* hix/lox must appear before hi/lo so %hix won't be - mistaken for %hi. */ - { "hix", 3, BFD_RELOC_SPARC_HIX22, 1, 0 }, - { "lox", 3, BFD_RELOC_SPARC_LOX10, 1, 0 }, - { "hi", 2, BFD_RELOC_HI22, 0, 1 }, - { "lo", 2, BFD_RELOC_LO10, 0, 1 }, - { "pc22", 4, BFD_RELOC_SPARC_PC22, 0, 1 }, - { "pc10", 4, BFD_RELOC_SPARC_PC10, 0, 1 }, - { "hh", 2, BFD_RELOC_SPARC_HH22, 1, 1 }, - { "hm", 2, BFD_RELOC_SPARC_HM10, 1, 1 }, - { "lm", 2, BFD_RELOC_SPARC_LM22, 1, 1 }, - { "h34", 3, BFD_RELOC_SPARC_H34, 1, 0 }, - { "l34", 3, BFD_RELOC_SPARC_L44, 1, 0 }, - { "h44", 3, BFD_RELOC_SPARC_H44, 1, 0 }, - { "m44", 3, BFD_RELOC_SPARC_M44, 1, 0 }, - { "l44", 3, BFD_RELOC_SPARC_L44, 1, 0 }, - { "uhi", 3, BFD_RELOC_SPARC_HH22, 1, 0 }, - { "ulo", 3, BFD_RELOC_SPARC_HM10, 1, 0 }, - { "tgd_hi22", 8, BFD_RELOC_SPARC_TLS_GD_HI22, 0, 0 }, - { "tgd_lo10", 8, BFD_RELOC_SPARC_TLS_GD_LO10, 0, 0 }, - { "tldm_hi22", 9, BFD_RELOC_SPARC_TLS_LDM_HI22, 0, 0 }, - { "tldm_lo10", 9, BFD_RELOC_SPARC_TLS_LDM_LO10, 0, 0 }, - { "tldo_hix22", 10, BFD_RELOC_SPARC_TLS_LDO_HIX22, 0, - 0 }, - { "tldo_lox10", 10, BFD_RELOC_SPARC_TLS_LDO_LOX10, 0, - 0 }, - { "tie_hi22", 8, BFD_RELOC_SPARC_TLS_IE_HI22, 0, 0 }, - { "tie_lo10", 8, BFD_RELOC_SPARC_TLS_IE_LO10, 0, 0 }, - { "tle_hix22", 9, BFD_RELOC_SPARC_TLS_LE_HIX22, 0, 0 }, - { "tle_lox10", 9, BFD_RELOC_SPARC_TLS_LE_LOX10, 0, 0 }, - { "gdop_hix22", 10, BFD_RELOC_SPARC_GOTDATA_OP_HIX22, - 0, 0 }, - { "gdop_lox10", 10, BFD_RELOC_SPARC_GOTDATA_OP_LOX10, - 0, 0 }, - { NULL, 0, 0, 0, 0 } - }; - const struct ops *o; - - for (o = ops; o->name; o++) - if (strncmp (s + 1, o->name, o->len) == 0) - break; - if (o->name == NULL) - break; - - if (s[o->len + 1] != '(') + const struct perc_entry *p; + + for (p = perc_table; p->type != perc_entry_none; p++) + if ((p->type == perc_entry_imm_pop || p->type == perc_entry_reg) + && strncmp (s + 1, p->name, p->len) == 0) + break; + if (p->type == perc_entry_none || p->type == perc_entry_reg) + break; + + if (s[p->len + 1] != '(') { - as_bad (_("Illegal operands: %%%s requires arguments in ()"), o->name); + as_bad (_("Illegal operands: %%%s requires arguments in ()"), p->name); return special_case; } - op_arg = o->name; - the_insn.reloc = o->reloc; - s += o->len + 2; - v9_arg_p = o->v9_p; + op_arg = p->name; + the_insn.reloc = p->pop->reloc; + s += p->len + 2; + v9_arg_p = p->pop->flags & F_POP_V9; } /* Note that if the get_expression() fails, we will still -- 2.34.1