Skip to content

Commit

Permalink
[yaml2obj] - Add ELFYAML::YAMLIntUInt to fix how we parse a relocat…
Browse files Browse the repository at this point in the history
…ion `Addend` key.

This patch makes `Relocation::Addend` to be `ELFYAML::YAMLIntUInt` and not `int64_t`.

`ELFYAML::YAMLIntUInt` it is a new type and it has the following benefits/features:

1) For an 64-bit object any hex/decimal addends
   in the range [INT64_MIN, UINT64_MAX] is accepted.
2) For an 32-bit object any hex/decimal addends
   in range [INT32_MIN, UINT32_MAX] is accepted.
3) Negative hex numbers like -0xffffffff are not accepted.
4) It is printed as decimal. I.e. obj2yaml will print
   something like "Addend: 125", this matches the current behavior.

This fixes all FIXMEs in `relocation-addend.yaml`.

Differential revision: https://reviews.llvm.org/D75527
  • Loading branch information
Georgii Rymar committed Mar 17, 2020
1 parent af64948 commit 4dd5f1c
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 51 deletions.
11 changes: 10 additions & 1 deletion llvm/include/llvm/ObjectYAML/ELFYAML.h
Expand Up @@ -65,6 +65,7 @@ LLVM_YAML_STRONG_TYPEDEF(uint32_t, MIPS_AFL_FLAGS1)
LLVM_YAML_STRONG_TYPEDEF(uint32_t, MIPS_ISA)

LLVM_YAML_STRONG_TYPEDEF(StringRef, YAMLFlowString)
LLVM_YAML_STRONG_TYPEDEF(int64_t, YAMLIntUInt)

// For now, hardcode 64 bits everywhere that 32 or 64 would be needed
// since 64-bit can hold 32-bit values too.
Expand Down Expand Up @@ -439,7 +440,7 @@ struct Group : Section {

struct Relocation {
llvm::yaml::Hex64 Offset;
int64_t Addend;
YAMLIntUInt Addend;
ELF_REL Type;
Optional<StringRef> Symbol;
};
Expand Down Expand Up @@ -542,6 +543,14 @@ LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::ELFYAML::SectionName)
namespace llvm {
namespace yaml {

template <> struct ScalarTraits<ELFYAML::YAMLIntUInt> {
static void output(const ELFYAML::YAMLIntUInt &Val, void *Ctx,
raw_ostream &Out);
static StringRef input(StringRef Scalar, void *Ctx,
ELFYAML::YAMLIntUInt &Val);
static QuotingType mustQuote(StringRef) { return QuotingType::None; }
};

template <>
struct ScalarEnumerationTraits<ELFYAML::ELF_ET> {
static void enumeration(IO &IO, ELFYAML::ELF_ET &Value);
Expand Down
34 changes: 33 additions & 1 deletion llvm/lib/ObjectYAML/ELFYAML.cpp
Expand Up @@ -982,6 +982,38 @@ struct NormalizedOther {

} // end anonymous namespace

void ScalarTraits<ELFYAML::YAMLIntUInt>::output(const ELFYAML::YAMLIntUInt &Val,
void *Ctx, raw_ostream &Out) {
Out << Val;
}

StringRef ScalarTraits<ELFYAML::YAMLIntUInt>::input(StringRef Scalar, void *Ctx,
ELFYAML::YAMLIntUInt &Val) {
const bool Is64 = static_cast<ELFYAML::Object *>(Ctx)->Header.Class ==
ELFYAML::ELF_ELFCLASS(ELF::ELFCLASS64);
StringRef ErrMsg = "invalid number";
// We do not accept negative hex numbers because their meaning is ambiguous.
// For example, would -0xfffffffff mean 1 or INT32_MIN?
if (Scalar.empty() || Scalar.startswith("-0x"))
return ErrMsg;

if (Scalar.startswith("-")) {
const int64_t MinVal = Is64 ? INT64_MIN : INT32_MIN;
long long Int;
if (getAsSignedInteger(Scalar, /*Radix=*/0, Int) || (Int < MinVal))
return ErrMsg;
Val = Int;
return "";
}

const uint64_t MaxVal = Is64 ? UINT64_MAX : UINT32_MAX;
unsigned long long UInt;
if (getAsUnsignedInteger(Scalar, /*Radix=*/0, UInt) || (UInt > MaxVal))
return ErrMsg;
Val = UInt;
return "";
}

void MappingTraits<ELFYAML::Symbol>::mapping(IO &IO, ELFYAML::Symbol &Symbol) {
IO.mapOptional("Name", Symbol.Name, StringRef());
IO.mapOptional("StName", Symbol.StName);
Expand Down Expand Up @@ -1582,7 +1614,7 @@ void MappingTraits<ELFYAML::Relocation>::mapping(IO &IO,
} else
IO.mapRequired("Type", Rel.Type);

IO.mapOptional("Addend", Rel.Addend, (int64_t)0);
IO.mapOptional("Addend", Rel.Addend, (ELFYAML::YAMLIntUInt)0);
}

void MappingTraits<ELFYAML::Object>::mapping(IO &IO, ELFYAML::Object &Object) {
Expand Down
125 changes: 76 additions & 49 deletions llvm/test/tools/yaml2obj/ELF/relocation-addend.yaml
Expand Up @@ -2,36 +2,50 @@

## Case 1: Check a 64-bit object.

## Case 1.1: Document we accept an addend with the
## value INT64_MAX = 2^63-1 = 0x7FFFFFFFFFFFFFFF = 9223372036854775807.
## Case 1.1: Document we accept any hex/decimal addends in [INT64_MIN, UINT64_MAX].

# RUN: yaml2obj %s -o %t1 -D ADDEND=9223372036854775807
# RUN: llvm-readobj -r %t1 | FileCheck %s --check-prefix=MAX64
# RUN: yaml2obj %s -o %t2 -D ADDEND=0x7FFFFFFFFFFFFFFF
# RUN: llvm-readobj -r %t2 | FileCheck %s --check-prefix=MAX64
## INT64_MIN == -9223372036854775808
## UINT64_MAX == 0xffffffffffffffff

# MAX64: 0x0 R_X86_64_PC32 foo 0x7FFFFFFFFFFFFFFF
## Addend == UINT64_MAX.
# RUN: yaml2obj %s -o %t64.decimal.max -DADDEND=18446744073709551615
# RUN: llvm-readobj -r %t64.decimal.max | FileCheck %s --check-prefix=TEST -DADDEND=0xFFFFFFFFFFFFFFFF
# RUN: yaml2obj %s -o %t64.hex.max -DADDEND=0xFFFFFFFFFFFFFFFF
# RUN: llvm-readobj -r %t64.hex.max | FileCheck %s --check-prefix=TEST -DADDEND=0xFFFFFFFFFFFFFFFF

## Case 1.2: Check we report an error when an addend is greater than INT64_MAX and
## it is in decimal form. We use (INT64_MAX + 1).
# RUN: not yaml2obj %s -o %t3 -D ADDEND=9223372036854775808 2>&1 | FileCheck %s --check-prefix=OVERFLOW64
## Addend == first positive integer.
# RUN: yaml2obj %s -o %t64.decimal.first.pos -DADDEND=1
# RUN: llvm-readobj -r %t64.decimal.first.pos | FileCheck %s --check-prefix=TEST -DADDEND=0x1
# RUN: yaml2obj %s -o %t64.hex.first.pos -DADDEND=0x1
# RUN: llvm-readobj -r %t64.hex.first.pos | FileCheck %s --check-prefix=TEST -DADDEND=0x1

# OVERFLOW64: error: invalid number
## Addend == 0.
# RUN: yaml2obj %s -o %t64.decimal.null -DADDEND=0
# RUN: llvm-readobj -r %t64.decimal.null | FileCheck %s --check-prefix=TEST -DADDEND=0x0
# RUN: yaml2obj %s -o %t64.hex.null -DADDEND=0x0
# RUN: llvm-readobj -r %t64.hex.null | FileCheck %s --check-prefix=TEST -DADDEND=0x0

## Case 1.3: Document we accept an addend with the
## value INT64_MIN = -2^63 = 0x8000000000000000 = -9223372036854775808.
## Addend == first negative integer.
# RUN: yaml2obj %s -o %t64.decimal.first.neg -DADDEND=-1
# RUN: llvm-readobj -r %t64.decimal.first.neg | FileCheck %s --check-prefix=TEST -DADDEND=0xFFFFFFFFFFFFFFFF
## We do not accept negative hex addends.
# RUN: not yaml2obj %s -o /dev/null -DADDEND=-0x1 2>&1 | FileCheck %s --check-prefix=ERR

# RUN: yaml2obj %s -o %t3 -D ADDEND=-9223372036854775808
# RUN: llvm-readobj -r %t3 | FileCheck %s --check-prefix=MIN64
## Addend == INT64_MIN.
# RUN: yaml2obj %s -o %t64.decimal.min -DADDEND=-9223372036854775808
# RUN: llvm-readobj -r %t64.decimal.min | FileCheck %s --check-prefix=TEST -DADDEND=0x8000000000000000
# TEST: 0x0 R_{{.*}}_PC32 foo [[ADDEND]]

# MIN64: 0x0 R_X86_64_PC32 foo 0x8000000000000000
# Case 1.2: Document we do not accept any hex/decimal addends outside of the range specified.

## FIXME: We should support the following case instead.
# RUN: not yaml2obj %s -o /dev/null -D ADDEND=0x8000000000000000 2>&1 | FileCheck %s --check-prefix=OVERFLOW64
## Addend == 2^64.
# RUN: not yaml2obj %s -o /dev/null -DADDEND=18446744073709551616 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not yaml2obj %s -o /dev/null -DADDEND=0x10000000000000000 2>&1 | FileCheck %s --check-prefix=ERR

## Case 1.4: Check we report an error when an addend is less than INT64_MIN and
## it is in decimal form. We use (INT64_MIN - 1).
# RUN: not yaml2obj %s -o /dev/null -D ADDEND=-9223372036854775809 2>&1 | FileCheck %s --check-prefix=OVERFLOW64
## Addend == INT64_MIN - 1.
# RUN: not yaml2obj %s -o /dev/null -DADDEND=-9223372036854775809 2>&1 | FileCheck %s --check-prefix=ERR

# ERR: invalid number

--- !ELF
FileHeader:
Expand All @@ -55,43 +69,47 @@ Symbols:

## Case 2: Check a 32-bit object.

## Case 2.1: Document we accept an addend with the
## value INT32_MAX = 2^31-1 = 0x7FFFFFFF = 2,147,483,647.

# RUN: yaml2obj --docnum=2 %s -o %t4 -D ADDEND=2147483647
# RUN: llvm-readobj -r %t4 | FileCheck %s --check-prefix=MAX32
# RUN: yaml2obj --docnum=2 %s -o %t5 -D ADDEND=0x7FFFFFFF
# RUN: cmp %t4 %t5
## INT32_MIN == -2147483648
## UINT32_MAX == 0xffffffff

# MAX32: 0x0 R_386_PC32 foo 0x7FFFFFFF{{$}}
## Case 2.1: Document we accept any hex/decimal addends in [INT32_MIN, UINT32_MAX].

## Case 2.2: Check we report an error when an addend is greater than INT32_MAX and
## it is specified in decimal form. We use (INT32_MAX + 1).
## Addend == UINT32_MAX.
# RUN: yaml2obj --docnum=2 %s -o %t32.decimal.max -DADDEND=4294967295
# RUN: llvm-readobj -r %t32.decimal.max | FileCheck %s --check-prefix=TEST -DADDEND=0xFFFFFFFF
# RUN: yaml2obj --docnum=2 %s -o %t32.hex.max -DADDEND=0xFFFFFFFF
# RUN: llvm-readobj -r %t32.hex.max | FileCheck %s --check-prefix=TEST -DADDEND=0xFFFFFFFF

## FIXME: The following case should fail, see OVERFLOW64.
# RUN: yaml2obj --docnum=2 %s -o %t6 -D ADDEND=2147483648
# RUN: llvm-readobj -r %t6 | FileCheck %s --check-prefix=OVERFLOW32-1
## Addend == first positive integer.
# RUN: yaml2obj --docnum=2 %s -o %t32.decimal.first.pos -DADDEND=1
# RUN: llvm-readobj -r %t32.decimal.first.pos | FileCheck %s --check-prefix=TEST -DADDEND=0x1
# RUN: yaml2obj --docnum=2 %s -o %t32.hex.first.pos -DADDEND=0x1
# RUN: llvm-readobj -r %t32.hex.first.pos | FileCheck %s --check-prefix=TEST -DADDEND=0x1

# OVERFLOW32-1: 0x0 R_386_PC32 foo 0x80000000{{$}}
## Addend == 0.
# RUN: yaml2obj --docnum=2 %s -o %t32.decimal.null -DADDEND=0
# RUN: llvm-readobj -r %t32.decimal.null | FileCheck %s --check-prefix=TEST -DADDEND=0x0
# RUN: yaml2obj --docnum=2 %s -o %t32.hex.null -DADDEND=0x0
# RUN: llvm-readobj -r %t32.hex.null | FileCheck %s --check-prefix=TEST -DADDEND=0x0

## Case 2.3: Document we accept an addend with the
## value INT32_MIN = -2^31 = 0x80000000 = -2,147,483,648.
## Addend == first negative integer.
# RUN: yaml2obj --docnum=2 %s -o %t32.decimal.first.neg -DADDEND=-1
# RUN: llvm-readobj -r %t32.decimal.first.neg | FileCheck %s --check-prefix=TEST -DADDEND=0xFFFFFFFF
## We do not accept negative hex addends.
# RUN: not yaml2obj --docnum=2 %s -o /dev/null -DADDEND=-0x1 2>&1 | FileCheck %s --check-prefix=ERR

# RUN: yaml2obj --docnum=2 %s -o %t7 -D ADDEND=-2147483648
# RUN: llvm-readobj -r %t7 | FileCheck %s --check-prefix=MIN32
# RUN: yaml2obj --docnum=2 %s -o %t8 -D ADDEND=0x80000000
# RUN: cmp %t7 %t8
## Addend == INT32_MIN
# RUN: yaml2obj --docnum=2 %s -o %t32.decimal.min -DADDEND=-2147483648
# RUN: llvm-readobj -r %t32.decimal.min | FileCheck %s --check-prefix=TEST -DADDEND=0x80000000

# MIN32: 0x0 R_386_PC32 foo 0x80000000{{$}}
# Case 2.2: Document we do not accept any hex/decimal addends outside of the range specified.

## Case 2.4: Check we report an error when an addend is less than INT32_MIN and
## it is in decimal form. We use (INT32_MIN - 1).
## Addend == 2^32.
# RUN: not yaml2obj --docnum=2 %s -o /dev/null -DADDEND=4294967296 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not yaml2obj --docnum=2 %s -o /dev/null -DADDEND=0x100000000 2>&1 | FileCheck %s --check-prefix=ERR

## FIXME: The following case should fail, see OVERFLOW64.
# RUN: yaml2obj --docnum=2 %s -o %t9 -D ADDEND=-2147483649
# RUN: llvm-readobj -r %t9 | FileCheck %s --check-prefix=OVERFLOW32-2

# OVERFLOW32-2: 0x0 R_386_PC32 foo 0x7FFFFFFF{{$}}
## Addend == INT32_MIN - 1.
# RUN: not yaml2obj --docnum=2 %s -o /dev/null -DADDEND=-2147483649 2>&1 | FileCheck %s --check-prefix=ERR

--- !ELF
FileHeader:
Expand All @@ -112,3 +130,12 @@ Sections:
Addend: [[ADDEND]]
Symbols:
- Name: foo

## Case 3: Check we do not allow invalid values.
# RUN: not yaml2obj %s -D ADDEND=0x1122GGEE 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not yaml2obj %s -D ADDEND=-0x1122GGEE 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not yaml2obj %s -D ADDEND=1234G5 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not yaml2obj %s -D ADDEND=-1234G5 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not yaml2obj %s -D ADDEND=foo 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not yaml2obj %s -D ADDEND=- 2>&1 | FileCheck %s --check-prefix=ERR
# RUN: not yaml2obj %s -D ADDEND=--1234 2>&1 | FileCheck %s --check-prefix=ERR

0 comments on commit 4dd5f1c

Please sign in to comment.