MdePkg/BaseLib: add PatchInstructionX86()

Some edk2 modules generate X86 machine code at module execution time by:

- compiling "template" code with NASM at module build time,

- linking the object code into the module,

- and patching the immediate (constant) operands of some instructions when
  the module is executed.

Add a helper function to BaseLib so that the C code performing the
patching is easier to read and maintain.

The implementation in this patch is taken mainly from Mike Kinney's
mailing list messages at
<http://mid.mail-archive.com/E92EE9817A31E24EB0585FDF735412F5B895C360@ORSMSX113.amr.corp.intel.com>,
<http://mid.mail-archive.com/E92EE9817A31E24EB0585FDF735412F5B898BF66@ORSMSX112.amr.corp.intel.com>.

Cc: Liming Gao <liming.gao@intel.com>
Cc: Michael D Kinney <michael.d.kinney@intel.com>
Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=866
Contributed-under: TianoCore Contribution Agreement 1.1
Signed-off-by: Laszlo Ersek <lersek@redhat.com>
Reviewed-by: Liming Gao <liming.gao@intel.com>
This commit is contained in:
Laszlo Ersek 2018-02-01 22:00:40 +01:00
parent de4f7f52f2
commit 8596c14090
3 changed files with 147 additions and 0 deletions

View File

@ -6881,6 +6881,20 @@ typedef struct {
#define THUNK_ATTRIBUTE_DISABLE_A20_MASK_INT_15 0x00000002
#define THUNK_ATTRIBUTE_DISABLE_A20_MASK_KBD_CTRL 0x00000004
///
/// Type definition for representing labels in NASM source code that allow for
/// the patching of immediate operands of IA32 and X64 instructions.
///
/// While the type is technically defined as a function type (note: not a
/// pointer-to-function type), such labels in NASM source code never stand for
/// actual functions, and identifiers declared with this function type should
/// never be called. This is also why the EFIAPI calling convention specifier
/// is missing from the typedef, and why the typedef does not follow the usual
/// edk2 coding style for function (or pointer-to-function) typedefs. The VOID
/// return type and the VOID argument list are merely artifacts.
///
typedef VOID (X86_ASSEMBLY_PATCH_LABEL) (VOID);
/**
Retrieves CPUID information.
@ -9068,5 +9082,47 @@ AsmWriteTr (
IN UINT16 Selector
);
/**
Patch the immediate operand of an IA32 or X64 instruction such that the byte,
word, dword or qword operand is encoded at the end of the instruction's
binary representation.
This function should be used to update object code that was compiled with
NASM from assembly source code. Example:
NASM source code:
mov eax, strict dword 0 ; the imm32 zero operand will be patched
ASM_PFX(gPatchCr3):
mov cr3, eax
C source code:
X86_ASSEMBLY_PATCH_LABEL gPatchCr3;
PatchInstructionX86 (gPatchCr3, AsmReadCr3 (), 4);
@param[out] InstructionEnd Pointer right past the instruction to patch. The
immediate operand to patch is expected to
comprise the trailing bytes of the instruction.
If InstructionEnd is closer to address 0 than
ValueSize permits, then ASSERT().
@param[in] PatchValue The constant to write to the immediate operand.
The caller is responsible for ensuring that
PatchValue can be represented in the byte, word,
dword or qword operand (as indicated through
ValueSize); otherwise ASSERT().
@param[in] ValueSize The size of the operand in bytes; must be 1, 2,
4, or 8. ASSERT() otherwise.
**/
VOID
EFIAPI
PatchInstructionX86 (
OUT X86_ASSEMBLY_PATCH_LABEL *InstructionEnd,
IN UINT64 PatchValue,
IN UINTN ValueSize
);
#endif // defined (MDE_CPU_IA32) || defined (MDE_CPU_X64)
#endif // !defined (__BASE_LIB__)

View File

@ -431,6 +431,7 @@
X86DisablePaging64.c
X86DisablePaging32.c
X86RdRand.c
X86PatchInstruction.c
[Sources.X64]
X64/Thunk16.nasm
@ -757,6 +758,7 @@
X86DisablePaging64.c
X86DisablePaging32.c
X86RdRand.c
X86PatchInstruction.c
X64/GccInline.c | GCC
X64/Thunk16.S | XCODE
X64/SwitchStack.nasm| GCC

View File

@ -0,0 +1,89 @@
/** @file
IA-32/x64 PatchInstructionX86()
Copyright (C) 2018, Intel Corporation. All rights reserved.<BR>
Copyright (C) 2018, Red Hat, Inc.
This program and the accompanying materials are licensed and made available
under the terms and conditions of the BSD License which accompanies this
distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include "BaseLibInternals.h"
/**
Patch the immediate operand of an IA32 or X64 instruction such that the byte,
word, dword or qword operand is encoded at the end of the instruction's
binary representation.
This function should be used to update object code that was compiled with
NASM from assembly source code. Example:
NASM source code:
mov eax, strict dword 0 ; the imm32 zero operand will be patched
ASM_PFX(gPatchCr3):
mov cr3, eax
C source code:
X86_ASSEMBLY_PATCH_LABEL gPatchCr3;
PatchInstructionX86 (gPatchCr3, AsmReadCr3 (), 4);
@param[out] InstructionEnd Pointer right past the instruction to patch. The
immediate operand to patch is expected to
comprise the trailing bytes of the instruction.
If InstructionEnd is closer to address 0 than
ValueSize permits, then ASSERT().
@param[in] PatchValue The constant to write to the immediate operand.
The caller is responsible for ensuring that
PatchValue can be represented in the byte, word,
dword or qword operand (as indicated through
ValueSize); otherwise ASSERT().
@param[in] ValueSize The size of the operand in bytes; must be 1, 2,
4, or 8. ASSERT() otherwise.
**/
VOID
EFIAPI
PatchInstructionX86 (
OUT X86_ASSEMBLY_PATCH_LABEL *InstructionEnd,
IN UINT64 PatchValue,
IN UINTN ValueSize
)
{
//
// The equality ((UINTN)InstructionEnd == ValueSize) would assume a zero-size
// instruction at address 0; forbid it.
//
ASSERT ((UINTN)InstructionEnd > ValueSize);
switch (ValueSize) {
case 1:
ASSERT (PatchValue <= MAX_UINT8);
*((UINT8 *)(UINTN)InstructionEnd - 1) = (UINT8)PatchValue;
break;
case 2:
ASSERT (PatchValue <= MAX_UINT16);
WriteUnaligned16 ((UINT16 *)(UINTN)InstructionEnd - 1, (UINT16)PatchValue);
break;
case 4:
ASSERT (PatchValue <= MAX_UINT32);
WriteUnaligned32 ((UINT32 *)(UINTN)InstructionEnd - 1, (UINT32)PatchValue);
break;
case 8:
WriteUnaligned64 ((UINT64 *)(UINTN)InstructionEnd - 1, PatchValue);
break;
default:
ASSERT (FALSE);
}
}