|
Message-ID: <20190813145437.lgmli3uutqvvkx3y@gmail.com> Date: Tue, 13 Aug 2019 14:54:37 +0000 From: Fangrui Song <i@...kray.me> To: Samuel Holland <samuel@...lland.org> Cc: musl@...ts.openwall.com Subject: Re: [PATCH] add support for powerpc/powerpc64 unaligned relocations On 2019-06-30, Samuel Holland wrote: >R_PPC_UADDR32 (R_PPC64_UADDR64) has the same meaning as R_PPC_ADDR32 >(R_PPC64_ADDR64), except that its address need not be aligned. For >powerpc64, BFD ld(1) will automatically convert between ADDR<->UADDR >relocations when the address is/isn't at its native alignment. This >will happen if, for example, there is a pointer in a packed struct. > >gold and lld do not currently generate R_PPC64_UADDR64, but pass >through misaligned R_PPC64_ADDR64 relocations from object files, >possibly relaxing them to misaligned R_PPC64_RELATIVE. In both cases >(relaxed or not) this violates the PSABI, which defines the relevant >field type as "a 64-bit field occupying 8 bytes, the alignment of >which is 8 bytes unless otherwise specified." > >All three linkers violate the PSABI on 32-bit powerpc, where the only >difference is that the field is 32 bits wide, aligned to 4 bytes. > >Currently musl fails to load executables linked by BFD ld containing >R_PPC64_UADDR64, with the error "unsupported relocation type 43". >This change provides compatibility with BFD ld on powerpc64, and any >static linker on either architecture that starts following the PSABI >more closely. >--- > arch/powerpc/reloc.h | 1 + > arch/powerpc64/reloc.h | 1 + > ldso/dynlink.c | 3 +++ > src/internal/dynlink.h | 1 + > 4 files changed, 6 insertions(+) > >diff --git a/arch/powerpc/reloc.h b/arch/powerpc/reloc.h >index 1b4cab36..527b6b7c 100644 >--- a/arch/powerpc/reloc.h >+++ b/arch/powerpc/reloc.h >@@ -9,6 +9,7 @@ > #define TPOFF_K (-0x7000) > > #define REL_SYMBOLIC R_PPC_ADDR32 >+#define REL_USYMBOLIC R_PPC_UADDR32 > #define REL_GOT R_PPC_GLOB_DAT > #define REL_PLT R_PPC_JMP_SLOT > #define REL_RELATIVE R_PPC_RELATIVE >diff --git a/arch/powerpc64/reloc.h b/arch/powerpc64/reloc.h >index faf70acd..5bdaeede 100644 >--- a/arch/powerpc64/reloc.h >+++ b/arch/powerpc64/reloc.h >@@ -11,6 +11,7 @@ > #define TPOFF_K (-0x7000) > > #define REL_SYMBOLIC R_PPC64_ADDR64 >+#define REL_USYMBOLIC R_PPC64_UADDR64 > #define REL_GOT R_PPC64_GLOB_DAT > #define REL_PLT R_PPC64_JMP_SLOT > #define REL_RELATIVE R_PPC64_RELATIVE >diff --git a/ldso/dynlink.c b/ldso/dynlink.c >index db543c19..b5ef4bfc 100644 >--- a/ldso/dynlink.c >+++ b/ldso/dynlink.c >@@ -407,6 +407,9 @@ static void do_relocs(struct dso *dso, size_t *rel, size_t rel_size, size_t stri > case REL_PLT: > *reloc_addr = sym_val + addend; > break; >+ case REL_USYMBOLIC: >+ memcpy(reloc_addr, &(size_t){sym_val + addend}, sizeof(size_t)); >+ break; > case REL_RELATIVE: > *reloc_addr = (size_t)base + addend; > break; >diff --git a/src/internal/dynlink.h b/src/internal/dynlink.h >index 165bbedb..ffd06b04 100644 >--- a/src/internal/dynlink.h >+++ b/src/internal/dynlink.h >@@ -28,6 +28,7 @@ typedef Elf64_Sym Sym; > enum { > REL_NONE = 0, > REL_SYMBOLIC = -100, >+ REL_USYMBOLIC, > REL_GOT, > REL_PLT, > REL_RELATIVE, This has been applied (commit 08869deb7efbda6e979886cb67e3d5843f92c2e8) Writing something for archival purposes... % cat d.c char y; struct{char _; const char *x; char *y;} __attribute__((packed)) _ = {'_', "a", &y}; void _start() {} # smaeul mentioned the real world case is https://github.com/FreeRDP/FreeRDP/blob/master/winpr/libwinpr/timezone/TimeZones.c powerpc gas does not produce UADDR32. It just produces misaligned ADDR32. For an initial relocation ADDR32 in a writable location, ld produces misaligned RELATIVE (nonpreemptable) or ADDR32 (preemptable). % powerpc-linux-gnu-gcc -c -fpic d.c; readelf -Wr d.o | grep R_PPC 00000001 00000501 R_PPC_ADDR32 00000000 .rodata + 0 00000005 00000901 R_PPC_ADDR32 00000001 y + 0 % powerpc-linux-gnu-ld -pie d.o -o d; readelf -Wr d | grep R_PPC 00020001 00000016 R_PPC_RELATIVE 1b8 00020005 00000016 R_PPC_RELATIVE 2001c % powerpc-linux-gnu-ld -shared d.o -o d.so; readelf -Wr d.so | grep R_PPC 00020001 00000016 R_PPC_RELATIVE 1c4 00020005 00000301 R_PPC_ADDR32 0002001c y + 0 I hacked llvm-mc to produce UADDR32. powerpc ld will create dynamic UADDR32. It doesn't optimize aligned UADDR32 to ADDR32. Dynamic UADDR32 is a case that a STT_SECTION STB_LOCAL symbol may be exported into .dynsym Relocation section '.rela.dyn' at offset 0x178 contains 2 entries: Offset Info Type Sym. Value Symbol's Name + Addend 00020001 00000118 R_PPC_UADDR32 00000190 .rodata + 190 00020005 00000118 R_PPC_UADDR32 00000190 .rodata + 2001c nsz mentioned glibc/sysdeps/powerpc/powerpc32/dl-machine.h:elf_machine_rela handles relocations to STB_LOCAL differently. Fortunately, if gas never emits initial UADDR32, ld will never emit outstanding UADDR32 and this piece of code will not run. (Ignoring r_addend seems a really broken behavior) The code was added in 2003 so probably nobody hit the issue. powerpc64 gas does not produce UADDR64. It just produces misaligned ADDR64. But ld is smart enough to convert misaligned ADDR64 to UADDR64, and aligned UADDR64 to ADDR64. % powerpc64le-linux-gnu-gcc -c -fpic d.c; readelf -Wr d.o | grep R_PPC64 0000000000000001 0000000500000026 R_PPC64_ADDR64 0000000000000000 .rodata + 0 0000000000000009 0000000900000026 R_PPC64_ADDR64 0000000000000001 y + 0 % powerpc64le-linux-gnu-ld -pie d.o -o d; readelf -Wr d | grep R_PPC64 0000000000020001 000000010000002b R_PPC64_UADDR64 0000000000000298 .text + 28 0000000000020009 000000020000002b R_PPC64_UADDR64 0000000000020000 .data + 11 % powerpc64le-linux-gnu-ld -shared d.o -o d.so; readelf -Wr d.so | grep R_PPC64 0000000000020001 000000010000002b R_PPC64_UADDR64 0000000000000288 .text + 28 0000000000020009 000000040000002b R_PPC64_UADDR64 0000000000020011 y + 0 I don't know if on some older powerpc hardware misaligned store may trap but kernels will fix the trap: FreeBSD: sys/powerpc/powerpc/trap.c case EXC_ALI: if (fix_unaligned(td, frame) != 0) Linux: arch/powerpc/kernel/traps.c (controlled by prctl PR_SET_UNALIGN) if (!(current->thread.align_ctl & PR_UNALIGN_SIGBUS)) fixed = fix_alignment(regs); Misaligned ADDR violates ABIs. 32-bit psABI says: word32 Specifies a 32-bit bit-field taking up 4 bytes maintaining 4-byte alignment unless otherwise indicated. ELFv2 ABI says: doubleword64 specifies a 64-bit field occupying 8 bytes, the alignment of which is 8 bytes unless otherwise specified. In powerpc64 ld, UADDR64 -> ADDR64 conversion is probably not relevant nowadays. ADDR64 -> UADDR64 conversion makes outstanding relocation conform to the ABI but I am not sure if this is useful now. (on powerpc64le (defaults to -mcpu=power8), `std` is generated as 64-bit store. The ABI ADDR64/UADDR64 distinction makes little sense.)
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.