llvm-mirror/include/llvm/MC/MCFixup.h
Alex Bradbury ebc93587cb [RISCV] Add symbol diff relocation support for RISC-V
For RISC-V it is desirable to have relaxation happen in the linker once 
addresses are known, and as such the size between two instructions/byte 
sequences in a section could change.

For most assembler expressions, this is fine, as the absolute address results 
in the expression being converted to a fixup, and finally relocations. 
However, for expressions such as .quad .L2-.L1, the assembler folds this down 
to a constant once fragments are laid out, under the assumption that the 
difference can no longer change, although in the case of linker relaxation the 
differences can change at link time, so the constant is incorrect. One place 
where this commonly appears is in debug information, where the size of a 
function expression is in a form similar to the above.

This patch extends the assembler to allow an AsmBackend to declare that it 
does not want the assembler to fold down this expression, and instead generate 
a pair of relocations that allow the linker to carry out the calculation. In 
this case, the expression is not folded, but when it comes to emitting a 
fixup, the generic FK_Data_* fixups are converted into a pair, one for the 
addition half, one for the subtraction, and this is passed to the relocation 
generating methods as usual. I have named these FK_Data_Add_* and 
FK_Data_Sub_* to indicate which half these are for.

For RISC-V, which supports this via e.g. the R_RISCV_ADD64, R_RISCV_SUB64 pair 
of relocations, these are also set to always emit relocations relative to 
local symbols rather than section offsets. This is to deal with the fact that 
if relocations were calculated on e.g. .text+8 and .text+4, the result 12 
would be stored rather than 4 as both addends are added in the linker.

Differential Revision: https://reviews.llvm.org/D45181
Patch by Simon Cook.

llvm-svn: 333079
2018-05-23 12:36:18 +00:00

172 lines
6.1 KiB
C++

//===-- llvm/MC/MCFixup.h - Instruction Relocation and Patching -*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_MC_MCFIXUP_H
#define LLVM_MC_MCFIXUP_H
#include "llvm/MC/MCExpr.h"
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/SMLoc.h"
#include <cassert>
namespace llvm {
class MCExpr;
/// Extensible enumeration to represent the type of a fixup.
enum MCFixupKind {
FK_Data_1 = 0, ///< A one-byte fixup.
FK_Data_2, ///< A two-byte fixup.
FK_Data_4, ///< A four-byte fixup.
FK_Data_8, ///< A eight-byte fixup.
FK_PCRel_1, ///< A one-byte pc relative fixup.
FK_PCRel_2, ///< A two-byte pc relative fixup.
FK_PCRel_4, ///< A four-byte pc relative fixup.
FK_PCRel_8, ///< A eight-byte pc relative fixup.
FK_GPRel_1, ///< A one-byte gp relative fixup.
FK_GPRel_2, ///< A two-byte gp relative fixup.
FK_GPRel_4, ///< A four-byte gp relative fixup.
FK_GPRel_8, ///< A eight-byte gp relative fixup.
FK_DTPRel_4, ///< A four-byte dtp relative fixup.
FK_DTPRel_8, ///< A eight-byte dtp relative fixup.
FK_TPRel_4, ///< A four-byte tp relative fixup.
FK_TPRel_8, ///< A eight-byte tp relative fixup.
FK_SecRel_1, ///< A one-byte section relative fixup.
FK_SecRel_2, ///< A two-byte section relative fixup.
FK_SecRel_4, ///< A four-byte section relative fixup.
FK_SecRel_8, ///< A eight-byte section relative fixup.
FK_Data_Add_1, ///< A one-byte add fixup.
FK_Data_Add_2, ///< A two-byte add fixup.
FK_Data_Add_4, ///< A four-byte add fixup.
FK_Data_Add_8, ///< A eight-byte add fixup.
FK_Data_Sub_1, ///< A one-byte sub fixup.
FK_Data_Sub_2, ///< A two-byte sub fixup.
FK_Data_Sub_4, ///< A four-byte sub fixup.
FK_Data_Sub_8, ///< A eight-byte sub fixup.
FirstTargetFixupKind = 128,
// Limit range of target fixups, in case we want to pack more efficiently
// later.
MaxTargetFixupKind = (1 << 8)
};
/// Encode information on a single operation to perform on a byte
/// sequence (e.g., an encoded instruction) which requires assemble- or run-
/// time patching.
///
/// Fixups are used any time the target instruction encoder needs to represent
/// some value in an instruction which is not yet concrete. The encoder will
/// encode the instruction assuming the value is 0, and emit a fixup which
/// communicates to the assembler backend how it should rewrite the encoded
/// value.
///
/// During the process of relaxation, the assembler will apply fixups as
/// symbolic values become concrete. When relaxation is complete, any remaining
/// fixups become relocations in the object file (or errors, if the fixup cannot
/// be encoded on the target).
class MCFixup {
/// The value to put into the fixup location. The exact interpretation of the
/// expression is target dependent, usually it will be one of the operands to
/// an instruction or an assembler directive.
const MCExpr *Value;
/// The byte index of start of the relocation inside the MCFragment.
uint32_t Offset;
/// The target dependent kind of fixup item this is. The kind is used to
/// determine how the operand value should be encoded into the instruction.
unsigned Kind;
/// The source location which gave rise to the fixup, if any.
SMLoc Loc;
public:
static MCFixup create(uint32_t Offset, const MCExpr *Value,
MCFixupKind Kind, SMLoc Loc = SMLoc()) {
assert(unsigned(Kind) < MaxTargetFixupKind && "Kind out of range!");
MCFixup FI;
FI.Value = Value;
FI.Offset = Offset;
FI.Kind = unsigned(Kind);
FI.Loc = Loc;
return FI;
}
/// Return a fixup corresponding to the add half of a add/sub fixup pair for
/// the given Fixup.
static MCFixup createAddFor(const MCFixup &Fixup) {
MCFixup FI;
FI.Value = Fixup.getValue();
FI.Offset = Fixup.getOffset();
FI.Kind = (unsigned)getAddKindForKind(Fixup.getKind());
FI.Loc = Fixup.getLoc();
return FI;
}
/// Return a fixup corresponding to the sub half of a add/sub fixup pair for
/// the given Fixup.
static MCFixup createSubFor(const MCFixup &Fixup) {
MCFixup FI;
FI.Value = Fixup.getValue();
FI.Offset = Fixup.getOffset();
FI.Kind = (unsigned)getSubKindForKind(Fixup.getKind());
FI.Loc = Fixup.getLoc();
return FI;
}
MCFixupKind getKind() const { return MCFixupKind(Kind); }
uint32_t getOffset() const { return Offset; }
void setOffset(uint32_t Value) { Offset = Value; }
const MCExpr *getValue() const { return Value; }
/// Return the generic fixup kind for a value with the given size. It
/// is an error to pass an unsupported size.
static MCFixupKind getKindForSize(unsigned Size, bool isPCRel) {
switch (Size) {
default: llvm_unreachable("Invalid generic fixup size!");
case 1: return isPCRel ? FK_PCRel_1 : FK_Data_1;
case 2: return isPCRel ? FK_PCRel_2 : FK_Data_2;
case 4: return isPCRel ? FK_PCRel_4 : FK_Data_4;
case 8: return isPCRel ? FK_PCRel_8 : FK_Data_8;
}
}
/// Return the generic fixup kind for an addition with a given size. It
/// is an error to pass an unsupported size.
static MCFixupKind getAddKindForKind(unsigned Kind) {
switch (Kind) {
default: llvm_unreachable("Unknown type to convert!");
case FK_Data_1: return FK_Data_Add_1;
case FK_Data_2: return FK_Data_Add_2;
case FK_Data_4: return FK_Data_Add_4;
case FK_Data_8: return FK_Data_Add_8;
}
}
/// Return the generic fixup kind for an subtraction with a given size. It
/// is an error to pass an unsupported size.
static MCFixupKind getSubKindForKind(unsigned Kind) {
switch (Kind) {
default: llvm_unreachable("Unknown type to convert!");
case FK_Data_1: return FK_Data_Sub_1;
case FK_Data_2: return FK_Data_Sub_2;
case FK_Data_4: return FK_Data_Sub_4;
case FK_Data_8: return FK_Data_Sub_8;
}
}
SMLoc getLoc() const { return Loc; }
};
} // End llvm namespace
#endif