mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-23 08:26:23 -06:00
4078b7760e
The old barrier implementation was very slow when running on a multi-socket machine (pcmemtest issue 16). The new implementation provides two options: - when blocked, spin on a thread-local flag - when blocked, execute a HLT instruction and wait for a NMI The first option might be faster, but we need to measure it to find out. A new boot command line option is provided to select between the two, with a third setting that uses a mixture of the two.
177 lines
4.9 KiB
C
177 lines
4.9 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2020-2022 Martin Whitaker.
|
|
//
|
|
// Derived from memtest86+ reloc.c:
|
|
//
|
|
// reloc.c - MemTest-86 Version 3.3
|
|
//
|
|
// Released under version 2 of the Gnu Public License.
|
|
// By Eric Biederman
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
|
|
#include "assert.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Constants
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Dynamic section tag values
|
|
|
|
#define DT_NULL 0 // End of dynamic section
|
|
#define DT_PLTRELSZ 2 // Size in bytes of PLT relocs
|
|
#define DT_RELA 7 // Address of Rel relocs
|
|
#define DT_RELASZ 8 // Total size of Rel relocs
|
|
#define DT_RELAENT 9
|
|
#define DT_PLTREL 20 // Type of reloc in PLT
|
|
#define DT_JMPREL 23 // Address of PLT relocs
|
|
#define DT_NUM 34 // Number of tag values
|
|
|
|
// Relocation types
|
|
|
|
#define R_X86_64_NONE 0
|
|
#define R_X86_64_RELATIVE 8
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Types
|
|
//------------------------------------------------------------------------------
|
|
|
|
typedef uint64_t Elf64_Addr;
|
|
typedef int64_t Elf64_Sxword;
|
|
typedef uint64_t Elf64_Xword;
|
|
|
|
typedef struct
|
|
{
|
|
Elf64_Sxword d_tag;
|
|
union
|
|
{
|
|
Elf64_Xword d_val;
|
|
Elf64_Addr d_ptr;
|
|
} d_un;
|
|
} Elf64_Dyn;
|
|
|
|
typedef struct
|
|
{
|
|
Elf64_Addr r_offset;
|
|
Elf64_Xword r_info;
|
|
Elf64_Sxword r_addend;
|
|
} Elf64_Rela;
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Private Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
#define ELF64_R_TYPE(r_info) ((r_info) & 0xffffffff)
|
|
|
|
/*
|
|
* Return the run-time load address of the shared object.
|
|
*/
|
|
static inline Elf64_Addr __attribute__ ((unused)) get_load_address(void)
|
|
{
|
|
Elf64_Addr addr;
|
|
__asm__ __volatile__ (
|
|
"leaq _start(%%rip), %0"
|
|
: "=r" (addr)
|
|
:
|
|
: "cc"
|
|
);
|
|
return addr;
|
|
}
|
|
|
|
/*
|
|
* Return the link-time address of _DYNAMIC. Conveniently, this is the first
|
|
* element of the GOT.
|
|
*/
|
|
static inline Elf64_Addr __attribute__ ((unused)) get_dynamic_section_offset(void)
|
|
{
|
|
Elf64_Addr offs;
|
|
__asm__ __volatile__ (
|
|
"movq _GLOBAL_OFFSET_TABLE_(%%rip), %0"
|
|
: "=r" (offs)
|
|
:
|
|
: "cc"
|
|
);
|
|
return offs;
|
|
}
|
|
|
|
static void get_dynamic_info(Elf64_Dyn *dyn_section, Elf64_Addr load_offs, Elf64_Dyn *dyn_info[DT_NUM])
|
|
{
|
|
Elf64_Dyn *dyn = dyn_section;
|
|
while (dyn->d_tag != DT_NULL) {
|
|
if (dyn->d_tag < DT_NUM) {
|
|
dyn_info[dyn->d_tag] = dyn;
|
|
}
|
|
dyn++;
|
|
}
|
|
|
|
if (dyn_info[DT_RELA] != NULL) {
|
|
assert(dyn_info[DT_RELAENT]->d_un.d_val == sizeof(Elf64_Rela));
|
|
dyn_info[DT_RELA]->d_un.d_ptr += load_offs;
|
|
}
|
|
if (dyn_info[DT_PLTREL] != NULL) {
|
|
assert(dyn_info[DT_PLTREL]->d_un.d_val == DT_RELA);
|
|
}
|
|
if (dyn_info[DT_JMPREL] != NULL) {
|
|
dyn_info[DT_JMPREL]->d_un.d_ptr += load_offs;
|
|
}
|
|
}
|
|
|
|
static void do_relocation(Elf64_Addr load_addr, Elf64_Addr load_offs, const Elf64_Rela *rel)
|
|
{
|
|
Elf64_Addr *target_addr = (Elf64_Addr *)(load_addr + rel->r_offset);
|
|
if (ELF64_R_TYPE(rel->r_info) == R_X86_64_RELATIVE) {
|
|
if (load_offs == load_addr) {
|
|
*target_addr = load_addr + rel->r_addend;
|
|
} else {
|
|
*target_addr += load_offs;
|
|
}
|
|
return;
|
|
}
|
|
if (ELF64_R_TYPE(rel->r_info) == R_X86_64_NONE) {
|
|
return;
|
|
}
|
|
assert(! "unexpected dynamic reloc type");
|
|
}
|
|
|
|
static void do_relocations(Elf64_Addr load_addr, Elf64_Addr load_offs, Elf64_Addr rel_addr, Elf64_Addr rel_size)
|
|
{
|
|
const Elf64_Rela *rel_start = (const Elf64_Rela *)(rel_addr);
|
|
const Elf64_Rela *rel_end = (const Elf64_Rela *)(rel_addr + rel_size);
|
|
|
|
for (const Elf64_Rela *rel = rel_start; rel < rel_end; rel++) {
|
|
do_relocation(load_addr, load_offs, rel);
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
void reloc(void)
|
|
{
|
|
static volatile Elf64_Addr last_load_addr = 0;
|
|
|
|
Elf64_Dyn *dyn_info[DT_NUM];
|
|
|
|
for (int i = 0; i < DT_NUM; i++) {
|
|
dyn_info[i] = NULL;
|
|
}
|
|
|
|
Elf64_Addr load_addr = get_load_address();
|
|
Elf64_Addr load_offs = load_addr - last_load_addr;
|
|
if (load_addr == last_load_addr) {
|
|
return;
|
|
}
|
|
last_load_addr = load_addr;
|
|
|
|
Elf64_Dyn *dyn_section = (Elf64_Dyn *)(load_addr + get_dynamic_section_offset());
|
|
get_dynamic_info(dyn_section, load_offs, dyn_info);
|
|
|
|
do_relocations(load_addr, load_offs, dyn_info[DT_RELA]->d_un.d_ptr, dyn_info[DT_RELASZ]->d_un.d_val);
|
|
|
|
if (dyn_info[DT_PLTREL]->d_un.d_val == DT_RELA) {
|
|
do_relocations(load_addr, load_offs, dyn_info[DT_JMPREL]->d_un.d_ptr, dyn_info[DT_PLTRELSZ]->d_un.d_val);
|
|
}
|
|
}
|