summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorH. Peter Anvin <hpa@zytor.com>2019-07-29 17:36:56 -0700
committerH. Peter Anvin <hpa@zytor.com>2019-07-29 17:36:56 -0700
commit77726c3ebbd052f77382e3ddf3aa003f42442f6b (patch)
tree69966d71d1ad4fd327436da2ffbcc8f245dc0c43
parent6ad9715994e0a6d3c080e5d8f5e4fb8c48925b02 (diff)
downloadbinutils-77726c3ebbd052f77382e3ddf3aa003f42442f6b.tar.gz
binutils-77726c3ebbd052f77382e3ddf3aa003f42442f6b.tar.xz
binutils-77726c3ebbd052f77382e3ddf3aa003f42442f6b.zip
gas, i386: support generating SEG16 relocations
The syntax is symbol@SEG. In the process, I had to generalize the handling of @... expressions as it was hard-coded that they only apply to 4-byte values in 32-bit mode. In the process, give an explicit error message in case the operand size doesn't match the @ argument, instead of getting the "junk at end of line" generic message. Signed-off-by: H. Peter Anvin <hpa@zytor.com>
-rw-r--r--gas/config/tc-i386-intel.c2
-rw-r--r--gas/config/tc-i386.c193
2 files changed, 120 insertions, 75 deletions
diff --git a/gas/config/tc-i386-intel.c b/gas/config/tc-i386-intel.c
index 8f738b99a2..28498cb7e3 100644
--- a/gas/config/tc-i386-intel.c
+++ b/gas/config/tc-i386-intel.c
@@ -141,7 +141,7 @@ operatorT i386_operator (const char *name, unsigned int operands, char *pc)
int adjust = 0;
char *gotfree_input_line = lex_got (&i.reloc[this_operand],
&adjust,
- &intel_state.reloc_types);
+ &intel_state.reloc_types, -1);
if (!gotfree_input_line)
break;
diff --git a/gas/config/tc-i386.c b/gas/config/tc-i386.c
index 2710dcec72..3025a5e3c8 100644
--- a/gas/config/tc-i386.c
+++ b/gas/config/tc-i386.c
@@ -64,6 +64,12 @@
#endif
#endif
+static inline bfd_boolean
+is_power_of_2(unsigned int val)
+{
+ return (val & (val - 1)) == 0;
+}
+
/* Prefixes will be emitted in the order defined below.
WAIT_PREFIX must be the first prefix since FWAIT is really is an
instruction, and so must come before any prefixes.
@@ -8807,7 +8813,7 @@ x86_address_bytes (void)
#if !(defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) || defined (OBJ_MACH_O)) \
|| defined (LEX_AT)
-# define lex_got(reloc, adjust, types) NULL
+# define lex_got(reloc, adjust, types, size) NULL
#else
/* Parse operands of the form
<symbol>@GOTOFF+<nnn>
@@ -8821,7 +8827,8 @@ x86_address_bytes (void)
static char *
lex_got (enum bfd_reloc_code_real *rel,
int *adjust,
- i386_operand_type *types)
+ i386_operand_type *types,
+ int size)
{
/* Some of the relocations depend on the size of what field is to
be relocated. But in our callers i386_immediate and i386_displacement
@@ -8832,64 +8839,71 @@ lex_got (enum bfd_reloc_code_real *rel,
const char *str;
int len;
const enum bfd_reloc_code_real rel[2];
+ unsigned int sizemask;
const i386_operand_type types64;
} gotrel[] = {
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)
{ STRING_COMMA_LEN ("SIZE"), { BFD_RELOC_SIZE32,
BFD_RELOC_SIZE32 },
- OPERAND_TYPE_IMM32_64 },
+ 4|8, OPERAND_TYPE_IMM32_64 },
+ { STRING_COMMA_LEN ("SEG"), { BFD_RELOC_386_SEG16,
+ 0 /* not supported */ },
+ 2, OPERAND_TYPE_IMM16 },
+# define FIRST_NEED_GOT_SYMBOL 2
+#else
+# define FIRST_NEED_GOT_SYMBOL 0
#endif
{ STRING_COMMA_LEN ("PLTOFF"), { _dummy_first_bfd_reloc_code_real,
BFD_RELOC_X86_64_PLTOFF64 },
- OPERAND_TYPE_IMM64 },
+ 4|8, OPERAND_TYPE_IMM64 },
{ STRING_COMMA_LEN ("PLT"), { BFD_RELOC_386_PLT32,
BFD_RELOC_X86_64_PLT32 },
- OPERAND_TYPE_IMM32_32S_DISP32 },
+ 4|8, OPERAND_TYPE_IMM32_32S_DISP32 },
{ STRING_COMMA_LEN ("GOTPLT"), { _dummy_first_bfd_reloc_code_real,
BFD_RELOC_X86_64_GOTPLT64 },
- OPERAND_TYPE_IMM64_DISP64 },
+ 4|8, OPERAND_TYPE_IMM64_DISP64 },
{ STRING_COMMA_LEN ("GOTOFF"), { BFD_RELOC_386_GOTOFF,
BFD_RELOC_X86_64_GOTOFF64 },
- OPERAND_TYPE_IMM64_DISP64 },
+ 4|8, OPERAND_TYPE_IMM64_DISP64 },
{ STRING_COMMA_LEN ("GOTPCREL"), { _dummy_first_bfd_reloc_code_real,
BFD_RELOC_X86_64_GOTPCREL },
- OPERAND_TYPE_IMM32_32S_DISP32 },
+ 4|8, OPERAND_TYPE_IMM32_32S_DISP32 },
{ STRING_COMMA_LEN ("TLSGD"), { BFD_RELOC_386_TLS_GD,
BFD_RELOC_X86_64_TLSGD },
- OPERAND_TYPE_IMM32_32S_DISP32 },
+ 4|8, OPERAND_TYPE_IMM32_32S_DISP32 },
{ STRING_COMMA_LEN ("TLSLDM"), { BFD_RELOC_386_TLS_LDM,
_dummy_first_bfd_reloc_code_real },
- OPERAND_TYPE_NONE },
+ 4|8, OPERAND_TYPE_NONE },
{ STRING_COMMA_LEN ("TLSLD"), { _dummy_first_bfd_reloc_code_real,
BFD_RELOC_X86_64_TLSLD },
- OPERAND_TYPE_IMM32_32S_DISP32 },
+ 4|8, OPERAND_TYPE_IMM32_32S_DISP32 },
{ STRING_COMMA_LEN ("GOTTPOFF"), { BFD_RELOC_386_TLS_IE_32,
BFD_RELOC_X86_64_GOTTPOFF },
- OPERAND_TYPE_IMM32_32S_DISP32 },
+ 4|8, OPERAND_TYPE_IMM32_32S_DISP32 },
{ STRING_COMMA_LEN ("TPOFF"), { BFD_RELOC_386_TLS_LE_32,
BFD_RELOC_X86_64_TPOFF32 },
- OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
+ 4|8, OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
{ STRING_COMMA_LEN ("NTPOFF"), { BFD_RELOC_386_TLS_LE,
_dummy_first_bfd_reloc_code_real },
- OPERAND_TYPE_NONE },
+ 4|8, OPERAND_TYPE_NONE },
{ STRING_COMMA_LEN ("DTPOFF"), { BFD_RELOC_386_TLS_LDO_32,
BFD_RELOC_X86_64_DTPOFF32 },
- OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
+ 4|8, OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
{ STRING_COMMA_LEN ("GOTNTPOFF"),{ BFD_RELOC_386_TLS_GOTIE,
_dummy_first_bfd_reloc_code_real },
- OPERAND_TYPE_NONE },
+ 4|8, OPERAND_TYPE_NONE },
{ STRING_COMMA_LEN ("INDNTPOFF"),{ BFD_RELOC_386_TLS_IE,
_dummy_first_bfd_reloc_code_real },
- OPERAND_TYPE_NONE },
+ 4|8, OPERAND_TYPE_NONE },
{ STRING_COMMA_LEN ("GOT"), { BFD_RELOC_386_GOT32,
BFD_RELOC_X86_64_GOT32 },
- OPERAND_TYPE_IMM32_32S_64_DISP32 },
+ 4|8, OPERAND_TYPE_IMM32_32S_64_DISP32 },
{ STRING_COMMA_LEN ("TLSDESC"), { BFD_RELOC_386_TLS_GOTDESC,
BFD_RELOC_X86_64_GOTPC32_TLSDESC },
- OPERAND_TYPE_IMM32_32S_DISP32 },
+ 4|8, OPERAND_TYPE_IMM32_32S_DISP32 },
{ STRING_COMMA_LEN ("TLSCALL"), { BFD_RELOC_386_TLS_DESC_CALL,
BFD_RELOC_X86_64_TLSDESC_CALL },
- OPERAND_TYPE_IMM32_32S_DISP32 },
+ 4|8, OPERAND_TYPE_IMM32_32S_DISP32 },
};
char *cp;
unsigned int j;
@@ -8899,6 +8913,11 @@ lex_got (enum bfd_reloc_code_real *rel,
return NULL;
#endif
+ if (size > 0 && !is_power_of_2(size))
+ size = 0;
+ else
+ size &= ~(object_64bit ? 0 : 8);
+
for (cp = input_line_pointer; *cp != '@'; cp++)
if (is_end_of_line[(unsigned char) *cp] || *cp == ',')
return NULL;
@@ -8913,20 +8932,32 @@ lex_got (enum bfd_reloc_code_real *rel,
int first, second;
char *tmpbuf, *past_reloc;
+ if ((gotrel[j].sizemask & size) == 0) {
+ as_bad (_("invalid operand size for @%s reloc with %d-bit output format"),
+ gotrel[j].str, 1 << (5 + object_64bit));
+ return NULL;
+ }
+
*rel = gotrel[j].rel[object_64bit];
if (types)
{
if (flag_code != CODE_64BIT)
{
- types->bitfield.imm32 = 1;
- types->bitfield.disp32 = 1;
+ if (gotrel[j].sizemask & 2) {
+ types->bitfield.imm16 = 1;
+ types->bitfield.disp16 = 1;
+ }
+ if (gotrel[j].sizemask & 4) {
+ types->bitfield.imm32 = 1;
+ types->bitfield.disp32 = 1;
+ }
}
else
*types = gotrel[j].types64;
}
- if (j != 0 && GOT_symbol == NULL)
+ if (j >= FIRST_NEED_GOT_SYMBOL && GOT_symbol == NULL)
GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME);
/* The length of the first part of our input line. */
@@ -8988,25 +9019,32 @@ lex_got (enum bfd_reloc_code_real *rel,
static char *
lex_got (enum bfd_reloc_code_real *rel ATTRIBUTE_UNUSED,
int *adjust ATTRIBUTE_UNUSED,
- i386_operand_type *types)
+ i386_operand_type *types,
+ int size)
{
static const struct
{
const char *str;
int len;
const enum bfd_reloc_code_real rel[2];
+ unsigned int sizemask;
const i386_operand_type types64;
}
gotrel[] =
{
{ STRING_COMMA_LEN ("SECREL32"), { BFD_RELOC_32_SECREL,
BFD_RELOC_32_SECREL },
- OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
+ 4|8, OPERAND_TYPE_IMM32_32S_64_DISP32_64 },
};
char *cp;
unsigned j;
+ if (size > 0 && !is_power_of_2(size))
+ size = 0;
+ else
+ size &= ~(object_64bit ? 0 : 8);
+
for (cp = input_line_pointer; *cp != '@'; cp++)
if (is_end_of_line[(unsigned char) *cp] || *cp == ',')
return NULL;
@@ -9022,6 +9060,12 @@ lex_got (enum bfd_reloc_code_real *rel ATTRIBUTE_UNUSED,
int first, second;
char *tmpbuf, *past_reloc;
+ if ((gotrel[j].sizemask & size) == 0) {
+ as_bad (_("invalid operand size for @%s reloc with %d-bit output format"),
+ gotrel[j].str, 1 << (5 + object_64bit));
+ return NULL;
+ }
+
*rel = gotrel[j].rel[object_64bit];
if (adjust)
*adjust = len;
@@ -9030,8 +9074,14 @@ lex_got (enum bfd_reloc_code_real *rel ATTRIBUTE_UNUSED,
{
if (flag_code != CODE_64BIT)
{
- types->bitfield.imm32 = 1;
- types->bitfield.disp32 = 1;
+ if (gotrel[j].sizemask & 2) {
+ types->bitfield.imm16 = 1;
+ types->bitfield.disp16 = 1;
+ }
+ if (gotrel[j].sizemask & 4) {
+ types->bitfield.imm32 = 1;
+ types->bitfield.disp32 = 1;
+ }
}
else
*types = gotrel[j].types64;
@@ -9081,53 +9131,48 @@ x86_cons (expressionS *exp, int size)
intel_syntax = -intel_syntax;
exp->X_md = 0;
- if (size == 4 || (object_64bit && size == 8))
- {
- /* Handle @GOTOFF and the like in an expression. */
- char *save;
- char *gotfree_input_line;
- int adjust = 0;
-
- save = input_line_pointer;
- gotfree_input_line = lex_got (&got_reloc, &adjust, NULL);
- if (gotfree_input_line)
- input_line_pointer = gotfree_input_line;
-
- expression (exp);
-
- if (gotfree_input_line)
- {
- /* expression () has merrily parsed up to the end of line,
- or a comma - in the wrong buffer. Transfer how far
- input_line_pointer has moved to the right buffer. */
- input_line_pointer = (save
- + (input_line_pointer - gotfree_input_line)
- + adjust);
- free (gotfree_input_line);
- if (exp->X_op == O_constant
- || exp->X_op == O_absent
- || exp->X_op == O_illegal
- || exp->X_op == O_register
- || exp->X_op == O_big)
- {
- char c = *input_line_pointer;
- *input_line_pointer = 0;
- as_bad (_("missing or invalid expression `%s'"), save);
- *input_line_pointer = c;
- }
- else if ((got_reloc == BFD_RELOC_386_PLT32
- || got_reloc == BFD_RELOC_X86_64_PLT32)
- && exp->X_op != O_symbol)
- {
- char c = *input_line_pointer;
- *input_line_pointer = 0;
- as_bad (_("invalid PLT expression `%s'"), save);
- *input_line_pointer = c;
- }
+ /* Handle @GOTOFF and the like in an expression. */
+ char *save;
+ char *gotfree_input_line;
+ int adjust = 0;
+
+ save = input_line_pointer;
+ gotfree_input_line = lex_got (&got_reloc, &adjust, NULL, size);
+ if (gotfree_input_line)
+ input_line_pointer = gotfree_input_line;
+
+ expression (exp);
+
+ if (gotfree_input_line)
+ {
+ /* expression () has merrily parsed up to the end of line,
+ or a comma - in the wrong buffer. Transfer how far
+ input_line_pointer has moved to the right buffer. */
+ input_line_pointer = (save
+ + (input_line_pointer - gotfree_input_line)
+ + adjust);
+ free (gotfree_input_line);
+ if (exp->X_op == O_constant
+ || exp->X_op == O_absent
+ || exp->X_op == O_illegal
+ || exp->X_op == O_register
+ || exp->X_op == O_big)
+ {
+ char c = *input_line_pointer;
+ *input_line_pointer = 0;
+ as_bad (_("missing or invalid expression `%s'"), save);
+ *input_line_pointer = c;
+ }
+ else if ((got_reloc == BFD_RELOC_386_PLT32
+ || got_reloc == BFD_RELOC_X86_64_PLT32)
+ && exp->X_op != O_symbol)
+ {
+ char c = *input_line_pointer;
+ *input_line_pointer = 0;
+ as_bad (_("invalid PLT expression `%s'"), save);
+ *input_line_pointer = c;
}
}
- else
- expression (exp);
intel_syntax = -intel_syntax;
@@ -9345,7 +9390,7 @@ i386_immediate (char *imm_start)
save_input_line_pointer = input_line_pointer;
input_line_pointer = imm_start;
- gotfree_input_line = lex_got (&i.reloc[this_operand], NULL, &types);
+ gotfree_input_line = lex_got (&i.reloc[this_operand], NULL, &types, -1);
if (gotfree_input_line)
input_line_pointer = gotfree_input_line;
@@ -9602,7 +9647,7 @@ i386_displacement (char *disp_start, char *disp_end)
*displacement_string_end = '0';
}
#endif
- gotfree_input_line = lex_got (&i.reloc[this_operand], NULL, &types);
+ gotfree_input_line = lex_got (&i.reloc[this_operand], NULL, &types, -1);
if (gotfree_input_line)
input_line_pointer = gotfree_input_line;