From 4e40379d2b325c8b4c75535159272edf554f033d Mon Sep 17 00:00:00 2001 From: "H. Peter Anvin" Date: Mon, 24 Dec 2018 12:20:05 -0800 Subject: elf16: WIP: support for 16-bit segmented relocations in ELF32 Add very preliminary support for 16-bit segmented relocations. This does not handle segmented programming in any kind of programmer-friendly way, but it is the beginning to something testable. (Prototype) binutils: URL: https://github.com/hjl-tools/binutils-gdb Branch: users/hjl/16bit Signed-off-by: H. Peter Anvin --- output/outelf.c | 148 ++++++++++++++++++++++++++++-------------------------- test/sections.asm | 48 ++++++++++++++++++ 2 files changed, 126 insertions(+), 70 deletions(-) create mode 100644 test/sections.asm diff --git a/output/outelf.c b/output/outelf.c index 366e52de..c4a693fe 100644 --- a/output/outelf.c +++ b/output/outelf.c @@ -795,6 +795,8 @@ static void elf32_out(int32_t segto, const void *data, int reltype, bytes; int i; static struct symlininfo sinfo; + const char *gnu16 = NULL; + int badsize = 0; s = NULL; for (i = 0; i < nsects; i++) @@ -843,37 +845,19 @@ static void elf32_out(int32_t segto, const void *data, case OUT_ADDRESS: { - bool gnu16 = false; int asize = abs((int)size); addr = *(int64_t *)data; if (segment != NO_SEG) { - if (segment % 2) { - nasm_nonfatal("ELF format does not support" - " segment base references"); + if (segment & 1) { + if (asize < 2) { + nasm_nonfatal("invalid segment base reference"); + } else { + elf_add_reloc(s, segment & ~1, 0, R_386_SEG16); + gnu16 = "segment"; + } } else { - if (wrt == NO_SEG) { - /* - * The if() is a hack to deal with compilers which - * don't handle switch() statements with 64-bit - * expressions. - */ - switch (asize) { - case 1: - gnu16 = true; - elf_add_reloc(s, segment, 0, R_386_8); - break; - case 2: - gnu16 = true; - elf_add_reloc(s, segment, 0, R_386_16); - break; - case 4: - elf_add_reloc(s, segment, 0, R_386_32); - break; - default: /* Error issued further down */ - break; - } - } else if (wrt == elf_gotpc_sect + 1) { + if (wrt == elf_gotpc_sect + 1) { /* * The user will supply GOT relative to $$. ELF * will let us have GOT relative to $. So we @@ -892,12 +876,12 @@ static void elf32_out(int32_t segto, const void *data, } else if (wrt == elf_sym_sect + 1) { switch (asize) { case 1: - gnu16 = true; + gnu16 = "8-bit"; addr = elf_add_gsym_reloc(s, segment, addr, 0, R_386_8, false); break; case 2: - gnu16 = true; + gnu16 = "16-bit"; addr = elf_add_gsym_reloc(s, segment, addr, 0, R_386_16, false); break; @@ -912,24 +896,44 @@ static void elf32_out(int32_t segto, const void *data, nasm_nonfatal("ELF format cannot produce non-PC-" "relative PLT references"); } else { - nasm_nonfatal("ELF format does not support this" - " use of WRT"); - wrt = NO_SEG; /* we can at least _try_ to continue */ + if ((wrt != NO_SEG) && (wrt & SEG_ABS)) { + /* Versus a specific absolute segment base */ + addr -= (wrt & (SEG_ABS-1)) << 4; + wrt = NO_SEG; + } + + switch (asize) { + case 1: + gnu16 = "8-bit"; + elf_add_reloc(s, segment, 0, R_386_8); + if (wrt != NO_SEG) { + nasm_nonfatal("segment-relative 8-bit pointer " + "not supported"); + } + break; + case 2: + gnu16 = "16-bit"; + elf_add_reloc(s, segment, 0, R_386_16); + if (wrt != NO_SEG) + elf_add_reloc(s, wrt & ~1, 0, R_386_SUB16); + break; + case 4: + elf_add_reloc(s, segment, 0, R_386_32); + if (wrt != NO_SEG) { + gnu16 = "segment"; + elf_add_reloc(s, wrt & ~1, 0, R_386_SUB32); + } + break; + default: /* Error issued further down */ + break; + } } } } - if (gnu16) { - /*! - *!gnu-elf-extensions [off] using 8- or 16-bit relocation in ELF32, a GNU extension - *! warns if 8-bit or 16-bit relocations are used in the \c{elf32} output format. - *! The GNU extensions allow this. - */ - nasm_warn(WARN_GNU_ELF_EXTENSIONS, "8- or 16-bit relocations " - "in ELF32 is a GNU extension"); - } else if (asize != 4 && segment != NO_SEG) { - nasm_nonfatal("Unsupported non-32-bit ELF relocation"); - } + if (!gnu16 && (asize != 4 && segment != NO_SEG)) + badsize = asize; + elf_sect_writeaddr(s, addr, asize); break; } @@ -937,40 +941,30 @@ static void elf32_out(int32_t segto, const void *data, case OUT_REL1ADR: reltype = R_386_PC8; bytes = 1; - goto rel12adr; + gnu16 = "8-bit"; + goto reladr; case OUT_REL2ADR: reltype = R_386_PC16; bytes = 2; - goto rel12adr; + gnu16 = "16-bit"; + goto reladr; + case OUT_REL4ADR: + reltype = R_386_PC32; + bytes = 4; + goto reladr; -rel12adr: +reladr: addr = *(int64_t *)data - size; nasm_assert(segment != segto); - if (segment != NO_SEG && segment % 2) { + if (segment != NO_SEG && (segment & 1)) { nasm_nonfatal("ELF format does not support" - " segment base references"); + " relative segment base references"); + gnu16 = NULL; /* No need to print another message */ } else { if (wrt == NO_SEG) { - nasm_warn(WARN_GNU_ELF_EXTENSIONS, "8- or 16-bit relocations " - "in ELF is a GNU extension"); elf_add_reloc(s, segment, 0, reltype); - } else { - nasm_nonfatal("Unsupported non-32-bit ELF relocation"); - } - } - elf_sect_writeaddr(s, addr, bytes); - break; - - case OUT_REL4ADR: - addr = *(int64_t *)data - size; - if (segment == segto) - nasm_panic("intra-segment OUT_REL4ADR"); - if (segment != NO_SEG && segment % 2) { - nasm_nonfatal("ELF format does not support" - " segment base references"); - } else { - if (wrt == NO_SEG) { - elf_add_reloc(s, segment, 0, R_386_PC32); + } else if (bytes != 4) { + badsize = bytes; } else if (wrt == elf_plt_sect + 1) { elf_add_reloc(s, segment, 0, R_386_PLT32); } else if (wrt == elf_gotpc_sect + 1 || @@ -981,22 +975,36 @@ rel12adr: } else { nasm_nonfatal("ELF format does not support this" " use of WRT"); - wrt = NO_SEG; /* we can at least _try_ to continue */ } } - elf_sect_writeaddr(s, addr, 4); + elf_sect_writeaddr(s, addr, bytes); break; case OUT_REL8ADR: - nasm_nonfatal("32-bit ELF format does not support 64-bit relocations"); - addr = 0; + badsize = 8; + addr = *(int64_t *)data - size; elf_sect_writeaddr(s, addr, 8); break; default: panic(); } + + if (badsize) { + nasm_nonfatal("unsupported %d-bit ELF32 relocation", badsize << 3); + } else if (gnu16) { + /*! + *!gnu-elf-extensions [off] using GNU extensions in ELF32 + *! warns if 8-bit, 16-bit or segment base relocations are + *! used in the \c{elf32} output format. GNU extensions + *! allow this; this warning checks for strict classic ELF32 + *! compliance. + */ + nasm_warn(WARN_GNU_ELF_EXTENSIONS, + "%s relocations in ELF32 is a GNU extension", gnu16); + } } + static void elf64_out(int32_t segto, const void *data, enum out_type type, uint64_t size, int32_t segment, int32_t wrt) diff --git a/test/sections.asm b/test/sections.asm new file mode 100644 index 00000000..505a2ff5 --- /dev/null +++ b/test/sections.asm @@ -0,0 +1,48 @@ + bits 16 + + section s_start exec +s_start equ seg $$ + global _start +_start: + nop + nop + nop + nop + ret + + section s_foo exec +s_foo equ seg $$ + hlt + hlt + hlt +wibble: + hlt + hlt + hlt + hlt + + global g_bar, g_anear, g_afar + extern e_meep, e_note, e_note~b +g_bar: add eax,edx + add eax,[g_bar] + add eax,[g_bar wrt s_start] + add eax,[g_bar wrt s_foo] + mov ax,seg e_note~b + mov es,ax + add eax,[es:e_note] + add eax,[es:e_note wrt seg e_note~b] + + jmp s_foo:g_bar + jmp s_start:_start + jmp e_meep + jmp far e_meep + + sub eax,[e_note wrt 0] + +g_anear equ 3333h +g_afar equ 4444h:5555h +g_meh equ g_bar + + dw e_meep, seg e_meep + dw e_note, seg e_note + dw g_afar, seg g_afar -- cgit v1.2.1