Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 81137
b: refs/heads/master
c: eda09fb
h: refs/heads/master
i:
  81135: 0ea7016
v: v3
  • Loading branch information
Emil Medve authored and Paul Mackerras committed Dec 21, 2007
1 parent a2ccecb commit d7967e7
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 32 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 1fe58a875e4bb08125c657b1b91ac515d2bdbcbe
refs/heads/master: eda09fbdcd8c5afaa81c2f1d28e8b9725bad4d5a
77 changes: 63 additions & 14 deletions trunk/arch/powerpc/kernel/module_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/kernel.h>
#include <linux/cache.h>
#include <linux/bug.h>
#include <linux/sort.h>

#include "setup.h"

Expand Down Expand Up @@ -54,22 +55,60 @@ void module_free(struct module *mod, void *module_region)
addend) */
static unsigned int count_relocs(const Elf32_Rela *rela, unsigned int num)
{
unsigned int i, j, ret = 0;

/* Sure, this is order(n^2), but it's usually short, and not
time critical */
for (i = 0; i < num; i++) {
for (j = 0; j < i; j++) {
/* If this addend appeared before, it's
already been counted */
if (ELF32_R_SYM(rela[i].r_info)
== ELF32_R_SYM(rela[j].r_info)
&& rela[i].r_addend == rela[j].r_addend)
break;
unsigned int i, r_info, r_addend, _count_relocs;

_count_relocs = 0;
r_info = 0;
r_addend = 0;
for (i = 0; i < num; i++)
/* Only count 24-bit relocs, others don't need stubs */
if (ELF32_R_TYPE(rela[i].r_info) == R_PPC_REL24 &&
(r_info != ELF32_R_SYM(rela[i].r_info) ||
r_addend != rela[i].r_addend)) {
_count_relocs++;
r_info = ELF32_R_SYM(rela[i].r_info);
r_addend = rela[i].r_addend;
}
if (j == i) ret++;

return _count_relocs;
}

static int relacmp(const void *_x, const void *_y)
{
const Elf32_Rela *x, *y;

y = (Elf32_Rela *)_x;
x = (Elf32_Rela *)_y;

/* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to
* make the comparison cheaper/faster. It won't affect the sorting or
* the counting algorithms' performance
*/
if (x->r_info < y->r_info)
return -1;
else if (x->r_info > y->r_info)
return 1;
else if (x->r_addend < y->r_addend)
return -1;
else if (x->r_addend > y->r_addend)
return 1;
else
return 0;
}

static void relaswap(void *_x, void *_y, int size)
{
uint32_t *x, *y, tmp;
int i;

y = (uint32_t *)_x;
x = (uint32_t *)_y;

for (i = 0; i < sizeof(Elf32_Rela) / sizeof(uint32_t); i++) {
tmp = x[i];
x[i] = y[i];
y[i] = tmp;
}
return ret;
}

/* Get the potential trampolines size required of the init and
Expand Down Expand Up @@ -100,6 +139,16 @@ static unsigned long get_plt_size(const Elf32_Ehdr *hdr,
DEBUGP("Ptr: %p. Number: %u\n",
(void *)hdr + sechdrs[i].sh_offset,
sechdrs[i].sh_size / sizeof(Elf32_Rela));

/* Sort the relocation information based on a symbol and
* addend key. This is a stable O(n*log n) complexity
* alogrithm but it will reduce the complexity of
* count_relocs() to linear complexity O(n)
*/
sort((void *)hdr + sechdrs[i].sh_offset,
sechdrs[i].sh_size / sizeof(Elf32_Rela),
sizeof(Elf32_Rela), relacmp, relaswap);

ret += count_relocs((void *)hdr
+ sechdrs[i].sh_offset,
sechdrs[i].sh_size
Expand Down
81 changes: 64 additions & 17 deletions trunk/arch/powerpc/kernel/module_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <asm/module.h>
#include <asm/uaccess.h>
#include <asm/firmware.h>
#include <linux/sort.h>

#include "setup.h"

Expand Down Expand Up @@ -81,25 +82,23 @@ static struct ppc64_stub_entry ppc64_stub =
different addend) */
static unsigned int count_relocs(const Elf64_Rela *rela, unsigned int num)
{
unsigned int i, j, ret = 0;
unsigned int i, r_info, r_addend, _count_relocs;

/* FIXME: Only count external ones --RR */
/* Sure, this is order(n^2), but it's usually short, and not
time critical */
for (i = 0; i < num; i++) {
_count_relocs = 0;
r_info = 0;
r_addend = 0;
for (i = 0; i < num; i++)
/* Only count 24-bit relocs, others don't need stubs */
if (ELF64_R_TYPE(rela[i].r_info) != R_PPC_REL24)
continue;
for (j = 0; j < i; j++) {
/* If this addend appeared before, it's
already been counted */
if (rela[i].r_info == rela[j].r_info
&& rela[i].r_addend == rela[j].r_addend)
break;
if (ELF64_R_TYPE(rela[i].r_info) == R_PPC_REL24 &&
(r_info != ELF64_R_SYM(rela[i].r_info) ||
r_addend != rela[i].r_addend)) {
_count_relocs++;
r_info = ELF64_R_SYM(rela[i].r_info);
r_addend = rela[i].r_addend;
}
if (j == i) ret++;
}
return ret;

return _count_relocs;
}

void *module_alloc(unsigned long size)
Expand All @@ -118,6 +117,44 @@ void module_free(struct module *mod, void *module_region)
table entries. */
}

static int relacmp(const void *_x, const void *_y)
{
const Elf64_Rela *x, *y;

y = (Elf64_Rela *)_x;
x = (Elf64_Rela *)_y;

/* Compare the entire r_info (as opposed to ELF64_R_SYM(r_info) only) to
* make the comparison cheaper/faster. It won't affect the sorting or
* the counting algorithms' performance
*/
if (x->r_info < y->r_info)
return -1;
else if (x->r_info > y->r_info)
return 1;
else if (x->r_addend < y->r_addend)
return -1;
else if (x->r_addend > y->r_addend)
return 1;
else
return 0;
}

static void relaswap(void *_x, void *_y, int size)
{
uint64_t *x, *y, tmp;
int i;

y = (uint64_t *)_x;
x = (uint64_t *)_y;

for (i = 0; i < sizeof(Elf64_Rela) / sizeof(uint64_t); i++) {
tmp = x[i];
x[i] = y[i];
y[i] = tmp;
}
}

/* Get size of potential trampolines required. */
static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
const Elf64_Shdr *sechdrs)
Expand All @@ -133,6 +170,16 @@ static unsigned long get_stubs_size(const Elf64_Ehdr *hdr,
DEBUGP("Ptr: %p. Number: %lu\n",
(void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela));

/* Sort the relocation information based on a symbol and
* addend key. This is a stable O(n*log n) complexity
* alogrithm but it will reduce the complexity of
* count_relocs() to linear complexity O(n)
*/
sort((void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela),
sizeof(Elf64_Rela), relacmp, relaswap);

relocs += count_relocs((void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size
/ sizeof(Elf64_Rela));
Expand Down Expand Up @@ -343,7 +390,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
/* Simply set it */
*(u32 *)location = value;
break;

case R_PPC64_ADDR64:
/* Simply set it */
*(unsigned long *)location = value;
Expand Down Expand Up @@ -399,7 +446,7 @@ int apply_relocate_add(Elf64_Shdr *sechdrs,
}

/* Only replace bits 2 through 26 */
*(uint32_t *)location
*(uint32_t *)location
= (*(uint32_t *)location & ~0x03fffffc)
| (value & 0x03fffffc);
break;
Expand Down

0 comments on commit d7967e7

Please sign in to comment.