|
|
f1281f |
commit 9dbe121e3132630e9094d36c2b0624404b75beea
|
|
|
f1281f |
Author: Jan Stancek <jstancek@redhat.com>
|
|
|
f1281f |
Date: Tue Sep 1 15:49:35 2015 +0200
|
|
|
f1281f |
|
|
|
f1281f |
tests/linkhuge_rw: function ptr may not refer to .text
|
|
|
f1281f |
|
|
|
f1281f |
On some ABIs function pointer may not refer to .text section.
|
|
|
f1281f |
For example on powerPC 64-bit ABI, function pointer may refer
|
|
|
f1281f |
to a call stub from .opd section.
|
|
|
f1281f |
|
|
|
f1281f |
This creates a problem for linkhuge_rw tests which run with
|
|
|
f1281f |
HUGETLB_ELFMAP=R, because test is expecting that address of
|
|
|
f1281f |
function pointer will be backed by huge pages. But because
|
|
|
f1281f |
.opd section is from RW PT_LOAD segment, this doens't happen,
|
|
|
f1281f |
since libhugetlbfs is instructed to map only R segments via
|
|
|
f1281f |
HUGETLB_ELFMAP=R.
|
|
|
f1281f |
|
|
|
f1281f |
This patch is replacing use of function pointer with address
|
|
|
f1281f |
returned by gcc's __builtin_return_address(), that is called
|
|
|
f1281f |
by the function itself. This should provide an address that
|
|
|
f1281f |
is from an actual code, residing in .text section.
|
|
|
f1281f |
|
|
|
f1281f |
Signed-off-by: Jan Stancek <jstancek@redhat.com>
|
|
|
f1281f |
Cc: Adam Litke <agl@us.ibm.com>
|
|
|
f1281f |
Cc: Eric B Munson <emunson@mgebm.net>
|
|
|
f1281f |
Cc: Petr Holasek <pholasek@redhat.com>
|
|
|
f1281f |
Signed-off-by: Eric B Munson <emunson@mgebm.net>
|
|
|
f1281f |
|
|
|
f1281f |
diff --git a/tests/linkhuge_rw.c b/tests/linkhuge_rw.c
|
|
|
f1281f |
index f58fff2..c1c2e96 100644
|
|
|
f1281f |
--- a/tests/linkhuge_rw.c
|
|
|
f1281f |
+++ b/tests/linkhuge_rw.c
|
|
|
f1281f |
@@ -31,7 +31,8 @@
|
|
|
f1281f |
#include "hugetests.h"
|
|
|
f1281f |
|
|
|
f1281f |
#define BLOCK_SIZE 16384
|
|
|
f1281f |
-#define CONST 0xdeadbeef
|
|
|
f1281f |
+#define CONST 0xdeadbeef
|
|
|
f1281f |
+#define RETURN_ADDRESS 0x0
|
|
|
f1281f |
|
|
|
f1281f |
#define BIG_INIT { \
|
|
|
f1281f |
[0] = CONST, [17] = CONST, [BLOCK_SIZE-1] = CONST, \
|
|
|
f1281f |
@@ -45,13 +46,49 @@ static int big_bss[BLOCK_SIZE];
|
|
|
f1281f |
const int small_const = CONST;
|
|
|
f1281f |
const int big_const[BLOCK_SIZE] = BIG_INIT;
|
|
|
f1281f |
|
|
|
f1281f |
-static int static_func(int x)
|
|
|
f1281f |
+/*
|
|
|
f1281f |
+ * Turn function pointer into address from .text.
|
|
|
f1281f |
+ *
|
|
|
f1281f |
+ * On some ABIs function pointer may not refer to .text section. For example
|
|
|
f1281f |
+ * on powerPC 64-bit ABI, function pointer may refer to call stub from
|
|
|
f1281f |
+ * .opd section.
|
|
|
f1281f |
+ *
|
|
|
f1281f |
+ * This function expects that parameter data is a function pointer of type:
|
|
|
f1281f |
+ * long f(long), and when called with special parameter, it returns an address
|
|
|
f1281f |
+ * corresponding to actual code of the function. Current implementation relies
|
|
|
f1281f |
+ * on gcc's __builtin_return_address, see get_pc() below.
|
|
|
f1281f |
+ */
|
|
|
f1281f |
+static inline void *get_text_addr(void *data)
|
|
|
f1281f |
+{
|
|
|
f1281f |
+ long (*gettext)(long) = data;
|
|
|
f1281f |
+
|
|
|
f1281f |
+ return (void *)gettext(RETURN_ADDRESS);
|
|
|
f1281f |
+}
|
|
|
f1281f |
+
|
|
|
f1281f |
+static void __attribute__ ((noinline)) *get_pc(void)
|
|
|
f1281f |
+{
|
|
|
f1281f |
+#if defined(__s390__) && __WORDSIZE == 32
|
|
|
f1281f |
+ /* taken from sysdeps/unix/sysv/linux/s390/s390-32/profil-counter.h
|
|
|
f1281f |
+ * 31-bit s390 pointers don't use the 32th bit, however integers do,
|
|
|
f1281f |
+ * so wrap the value around at 31 bits */
|
|
|
f1281f |
+ return (void *)
|
|
|
f1281f |
+ ((unsigned long) __builtin_return_address(0) & 0x7fffffffUL);
|
|
|
f1281f |
+#else
|
|
|
f1281f |
+ return __builtin_return_address(0);
|
|
|
f1281f |
+#endif
|
|
|
f1281f |
+}
|
|
|
f1281f |
+
|
|
|
f1281f |
+static long static_func(long x)
|
|
|
f1281f |
{
|
|
|
f1281f |
+ if (x == RETURN_ADDRESS)
|
|
|
f1281f |
+ return (long)get_pc();
|
|
|
f1281f |
return x;
|
|
|
f1281f |
}
|
|
|
f1281f |
|
|
|
f1281f |
-int global_func(int x)
|
|
|
f1281f |
+long global_func(long x)
|
|
|
f1281f |
{
|
|
|
f1281f |
+ if (x == RETURN_ADDRESS)
|
|
|
f1281f |
+ return (long)get_pc();
|
|
|
f1281f |
return x;
|
|
|
f1281f |
}
|
|
|
f1281f |
|
|
|
f1281f |
@@ -59,27 +96,28 @@ static struct test_entry {
|
|
|
f1281f |
const char *name;
|
|
|
f1281f |
void *data;
|
|
|
f1281f |
int size;
|
|
|
f1281f |
- int writable, execable;
|
|
|
f1281f |
+ int writable;
|
|
|
f1281f |
+ int execable;
|
|
|
f1281f |
int is_huge;
|
|
|
f1281f |
} testtab[] = {
|
|
|
f1281f |
-#define ENT(name, exec) { #name, (void *)&name, sizeof(name), 0, exec, }
|
|
|
f1281f |
+#define ENT(entry_name, exec) { \
|
|
|
f1281f |
+ .name = #entry_name, \
|
|
|
f1281f |
+ .data = (void *)&entry_name, \
|
|
|
f1281f |
+ .size = sizeof(entry_name), \
|
|
|
f1281f |
+ .writable = 0, \
|
|
|
f1281f |
+ .execable = exec }
|
|
|
f1281f |
+
|
|
|
f1281f |
ENT(small_data, 0),
|
|
|
f1281f |
ENT(big_data, 0),
|
|
|
f1281f |
ENT(small_bss, 0),
|
|
|
f1281f |
ENT(big_bss, 0),
|
|
|
f1281f |
ENT(small_const, 0),
|
|
|
f1281f |
ENT(big_const, 0),
|
|
|
f1281f |
-
|
|
|
f1281f |
- /*
|
|
|
f1281f |
- * XXX: Due to the way functions are defined in the powerPC 64-bit ABI,
|
|
|
f1281f |
- * the following entries will point to a call stub in the data segment
|
|
|
f1281f |
- * instead of to the code as one might think. Therefore, test coverage
|
|
|
f1281f |
- * is not quite as good as it could be for ppc64.
|
|
|
f1281f |
- */
|
|
|
f1281f |
ENT(static_func, 1),
|
|
|
f1281f |
ENT(global_func, 1),
|
|
|
f1281f |
};
|
|
|
f1281f |
|
|
|
f1281f |
+
|
|
|
f1281f |
#define NUM_TESTS (sizeof(testtab) / sizeof(testtab[0]))
|
|
|
f1281f |
|
|
|
f1281f |
static
|
|
|
f1281f |
@@ -116,12 +154,18 @@ static void check_if_writable(struct test_entry *te)
|
|
|
f1281f |
{
|
|
|
f1281f |
int pid, ret, status;
|
|
|
f1281f |
|
|
|
f1281f |
-
|
|
|
f1281f |
pid = fork();
|
|
|
f1281f |
if (pid < 0)
|
|
|
f1281f |
FAIL("fork: %s", strerror(errno));
|
|
|
f1281f |
else if (pid == 0) {
|
|
|
f1281f |
- (*(char *) te->data) = 0;
|
|
|
f1281f |
+ void *data;
|
|
|
f1281f |
+
|
|
|
f1281f |
+ if (te->execable)
|
|
|
f1281f |
+ data = get_text_addr(te->data);
|
|
|
f1281f |
+ else
|
|
|
f1281f |
+ data = te->data;
|
|
|
f1281f |
+
|
|
|
f1281f |
+ (*(char *)data) = 0;
|
|
|
f1281f |
exit (0);
|
|
|
f1281f |
} else {
|
|
|
f1281f |
ret = waitpid(pid, &status, 0);
|
|
|
f1281f |
@@ -137,11 +181,15 @@ static void check_if_writable(struct test_entry *te)
|
|
|
f1281f |
static void do_test(struct test_entry *te)
|
|
|
f1281f |
{
|
|
|
f1281f |
int i;
|
|
|
f1281f |
- volatile int *p = te->data;
|
|
|
f1281f |
+ void *data = te->data;
|
|
|
f1281f |
|
|
|
f1281f |
check_if_writable(te);
|
|
|
f1281f |
+ verbose_printf("entry: %s, data: %p, writable: %d\n",
|
|
|
f1281f |
+ te->name, data, te->writable);
|
|
|
f1281f |
|
|
|
f1281f |
if (te->writable) {
|
|
|
f1281f |
+ volatile int *p = data;
|
|
|
f1281f |
+
|
|
|
f1281f |
for (i = 0; i < (te->size / sizeof(*p)); i++)
|
|
|
f1281f |
p[i] = CONST ^ i;
|
|
|
f1281f |
|
|
|
f1281f |
@@ -151,17 +199,23 @@ static void do_test(struct test_entry *te)
|
|
|
f1281f |
if (p[i] != (CONST ^ i))
|
|
|
f1281f |
FAIL("mismatch on %s", te->name);
|
|
|
f1281f |
} else if (te->execable) {
|
|
|
f1281f |
- int (*pf)(int) = te->data;
|
|
|
f1281f |
+ long (*pf)(long) = data;
|
|
|
f1281f |
+
|
|
|
f1281f |
+ data = get_text_addr(data);
|
|
|
f1281f |
|
|
|
f1281f |
if ((*pf)(CONST) != CONST)
|
|
|
f1281f |
FAIL("%s returns incorrect results", te->name);
|
|
|
f1281f |
} else {
|
|
|
f1281f |
/* Otherwise just read touch it */
|
|
|
f1281f |
+ volatile int *p = data;
|
|
|
f1281f |
+
|
|
|
f1281f |
for (i = 0; i < (te->size / sizeof(*p)); i++)
|
|
|
f1281f |
p[i];
|
|
|
f1281f |
}
|
|
|
f1281f |
|
|
|
f1281f |
- te->is_huge = (test_addr_huge(te->data) == 1);
|
|
|
f1281f |
+ te->is_huge = (test_addr_huge(data) == 1);
|
|
|
f1281f |
+ verbose_printf("entry: %s, data: %p, is_huge: %d\n",
|
|
|
f1281f |
+ te->name, data, te->is_huge);
|
|
|
f1281f |
}
|
|
|
f1281f |
|
|
|
f1281f |
int main(int argc, char *argv[])
|