diff --git a/base/ChangeLog.md b/base/ChangeLog.md index 5edafd9d2..feb3ffb05 100644 --- a/base/ChangeLog.md +++ b/base/ChangeLog.md @@ -9,6 +9,8 @@ non–position-independent executables. These were previously omitted due to an oversight in the implementation. +- Add support for PPC32 and PPC64 relocations in `Data.Macaw.Memory.ElfLoader`. + ### API Changes - Architecture-specific block terminators can now contain macaw values diff --git a/base/src/Data/Macaw/Memory/ElfLoader.hs b/base/src/Data/Macaw/Memory/ElfLoader.hs index 3afcfff13..a5f29fe6c 100644 --- a/base/src/Data/Macaw/Memory/ElfLoader.hs +++ b/base/src/Data/Macaw/Memory/ElfLoader.hs @@ -796,6 +796,164 @@ relaTargetARM64 end msegIndex symtab rel addend relFlag = tp -> do throwError $ RelocationUnsupportedType (show tp) +-- | Attempt to resolve a PPC32-specific symbol. +relaTargetPPC32 :: Endianness + -- ^ Endianness of relocations + -> Maybe SegmentIndex + -- ^ Index of segment for dynamic relocations + -> SymbolTable 32 -- ^ Symbol table + -> Elf.RelEntry Elf.PPC32_RelocationType -- ^ Relocation entry + -> MemWord 32 + -- ^ Addend of symbol + -> RelFlag + -> SymbolResolver (Relocation 32) +relaTargetPPC32 end msegIndex symtab rel addend _relFlag = + case Elf.relType rel of + Elf.R_PPC_ADDR32 -> do + sym <- resolveRelocationSym symtab (Elf.relSym rel) + pure $! Relocation { relocationSym = sym + , relocationOffset = addend + , relocationIsRel = False + , relocationSize = 4 + , relocationIsSigned = False + , relocationEndianness = end + , relocationJumpSlot = False + } + Elf.R_PPC_GLOB_DAT -> do + sym <- resolveRelocationSym symtab (Elf.relSym rel) + pure $! Relocation { relocationSym = sym + , relocationOffset = addend + , relocationIsRel = False + , relocationSize = 4 + , relocationIsSigned = False + , relocationEndianness = end + , relocationJumpSlot = False + } + Elf.R_PPC_RELATIVE -> do + -- This relocation has the value B + A where + -- - A is the addend for the relocation, and + -- - B resolves to the difference between the + -- address at which the segment defining the symbol was + -- loaded and the address at which it was linked. + -- + -- Since the address at which it was linked is a constant, we + -- create a non-relative address but subtract the link address + -- from the offset. + + -- Get the address at which it was linked so we can subtract from offset. + let linktimeAddr = Elf.relAddr rel + + -- Resolve the symbol using the index in the relocation. + sym <- + if Elf.relSym rel == 0 then do + case msegIndex of + Nothing -> do + throwError $ RelocationZeroSymbol + Just idx -> + pure $! SegmentBaseAddr idx + else do + resolveRelocationSym symtab (Elf.relSym rel) + pure $! Relocation { relocationSym = sym + , relocationOffset = addend - fromIntegral linktimeAddr + , relocationIsRel = False + , relocationSize = 4 + , relocationIsSigned = False + , relocationEndianness = end + , relocationJumpSlot = False + } + Elf.R_PPC_JMP_SLOT -> do + -- This is a PLT relocation + sym <- resolveRelocationSym symtab (Elf.relSym rel) + pure $! Relocation { relocationSym = sym + , relocationOffset = addend + , relocationIsRel = False + , relocationSize = 4 + , relocationIsSigned = False + , relocationEndianness = end + , relocationJumpSlot = True + } + tp -> + throwError $ RelocationUnsupportedType (show tp) + +-- | Attempt to resolve a PPC64-specific symbol. +relaTargetPPC64 :: Endianness + -- ^ Endianness of relocations + -> Maybe SegmentIndex + -- ^ Index of segment for dynamic relocations + -> SymbolTable 64 -- ^ Symbol table + -> Elf.RelEntry Elf.PPC64_RelocationType -- ^ Relocation entry + -> MemWord 64 + -- ^ Addend of symbol + -> RelFlag + -> SymbolResolver (Relocation 64) +relaTargetPPC64 end msegIndex symtab rel addend _relFlag = + case Elf.relType rel of + Elf.R_PPC64_ADDR64 -> do + sym <- resolveRelocationSym symtab (Elf.relSym rel) + pure $! Relocation { relocationSym = sym + , relocationOffset = addend + , relocationIsRel = False + , relocationSize = 8 + , relocationIsSigned = False + , relocationEndianness = end + , relocationJumpSlot = False + } + Elf.R_PPC64_GLOB_DAT -> do + sym <- resolveRelocationSym symtab (Elf.relSym rel) + pure $! Relocation { relocationSym = sym + , relocationOffset = addend + , relocationIsRel = False + , relocationSize = 8 + , relocationIsSigned = False + , relocationEndianness = end + , relocationJumpSlot = False + } + Elf.R_PPC64_RELATIVE -> do + -- This relocation has the value B + A where + -- - A is the addend for the relocation, and + -- - B resolves to the difference between the + -- address at which the segment defining the symbol was + -- loaded and the address at which it was linked. + -- + -- Since the address at which it was linked is a constant, we + -- create a non-relative address but subtract the link address + -- from the offset. + + -- Get the address at which it was linked so we can subtract from offset. + let linktimeAddr = Elf.relAddr rel + + -- Resolve the symbol using the index in the relocation. + sym <- + if Elf.relSym rel == 0 then do + case msegIndex of + Nothing -> do + throwError $ RelocationZeroSymbol + Just idx -> + pure $! SegmentBaseAddr idx + else do + resolveRelocationSym symtab (Elf.relSym rel) + pure $! Relocation { relocationSym = sym + , relocationOffset = addend - fromIntegral linktimeAddr + , relocationIsRel = False + , relocationSize = 8 + , relocationIsSigned = False + , relocationEndianness = end + , relocationJumpSlot = False + } + Elf.R_PPC64_JMP_SLOT -> do + -- This is a PLT relocation + sym <- resolveRelocationSym symtab (Elf.relSym rel) + pure $! Relocation { relocationSym = sym + , relocationOffset = addend + , relocationIsRel = False + , relocationSize = 8 + , relocationIsSigned = False + , relocationEndianness = end + , relocationJumpSlot = True + } + tp -> + throwError $ RelocationUnsupportedType (show tp) + toEndianness :: Elf.ElfData -> Endianness toEndianness Elf.ELFDATA2LSB = LittleEndian toEndianness Elf.ELFDATA2MSB = BigEndian @@ -809,13 +967,17 @@ getRelocationResolver hdr = case (Elf.headerClass hdr, Elf.headerMachine hdr) of (Elf.ELFCLASS64, Elf.EM_X86_64) -> pure $ SomeRelocationResolver relaTargetX86_64 - (Elf.ELFCLASS32, Elf.EM_ARM) -> do - let end = toEndianness (Elf.headerData hdr) + (Elf.ELFCLASS32, Elf.EM_ARM) -> pure $ SomeRelocationResolver $ relaTargetARM32 end - (Elf.ELFCLASS64, Elf.EM_AARCH64) -> do - let end = toEndianness (Elf.headerData hdr) + (Elf.ELFCLASS64, Elf.EM_AARCH64) -> pure $ SomeRelocationResolver $ relaTargetARM64 end + (Elf.ELFCLASS32, Elf.EM_PPC) -> + pure $ SomeRelocationResolver $ relaTargetPPC32 end + (Elf.ELFCLASS64, Elf.EM_PPC64) -> + pure $ SomeRelocationResolver $ relaTargetPPC64 end (_,mach) -> throwError $ UnsupportedArchitecture (show mach) + where + end = toEndianness (Elf.headerData hdr) resolveRela :: ( MemWidth w , Elf.RelocationWidth tp ~ w diff --git a/deps/elf-edit b/deps/elf-edit index 3ba7d7148..5531161f6 160000 --- a/deps/elf-edit +++ b/deps/elf-edit @@ -1 +1 @@ -Subproject commit 3ba7d7148adc6029b0046229c4fecbb9ee048f9b +Subproject commit 5531161f64f92e13ae04cfe0de0042754ae050cd diff --git a/macaw-ppc/tests/ppc32/Makefile b/macaw-ppc/tests/ppc32/Makefile index fabefde1a..5a4651509 100644 --- a/macaw-ppc/tests/ppc32/Makefile +++ b/macaw-ppc/tests/ppc32/Makefile @@ -7,6 +7,10 @@ all: $(patsubst %c,%exe,$(wildcard *.c)) %.s: %.c $(CC) -fno-stack-protector -S -c $< -o $@ +# This test relies on the binary having dynamic relocations. +test-relocs.exe: test-relocs.s + $(CC) -fno-stack-protector -nostartfiles $< -o $@ + .PRECIOUS: %.s clean: diff --git a/macaw-ppc/tests/ppc32/test-relocs.c b/macaw-ppc/tests/ppc32/test-relocs.c new file mode 100644 index 000000000..aba653d40 --- /dev/null +++ b/macaw-ppc/tests/ppc32/test-relocs.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("Hello, %s!\n", "World"); + return 0; +} diff --git a/macaw-ppc/tests/ppc32/test-relocs.exe b/macaw-ppc/tests/ppc32/test-relocs.exe new file mode 100755 index 000000000..2ed42f008 Binary files /dev/null and b/macaw-ppc/tests/ppc32/test-relocs.exe differ diff --git a/macaw-ppc/tests/ppc32/test-relocs.s b/macaw-ppc/tests/ppc32/test-relocs.s new file mode 100644 index 000000000..1e8759dca --- /dev/null +++ b/macaw-ppc/tests/ppc32/test-relocs.s @@ -0,0 +1,48 @@ + .file "test-relocs.c" + .machine ppc + .section ".text" + .section .rodata + .align 2 +.LC0: + .string "World" + .align 2 +.LC1: + .string "Hello, %s!\n" + .section ".text" + .align 2 + .globl main + .type main, @function +main: +.LFB0: + .cfi_startproc + stwu 1,-16(1) + .cfi_def_cfa_offset 16 + mflr 0 + stw 0,20(1) + stw 31,12(1) + .cfi_offset 65, 4 + .cfi_offset 31, -4 + mr 31,1 + .cfi_def_cfa_register 31 + lis 9,.LC0@ha + la 4,.LC0@l(9) + lis 9,.LC1@ha + la 3,.LC1@l(9) + crxor 6,6,6 + bl printf + li 9,0 + mr 3,9 + addi 11,31,16 + lwz 0,4(11) + mtlr 0 + lwz 31,-4(11) + .cfi_def_cfa 11, 0 + mr 1,11 + .cfi_restore 31 + .cfi_def_cfa_register 1 + blr + .cfi_endproc +.LFE0: + .size main,.-main + .ident "GCC: (Ubuntu 8.4.0-3ubuntu1) 8.4.0" + .section .note.GNU-stack,"",@progbits diff --git a/macaw-ppc/tests/ppc32/test-relocs.s.expected b/macaw-ppc/tests/ppc32/test-relocs.s.expected new file mode 100644 index 000000000..46dafa453 --- /dev/null +++ b/macaw-ppc/tests/ppc32/test-relocs.s.expected @@ -0,0 +1,6 @@ +R { funcs = [(0x10000210, [ (0x10000210, 44) + , (0x1000023c, 32) + ]) + ] + , ignoreBlocks = [0x10000260] + } diff --git a/macaw-ppc/tests/ppc64/Dockerfile b/macaw-ppc/tests/ppc64/Dockerfile new file mode 100644 index 000000000..cb102f018 --- /dev/null +++ b/macaw-ppc/tests/ppc64/Dockerfile @@ -0,0 +1,10 @@ +FROM ubuntu:20.04 +RUN apt update && apt install -y locales \ + && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 +ENV LANG en_US.utf8 +RUN apt install -y gcc-powerpc64-linux-gnu binutils-multiarch build-essential +RUN addgroup --gid 1000 theuser +RUN adduser --disabled-password --gecos "" --force-badname --gid 1000 --uid 1000 theuser +USER theuser +WORKDIR /build +ENTRYPOINT ["make"] diff --git a/macaw-ppc/tests/ppc64/Makefile b/macaw-ppc/tests/ppc64/Makefile index 93e456588..509bf69ad 100644 --- a/macaw-ppc/tests/ppc64/Makefile +++ b/macaw-ppc/tests/ppc64/Makefile @@ -7,6 +7,10 @@ all: $(patsubst %c,%exe,$(wildcard *.c)) %.s: %.c $(CC) -fno-stack-protector -S -c $< -o $@ +# This test relies on the binary having dynamic relocations. +test-relocs.exe: test-relocs.s + $(CC) -fno-stack-protector -nostartfiles $< -o $@ + .PRECIOUS: %.s clean: diff --git a/macaw-ppc/tests/ppc64/README.md b/macaw-ppc/tests/ppc64/README.md new file mode 100644 index 000000000..7434d8639 --- /dev/null +++ b/macaw-ppc/tests/ppc64/README.md @@ -0,0 +1,6 @@ +To build the binaries in this directory with the Docker container, run: + +``` +docker build . -t ppc64-cross +docker run -v $(pwd):/build --rm ppc64-cross +``` diff --git a/macaw-ppc/tests/ppc64/test-relocs.c b/macaw-ppc/tests/ppc64/test-relocs.c new file mode 100644 index 000000000..aba653d40 --- /dev/null +++ b/macaw-ppc/tests/ppc64/test-relocs.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + printf("Hello, %s!\n", "World"); + return 0; +} diff --git a/macaw-ppc/tests/ppc64/test-relocs.exe b/macaw-ppc/tests/ppc64/test-relocs.exe new file mode 100755 index 000000000..10238a21e Binary files /dev/null and b/macaw-ppc/tests/ppc64/test-relocs.exe differ diff --git a/macaw-ppc/tests/ppc64/test-relocs.s b/macaw-ppc/tests/ppc64/test-relocs.s new file mode 100644 index 000000000..409792b7f --- /dev/null +++ b/macaw-ppc/tests/ppc64/test-relocs.s @@ -0,0 +1,50 @@ + .file "test-relocs.c" + .section ".text" + .section .rodata + .align 3 +.LC0: + .string "World" + .align 3 +.LC1: + .string "Hello, %s!\n" + .section ".text" + .align 2 + .globl main + .section ".opd","aw" + .align 3 +main: + .quad .L.main,.TOC.@tocbase,0 + .previous + .type main, @function +.L.main: +.LFB0: + .cfi_startproc + mflr 0 + std 0,16(1) + std 31,-8(1) + stdu 1,-128(1) + .cfi_def_cfa_offset 128 + .cfi_offset 65, 16 + .cfi_offset 31, -8 + mr 31,1 + .cfi_def_cfa_register 31 + addis 4,2,.LC0@toc@ha + addi 4,4,.LC0@toc@l + addis 3,2,.LC1@toc@ha + addi 3,3,.LC1@toc@l + bl printf + nop + li 9,0 + mr 3,9 + addi 1,31,128 + .cfi_def_cfa 1, 0 + ld 0,16(1) + mtlr 0 + ld 31,-8(1) + blr + .long 0 + .byte 0,0,0,1,128,1,0,1 + .cfi_endproc +.LFE0: + .size main,.-.L.main + .ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0" diff --git a/macaw-ppc/tests/ppc64/test-relocs.s.expected b/macaw-ppc/tests/ppc64/test-relocs.s.expected new file mode 100644 index 000000000..a1be68331 --- /dev/null +++ b/macaw-ppc/tests/ppc64/test-relocs.s.expected @@ -0,0 +1,6 @@ +R { funcs = [(0x10000300, [ (0x10000300, 40) + , (0x10000328, 32) + ]) + ] + , ignoreBlocks = [0x100002e0] + }