diff --git a/configure.ac b/configure.ac
index 216f4725..a23e41e7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -136,12 +136,6 @@ AX_PYTHON_MODULE([psutil], [fatal])
AC_SUBST([AM_CFLAGS], ["-Wall -Wextra -Werror"])
AC_SUBST([AM_CCASFLAGS], ["-Wa,--fatal-warnings"])
-# Use the glibc versions installed on path
-AC_ARG_WITH([glibc],
-AS_HELP_STRING([--with-glibc=GLIBC_PATH],[Use the glibc installed on GLIBC_PATH, where the .so file is present]),
-[AC_SUBST([AM_LDFLAGS], ["-Wl,--dynamic-linker=$with_glibc/ld-linux-x86-64.so.2 -Wl,--rpath=$with_glibc/"])],
-[])
-
# Checking the call stack of all threads enables libpulp to only apply a live
# patch when no threads sit within the target library.
AC_ARG_ENABLE(stack-check,
@@ -223,14 +217,57 @@ AC_SUBST([PAGE_SIZE], [$($GETCONF PAGE_SIZE)]))
# before the entry point, and the remaining nops after it. At running time,
# whenever a live patch is applied, libpulp replaces the remaining nops with
# instructions that redirect execution to the universe handling routines.
-AC_SUBST([ULP_NOPS_LEN], [16])
-AC_SUBST([PRE_NOPS_LEN], [14])
+_NOPS_LEN=0
+_PRE_NOPS_LEN=0
+
+AS_CASE([$host_cpu],
+ [x86_64],
+ [
+ _NOPS_LEN=16
+ _PRE_NOPS_LEN=14
+ ],
+ [powerpc64le],
+ [
+ _NOPS_LEN=17
+ _PRE_NOPS_LEN=16
+ ]
+)
+
+AC_SUBST([ULP_NOPS_LEN], [$_NOPS_LEN])
+AC_SUBST([PRE_NOPS_LEN], [$_PRE_NOPS_LEN])
AC_DEFINE_UNQUOTED([ULP_NOPS_LEN], [$ULP_NOPS_LEN],
[Total number of padding nops])
AC_DEFINE_UNQUOTED([PRE_NOPS_LEN], [$PRE_NOPS_LEN],
[Padding nops before the entry point of functions])
+_LD_LINUX=""
+AC_CHECK_FILE("/usr/lib64/ld-linux-x86-64.so.2",
+ AC_SUBST([_LD_LINUX], "ld-linux-x86-64.so.2"))
+AC_CHECK_FILE("/usr/lib64/ld64.so.2",
+ AC_SUBST([_LD_LINUX], "ld64.so.2"))
+
+AC_DEFINE_UNQUOTED([LD_LINUX], ["$_LD_LINUX"], [Path to the ld-linux loader] )
+
+# Use the glibc versions installed on path
+AC_ARG_WITH([glibc],
+AS_HELP_STRING([--with-glibc=GLIBC_PATH],[Use the glibc installed on GLIBC_PATH, where the .so file is present]),
+[AC_SUBST([AM_LDFLAGS], ["-Wl,--dynamic-linker=$with_glibc/$_LD_LINUX -Wl,--rpath=$with_glibc/"])],
+[])
+
+# Check if -fpatchable-function-entry=$ULP_NOPS_LEN,$PRE_NOPS_LEN works
+# correctly.
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+ [[extern void g(void);
+ __attribute__((patchable_function_entry($_NOPS_LEN, $_PRE_NOPS_LEN)))
+ void f(void) { g(); }]])],
+ [patchable_works=yes],
+ [patchable_works=no])
+
+AS_IF([test "x$patchable_works" == "xno"],
+AC_MSG_ERROR(
+[The -fpatchable-functions-entry flag of your C compiler does not work correctly]))
+
AC_CONFIG_FILES([Makefile
include/Makefile
lib/Makefile
diff --git a/include/arch/powerpc64le/arch_common.h b/include/arch/powerpc64le/arch_common.h
new file mode 100644
index 00000000..d1a9949b
--- /dev/null
+++ b/include/arch/powerpc64le/arch_common.h
@@ -0,0 +1,34 @@
+#ifndef _ARCH_PPC64LE_H
+#define _ARCH_PPC64LE_H
+
+/** Offset of TLS pointer. */
+#define TLS_DTV_OFFSET 0x8000
+
+/** Struct used to store the registers in memory. */
+typedef struct pt_regs registers_t;
+
+/** Register in which the function stores the return value. */
+#define FUNCTION_RETURN_REG(reg) ((reg).gpr[3])
+
+/** Register which acts as a program counter. */
+#define PROGRAM_COUNTER_REG(reg) ((reg).nip)
+
+/** Register which acts as top of stack. */
+#define STACK_TOP_REG(reg) ((reg).gpr[1])
+
+/** Set the GLOBAL ENTRYPOINT REGISTER, which in power is r12. */
+#define SET_GLOBAL_ENTRYPOINT_REG(reg, val) (reg).gpr[12] = (val)
+
+/* Program load bias, which can be recovered by running `ld --verbose`. */
+#define EXECUTABLE_START 0x10000000UL
+
+/* The Red zone. */
+#define RED_ZONE_LEN 512
+
+/**
+ * Number of bytes that the kernel subtracts from the program counter,
+ * when an ongoing syscall gets interrupted and must be restarted.
+ */
+#define RESTART_SYSCALL_SIZE 0
+
+#endif
diff --git a/include/arch/x86_64/arch_common.h b/include/arch/x86_64/arch_common.h
index 16cf8baf..690f5f75 100644
--- a/include/arch/x86_64/arch_common.h
+++ b/include/arch/x86_64/arch_common.h
@@ -28,9 +28,6 @@ typedef struct user_regs_struct registers_t;
/** The red zone. */
#define RED_ZONE_LEN 128
-/** File name of the dynamic loader. */
-#define LD_LINUX "ld-linux-x86-64.so.2"
-
/**
* Number of bytes that the kernel subtracts from the program counter,
* when an ongoing syscall gets interrupted and must be restarted.
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 7071e234..4c669baf 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -29,7 +29,7 @@ libpulp_la_SOURCES = \
libpulp_la_DEPENDENCIES= libpulp.versions
libpulp_la_LDFLAGS = \
-ldl \
- -l:ld-linux-x86-64.so.2 \
+ -l:@_LD_LINUX@ \
-Wl,--version-script=$(srcdir)/libpulp.versions \
-Wl,--hash-style=sysv \ # Ubuntu seems to default to gnu, so be clear we ...
$(AM_LDFLAGS) # ... want old style hash sections, else DT_HASH is empty.
diff --git a/lib/arch/powerpc64le/patch.c b/lib/arch/powerpc64le/patch.c
new file mode 100644
index 00000000..2f0520e4
--- /dev/null
+++ b/lib/arch/powerpc64le/patch.c
@@ -0,0 +1,222 @@
+/*
+ * libpulp - User-space Livepatching Library
+ *
+ * Copyright (C) 2017-2023 SUSE Software Solutions GmbH
+ *
+ * This file is part of libpulp.
+ *
+ * libpulp is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libpulp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libpulp. If not, see .
+ */
+
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+
+#include "config.h"
+#include "error.h"
+#include "insn_queue_lib.h"
+#include "msg_queue.h"
+#include "ulp.h"
+
+/* clang-format off */
+
+/** Size of each instructions, in bytes. */
+#define INSN_SIZE 4
+
+static unsigned char ulp_prologue[INSN_SIZE * PRE_NOPS_LEN] = {
+ 0x22, 0x11, 0x80, 0x3d, // lis r12,0x1122
+ 0xa6, 0x02, 0x08, 0x7c, // mflr r0
+ 0x44, 0x33, 0x8c, 0x61, // ori r12,r12,0x3344
+ 0xc6, 0x07, 0x8c, 0x79, // sldi r12,r12,32
+ 0x66, 0x55, 0x8c, 0x65, // oris r12,r12,0x5566
+ 0x10, 0x00, 0x01, 0xf8, // std r0,16(r1)
+ 0xe1, 0xff, 0x21, 0xf8, // stdu r1,-32(r1)
+ 0x88, 0x77, 0x8c, 0x61, // ori r12,r12,0x7788
+ 0x18, 0x00, 0x41, 0xf8, // std r2,24(r1)
+ 0xa6, 0x03, 0x89, 0x7d, // mtctr r12
+ 0x21, 0x04, 0x80, 0x4e, // bctrl
+ 0x18, 0x00, 0x41, 0xe8, // ld r2,24(r1)
+ 0x20, 0x00, 0x21, 0x38, // addi r1,r1,32
+ 0x10, 0x00, 0x01, 0xe8, // ld r0,16(r1)
+ 0xa6, 0x03, 0x08, 0x7c, // mtlr r0
+ 0x20, 0x00, 0x80, 0x4e, // blr
+};
+
+/** The NOP instruction. */
+static const unsigned char gNop[] = { 0x00, 0x00, 0x00, 0x60 };
+
+/** Generate a branch (b) instruction according to offset. */
+static uint32_t
+generate_branch_to_prologue(int32_t offset)
+{
+ return (offset & 0x00FFFFFF) | (0x4B << 24);
+}
+
+#define WITH_OFFSET(x) (-(INSN_SIZE * PRE_NOPS_LEN + (offset)))
+#define WITHOUT_OFFSET WITH_OFFSET(0)
+
+/* clang-format on */
+
+/** @brief Copy the ulp prologue layout into the function to be patched's
+ * prologue
+ *
+ * This function copies the new code prologue into the old function prologue
+ * in order to redirect the execution to the new function.
+ *
+ */
+static void
+ulp_patch_prologue_layout(void *old_fentry, void *new_fentry, const unsigned char *prologue, int len)
+{
+ (void) len;
+
+ /* Create a copy of the prologue. */
+ unsigned char prolog[INSN_SIZE*PRE_NOPS_LEN];
+ _Static_assert(sizeof(prolog) == sizeof(ulp_prologue),
+ "Prologue sizes do not match");
+ memcpy(prolog, prologue, sizeof(prolog));
+
+ unsigned char new_fentry_bytes[sizeof(void*)];
+ memcpy(new_fentry_bytes, &new_fentry, sizeof(new_fentry_bytes));
+
+ /* Patch the code with the address of the function we want to be redirected. */
+ prolog[0] = new_fentry_bytes[6];
+ prolog[1] = new_fentry_bytes[7];
+ prolog[8] = new_fentry_bytes[4];
+ prolog[9] = new_fentry_bytes[5];
+ prolog[16] = new_fentry_bytes[2];
+ prolog[17] = new_fentry_bytes[3];
+ prolog[28] = new_fentry_bytes[0];
+ prolog[29] = new_fentry_bytes[1];
+
+ /* Point to the prologue. */
+ char *fentry_prologue = old_fentry - INSN_SIZE * PRE_NOPS_LEN;
+ memwrite(fentry_prologue, prolog, INSN_SIZE * PRE_NOPS_LEN);
+}
+
+/** @brief Get the offset of the NOP instruction.
+ *
+ * Some function do not have a global entry point prologue, that means
+ * the NOP instruction is placed at the same address as the calling point.
+ * We have to figure out which case we are handling.
+ */
+static int
+get_branch_offset(void *fentry)
+{
+ int valid_offsets[] = {
+ 0, // NOP located at the calling point.
+ 8, // func with global entry point, NOP is located 8 bytes after it.
+ };
+
+ for (unsigned i = 0; i < ARRAY_LENGTH(valid_offsets); i++) {
+ int offset = valid_offsets[i];
+ void *fpos = (void *) ((char *)fentry + offset);
+
+ /* Generate a branch instruction to the begining of the NOP prologue. */
+ uint32_t branch = generate_branch_to_prologue(WITH_OFFSET(offset));
+
+ /* There are two cases we must check:
+ - Function not livepatched: have a NOP insn here.
+ - Function is livepatched: have a B (branch) insn here. */
+ if (memcmp(fpos, gNop, sizeof(gNop)) == 0 ||
+ memcmp(fpos, &branch, sizeof(branch)) == 0) {
+ return offset;
+ }
+ }
+
+ /* Not valid. */
+ return -INT_MAX;
+}
+
+/** @brief skip the ulp prologue.
+ *
+ * When a function gets live patch, the nops at its entry point get replaced
+ * with a backwards-jump to a small segment of code that redirects execution to
+ * the new version of the function. However, when all live patches to said
+ * function are deactivated (because the live patches have been reversed), the
+ * need for the backwards-jump is gone.
+ *
+ * The following function replaces the backwards-jump with nops, thus making
+ * the target function look like it did at the beginning of execution, i.e.
+ * without live patches.
+ *
+ * @param fentry Address to write the prologue to.
+ */
+static int
+ulp_skip_prologue(void *fentry)
+{
+ int offset = get_branch_offset(fentry);
+ if (offset < 0) {
+ return ENOPATCHABLE;
+ }
+
+ unsigned char *dst = (unsigned char *)fentry + get_branch_offset(fentry);
+ memwrite(dst, gNop, sizeof(gNop));
+
+ return 0;
+}
+
+/** @brief Insert the backwards jump to the NOP prologue.
+ *
+ * When a function gets live patch, the nops at its entry point get replaced
+ * with a backwards-jump to a small segment of code that redirects execution to
+ * the new version of the function. This function does exactly this.
+ *
+ * @param fentry Address to write the prologue to.
+ */
+static int
+ulp_patch_addr_trampoline(void *old_fentry)
+{
+ int offset = get_branch_offset(old_fentry);
+ if (offset < 0) {
+ return ENOPATCHABLE;
+ }
+
+ uint32_t branch = generate_branch_to_prologue(WITH_OFFSET(offset));
+ char *dst = (char *)old_fentry + offset;
+ memwrite(dst, &branch, sizeof(branch));
+
+ return 0;
+}
+
+
+/** @brief Actually patch the old function with the new function
+ *
+ * This function will finally patch the old function pointed by `old_faddr`
+ * with the one pointed by `new_faddr`, replacing the ulp NOP prologue with
+ * the intended content to redirect to the new function.
+ *
+ * @param old_faddr Address of the old function.
+ * @param new_faddr Address of the new function.
+ * @param enable False to disable the redirection to the new function.
+ *
+ * @return 0 if success, error code otherwise.
+ */
+int
+ulp_patch_addr(void *old_faddr, void *new_faddr, int enable)
+{
+ unsigned char *dst = (unsigned char *) old_faddr;
+
+ int ret = 0;
+
+ if (enable) {
+ ulp_patch_prologue_layout(dst, new_faddr, ulp_prologue, 4*ULP_NOPS_LEN);
+ ret = ulp_patch_addr_trampoline(dst);
+ } else {
+ ret = ulp_skip_prologue(dst);
+ }
+
+ return ret;
+}
diff --git a/lib/arch/powerpc64le/ulp_interface.S b/lib/arch/powerpc64le/ulp_interface.S
new file mode 100644
index 00000000..cc57365e
--- /dev/null
+++ b/lib/arch/powerpc64le/ulp_interface.S
@@ -0,0 +1,124 @@
+/*
+ * libpulp - User-space Livepatching Library
+ *
+ * Copyright (C) 2017-2024 SUSE Software Solutions GmbH
+ *
+ * This file is part of libpulp.
+ *
+ * libpulp is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libpulp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libpulp. If not, see .
+ */
+
+/* Prevent the stack from being needlessly set to executable. */
+.section .note.GNU-stack,"",%progbits
+
+.section .text,"ax",@progbits
+
+/*
+ * The functions below are the entry points into libpulp, which are
+ * accessed from libpulp's tools, such as the trigger tool. The two
+ * nops here is to take into account syscall restarting by the kernel.
+ */
+
+.global __ulp_revert_all
+ nop
+ nop
+__ulp_revert_all:
+ nop
+ nop
+ addis 2, 12, .TOC.-__ulp_revert_all@ha
+ addi 2, 2, .TOC.-__ulp_revert_all@l
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ bl __ulp_revert_patches_from_lib
+ nop
+ addi %r1,%r1,32
+ ld %r0,16(%r1)
+ mtlr %r0
+ trap
+
+.globl __ulp_trigger
+.type __ulp_trigger, @function
+ nop
+ nop
+__ulp_trigger:
+ nop
+ nop
+ addis 2, 12, .TOC.-__ulp_trigger@ha
+ addi 2, 2, .TOC.-__ulp_trigger@l
+ #.localentry __ulp_trigger,.-__ulp_trigger
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ bl __ulp_apply_patch
+ nop
+ addi %r1,%r1,32
+ ld %r0,16(%r1)
+ mtlr %r0
+ trap
+
+.global __ulp_check_patched
+ nop
+ nop
+__ulp_check_patched:
+ nop
+ nop
+ addis 2, 12, .TOC.-__ulp_check_patched@ha
+ addi 2, 2, .TOC.-__ulp_check_patched@l
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ bl __ulp_check_applied_patch
+ nop
+ addi %r1,%r1,32
+ ld %r0,16(%r1)
+ mtlr %r0
+ trap
+
+.global __ulp_get_global_universe
+ nop
+ nop
+__ulp_get_global_universe:
+ nop
+ nop
+ addis 2, 12, .TOC.-__ulp_get_global_universe@ha
+ addi 2, 2, .TOC.-__ulp_get_global_universe@l
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ bl __ulp_get_global_universe_value
+ nop
+ addi %r1,%r1,32
+ ld %r0,16(%r1)
+ mtlr %r0
+ trap
+
+
+.global __ulp_enable_or_disable_patching
+ nop
+ nop
+__ulp_enable_or_disable_patching:
+ nop
+ nop
+ addis 2, 12, .TOC.-__ulp_enable_or_disable_patching@ha
+ addi 2, 2, .TOC.-__ulp_enable_or_disable_patching@l
+ mflr %r0
+ std %r0,16(%r1)
+ stdu %r1,-32(%r1)
+ bl ulp_enable_or_disable_patching
+ nop
+ addi %r1,%r1,32
+ ld %r0,16(%r1)
+ mtlr %r0
+ trap
diff --git a/lib/interpose.c b/lib/interpose.c
index 80633b8a..04934ce6 100644
--- a/lib/interpose.c
+++ b/lib/interpose.c
@@ -37,6 +37,7 @@
#include
#include
+#include "config.h"
#include "ld_rtld.h"
#include "msg_queue.h"
#include "ulp.h"
diff --git a/tests/Makefile.am b/tests/Makefile.am
index f7b7a87d..58abb6eb 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -559,7 +559,7 @@ manyprocesses_LDADD = libmanyprocesses.la
manyprocesses_DEPENDENCIES = $(POST_PROCESS) $(METADATA)
dlsym_SOURCES = dlsym.c
-dlsym_LDADD = -lpthread -ldl -lrt -l:ld-linux-x86-64.so.2
+dlsym_LDADD = -lpthread -ldl -lrt -l:@_LD_LINUX@
dlsym_DEPENDENCIES = $(POST_PROCESS) $(METADATA)
stress_SOURCES = stress.c
diff --git a/tests/asunsafe_conversion.py b/tests/asunsafe_conversion.py
index 0eba99b9..b1ca5bf7 100755
--- a/tests/asunsafe_conversion.py
+++ b/tests/asunsafe_conversion.py
@@ -34,7 +34,7 @@
errors = 0
try:
# Apply the live patch.
- child.livepatch('.libs/libblocked_livepatch1.so', retries=2000)
+ child.livepatch('.libs/libblocked_livepatch1.so', retries=100)
except subprocess.TimeoutExpired:
print('Deadlock while live patching - AS-Unsafe conversion not tested')
# The deadlock test (tests/deadlock) has a far greater chance of
diff --git a/tests/libpagecross_padding.S b/tests/libpagecross_padding.S
index 875966c7..74136c16 100644
--- a/tests/libpagecross_padding.S
+++ b/tests/libpagecross_padding.S
@@ -27,5 +27,4 @@
*/
.set FILL_SIZE, PAGE_SIZE-16
.section .text
-.align PAGE_SIZE
.fill FILL_SIZE, 1, 0xCC
diff --git a/tests/libtls.c b/tests/libtls.c
index e7b11cf7..8eb725c7 100644
--- a/tests/libtls.c
+++ b/tests/libtls.c
@@ -29,6 +29,12 @@ banner_get(void)
return banner;
}
+char **
+banner_get_ref(void)
+{
+ return &banner;
+}
+
void
banner_set(char *new)
{
diff --git a/tests/libtls_livepatch1.c b/tests/libtls_livepatch1.c
index 435d7aa6..786225a1 100644
--- a/tests/libtls_livepatch1.c
+++ b/tests/libtls_livepatch1.c
@@ -32,6 +32,8 @@ static char *ulpr_string = "String from live patch";
void *__tls_get_addr(tls_index *);
+#define SWAP(a,b) { typeof(a) _t = (a); (a) = (b); (b) = _t; }
+
void
new_banner_set(__attribute__((unused)) char *new)
{
@@ -39,6 +41,7 @@ new_banner_set(__attribute__((unused)) char *new)
errx(EXIT_FAILURE, "Live patch data references not initialized");
char **ulpr_banner = __tls_get_addr(&ti);
+ printf("module: %lx, offset: %lx\n", ti.ti_module, ti.ti_offset);
printf("addr: 0x%lx\n", (unsigned long)ulpr_banner);
*ulpr_banner = ulpr_string;
}
diff --git a/tests/process_access.c b/tests/process_access.c
index 5ab510cd..d1c9fd06 100644
--- a/tests/process_access.c
+++ b/tests/process_access.c
@@ -26,8 +26,7 @@
static char *banner = "Original banner";
/* Noinline must be set, else no calls are issued. */
-char *__attribute__((noinline)) banner_get(void) { printf("banner addr: 0x%lx\n", (unsigned long)banner); return banner; }
-
+char *__attribute__((noinline)) banner_get(void) { return banner; }
/* Since clang-18 when compiling this function it is broken in two parts:
banner_set and banner_set.specialized.1. We don't want it on this test so
diff --git a/tests/redzone.c b/tests/redzone.c
index 293c089a..536139e0 100644
--- a/tests/redzone.c
+++ b/tests/redzone.c
@@ -31,10 +31,10 @@
int
main(void)
{
- long int counter = LOOPS;
- long int result1;
- long int result2;
- long int result3;
+ volatile long int counter = LOOPS;
+ volatile long int result1;
+ volatile long int result2;
+ volatile long int result3;
/* Signal readiness. */
printf("Waiting for input.\n");
@@ -47,6 +47,7 @@ main(void)
*/
/* clang-format off */
asm volatile (
+#if defined(__x86_64__)
"movq $0, -0x08(%%rsp)\n"
"movq $0, -0x78(%%rsp)\n"
"movq $0, -0x80(%%rsp)\n"
@@ -60,6 +61,29 @@ main(void)
"movq -0x08(%%rsp), %0\n"
"movq -0x78(%%rsp), %1\n"
"movq -0x80(%%rsp), %2\n"
+#elif defined(__powerpc64__)
+ "li %%r10, 0\n"
+ "stw %%r10, -0x08(%%r1)\n"
+ "stw %%r10, -0x78(%%r1)\n"
+ "stw %%r10, -0x80(%%r1)\n"
+ "loop:\n"
+ "li %%r10, 1\n"
+ "lwa %%r11, -0x08(%%r1)\n"
+ "add %%r11, %%r11, %%r10\n"
+ "stw %%r11, -0x08(%%r1)\n"
+ "lwa %%r11, -0x78(%%r1)\n"
+ "add %%r11, %%r11, %%r10\n"
+ "stw %%r11, -0x78(%%r1)\n"
+ "lwa %%r11, -0x80(%%r1)\n"
+ "add %%r11, %%r11, %%r10\n"
+ "stw %%r11, -0x80(%%r1)\n"
+ "addi %3, %3, -1\n"
+ "cmpdi %%cr0, %3, 0\n"
+ "bne %%cr0, loop\n"
+ "lwz %0, -0x08(%%r1)\n"
+ "lwz %1, -0x78(%%r1)\n"
+ "lwz %2, -0x80(%%r1)\n"
+#endif
: "=r"(result1), "=r"(result2), "=r"(result3), "+r"(counter)
);
/* clang-format on */
diff --git a/tests/tls.c b/tests/tls.c
index 9db05afa..fa80f568 100644
--- a/tests/tls.c
+++ b/tests/tls.c
@@ -31,7 +31,8 @@
static pthread_t threads[NUM_THREADS];
static pthread_barrier_t barrier;
-char *banner_get();
+char *banner_get(void);
+char **banner_get_ref(void);
void banner_set(char *);
/* On two threads this should work. If more, race conditions will appear. */
@@ -51,7 +52,7 @@ thread_func(void *arg)
}
/* Original banner. */
- printf("Banner addr: 0x%lX\n", (unsigned long)banner_get());
+ printf("Banner addr: 0x%lX\n", (unsigned long)banner_get_ref());
printf("%s\n", banner_get());
sprintf(buf, "Banner changed from thread_func: %lu\n", i);
diff --git a/tools/arch/powerpc64le/post-arch.c b/tools/arch/powerpc64le/post-arch.c
new file mode 100644
index 00000000..30ff1d31
--- /dev/null
+++ b/tools/arch/powerpc64le/post-arch.c
@@ -0,0 +1,75 @@
+/*
+ * libpulp - User-space Livepatching Library
+ *
+ * Copyright (C) 2021-2023 SUSE Software Solutions GmbH
+ *
+ * This file is part of libpulp.
+ *
+ * libpulp is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * libpulp is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with libpulp. If not, see .
+ */
+
+
+#include
+#include
+#include
+#include
+#include
+
+#include "ulp_common.h"
+#include "post.h"
+#include "ptrace.h"
+
+/*
+ * On POWER all instructions are 4 bytes long, so there is no need
+ * to do anything in the `ulp post` command.
+ */
+void
+merge_nops_at_addr(Elf64_Addr addr, size_t amount)
+{
+ (void) addr;
+ (void) amount;
+}
+
+/** @brief Check if function at `sym_address` has the NOP preamble.
+ *
+ * Functions that are livepatchable has ULP_NOPS_LEN - PRE_NOPS_LEN at the
+ * beginning of the function. Check the existence of this preamble.
+ *
+ * @param sym_address Address of function in target process.
+ * @param pid Pid of the target process.
+ *
+ * @return True if preamble exists, false if not.
+ */
+bool
+check_preamble(ElfW(Addr) sym_address, pid_t pid)
+{
+ unsigned char bytes[12]; // 3 instructions
+
+ if (read_memory((char *)bytes, sizeof(bytes), pid, sym_address)) {
+ /* In case it was unable to read the symbol due to permission error, just
+ * warn in debug output. */
+ DEBUG("Unable to read symbol preamble at address %lx in process %d",
+ sym_address, pid);
+ return false;
+ }
+
+ const unsigned char nop[] = { 0x00, 0x00, 0x00, 0x60 };
+
+ /* Check if first or third insn is a NOP.. */
+ if (memcmp(bytes, nop, sizeof(nop)) == 0 ||
+ memcmp(bytes + 8, nop, sizeof(nop)) == 0) {
+ return true;
+ }
+ return false;
+}