1//===- lib/ReaderWriter/MachO/MachONormalizedFileBinaryUtils.h ------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#ifndef LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
10#define LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
11
12#include "MachONormalizedFile.h"
13#include "lld/Common/LLVM.h"
14#include "lld/Core/Error.h"
15#include "llvm/ADT/StringRef.h"
16#include "llvm/BinaryFormat/MachO.h"
17#include "llvm/Support/Casting.h"
18#include "llvm/Support/Endian.h"
19#include "llvm/Support/ErrorHandling.h"
20#include "llvm/Support/Host.h"
21#include "llvm/Support/LEB128.h"
22#include <system_error>
23
24namespace lld {
25namespace mach_o {
26namespace normalized {
27
28class ByteBuffer {
29public:
30  ByteBuffer() : _ostream(_bytes) { }
31
32  void append_byte(uint8_t b) {
33    _ostream << b;
34  }
35  void append_uleb128(uint64_t value) {
36    llvm::encodeULEB128(value, _ostream);
37  }
38  void append_uleb128Fixed(uint64_t value, unsigned byteCount) {
39    unsigned min = llvm::getULEB128Size(value);
40    assert(min <= byteCount);
41    unsigned pad = byteCount - min;
42    llvm::encodeULEB128(value, _ostream, pad);
43  }
44  void append_sleb128(int64_t value) {
45    llvm::encodeSLEB128(value, _ostream);
46  }
47  void append_string(StringRef str) {
48    _ostream << str;
49    append_byte(0);
50  }
51  void align(unsigned alignment) {
52    while ( (_ostream.tell() % alignment) != 0 )
53      append_byte(0);
54  }
55  size_t size() {
56    return _ostream.tell();
57  }
58  const uint8_t *bytes() {
59    return reinterpret_cast<const uint8_t*>(_ostream.str().data());
60  }
61
62private:
63  SmallVector<char, 128>        _bytes;
64  // Stream ivar must be after SmallVector ivar to construct properly.
65  llvm::raw_svector_ostream     _ostream;
66};
67
68using namespace llvm::support::endian;
69using llvm::sys::getSwappedBytes;
70
71template<typename T>
72static inline uint16_t read16(const T *loc, bool isBig) {
73  assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
74  return isBig ? read16be(loc) : read16le(loc);
75}
76
77template<typename T>
78static inline uint32_t read32(const T *loc, bool isBig) {
79  assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
80  return isBig ? read32be(loc) : read32le(loc);
81}
82
83template<typename T>
84static inline uint64_t read64(const T *loc, bool isBig) {
85  assert((uint64_t)loc % alignof(T) == 0 && "invalid pointer alignment");
86  return isBig ? read64be(loc) : read64le(loc);
87}
88
89inline void write16(uint8_t *loc, uint16_t value, bool isBig) {
90  if (isBig)
91    write16be(loc, value);
92  else
93    write16le(loc, value);
94}
95
96inline void write32(uint8_t *loc, uint32_t value, bool isBig) {
97  if (isBig)
98    write32be(loc, value);
99  else
100    write32le(loc, value);
101}
102
103inline void write64(uint8_t *loc, uint64_t value, bool isBig) {
104  if (isBig)
105    write64be(loc, value);
106  else
107    write64le(loc, value);
108}
109
110inline uint32_t
111bitFieldExtract(uint32_t value, bool isBigEndianBigField, uint8_t firstBit,
112                                                          uint8_t bitCount) {
113  const uint32_t mask = ((1<<bitCount)-1);
114  const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit;
115  return (value >> shift) & mask;
116}
117
118inline void
119bitFieldSet(uint32_t &bits, bool isBigEndianBigField, uint32_t newBits,
120                            uint8_t firstBit, uint8_t bitCount) {
121  const uint32_t mask = ((1<<bitCount)-1);
122  assert((newBits & mask) == newBits);
123  const uint8_t shift = isBigEndianBigField ? (32-firstBit-bitCount) : firstBit;
124  bits &= ~(mask << shift);
125  bits |= (newBits << shift);
126}
127
128inline Relocation unpackRelocation(const llvm::MachO::any_relocation_info &r,
129                                   bool isBigEndian) {
130  uint32_t r0 = read32(&r.r_word0, isBigEndian);
131  uint32_t r1 = read32(&r.r_word1, isBigEndian);
132
133  Relocation result;
134  if (r0 & llvm::MachO::R_SCATTERED) {
135    // scattered relocation record always laid out like big endian bit field
136    result.offset     = bitFieldExtract(r0, true, 8, 24);
137    result.scattered  = true;
138    result.type       = (RelocationInfoType)
139                        bitFieldExtract(r0, true, 4, 4);
140    result.length     = bitFieldExtract(r0, true, 2, 2);
141    result.pcRel      = bitFieldExtract(r0, true, 1, 1);
142    result.isExtern   = false;
143    result.value      = r1;
144    result.symbol     = 0;
145  } else {
146    result.offset     = r0;
147    result.scattered  = false;
148    result.type       = (RelocationInfoType)
149                        bitFieldExtract(r1, isBigEndian, 28, 4);
150    result.length     = bitFieldExtract(r1, isBigEndian, 25, 2);
151    result.pcRel      = bitFieldExtract(r1, isBigEndian, 24, 1);
152    result.isExtern   = bitFieldExtract(r1, isBigEndian, 27, 1);
153    result.value      = 0;
154    result.symbol     = bitFieldExtract(r1, isBigEndian, 0, 24);
155  }
156  return result;
157}
158
159
160inline llvm::MachO::any_relocation_info
161packRelocation(const Relocation &r, bool swap, bool isBigEndian) {
162  uint32_t r0 = 0;
163  uint32_t r1 = 0;
164
165  if (r.scattered) {
166    r1 = r.value;
167    bitFieldSet(r0, true, r.offset,    8, 24);
168    bitFieldSet(r0, true, r.type,      4, 4);
169    bitFieldSet(r0, true, r.length,    2, 2);
170    bitFieldSet(r0, true, r.pcRel,     1, 1);
171    bitFieldSet(r0, true, r.scattered, 0, 1); // R_SCATTERED
172  } else {
173    r0 = r.offset;
174    bitFieldSet(r1, isBigEndian, r.type,     28, 4);
175    bitFieldSet(r1, isBigEndian, r.isExtern, 27, 1);
176    bitFieldSet(r1, isBigEndian, r.length,   25, 2);
177    bitFieldSet(r1, isBigEndian, r.pcRel,    24, 1);
178    bitFieldSet(r1, isBigEndian, r.symbol,   0,  24);
179  }
180
181  llvm::MachO::any_relocation_info result;
182  result.r_word0 = swap ? getSwappedBytes(r0) : r0;
183  result.r_word1 = swap ? getSwappedBytes(r1) : r1;
184  return result;
185}
186
187inline StringRef getString16(const char s[16]) {
188  // The StringRef(const char *) constructor passes the const char * to
189  // strlen(), so we can't use this constructor here, because if there is no
190  // null terminator in s, then strlen() will read past the end of the array.
191  return StringRef(s, strnlen(s, 16));
192}
193
194inline void setString16(StringRef str, char s[16]) {
195  memset(s, 0, 16);
196  memcpy(s, str.begin(), (str.size() > 16) ? 16: str.size());
197}
198
199// Implemented in normalizedToAtoms() and used by normalizedFromAtoms() so
200// that the same table can be used to map mach-o sections to and from
201// DefinedAtom::ContentType.
202void relocatableSectionInfoForContentType(DefinedAtom::ContentType atomType,
203                                          StringRef &segmentName,
204                                          StringRef &sectionName,
205                                          SectionType &sectionType,
206                                          SectionAttr &sectionAttrs,
207                                          bool &relocsToDefinedCanBeImplicit);
208
209} // namespace normalized
210} // namespace mach_o
211} // namespace lld
212
213#endif // LLD_READER_WRITER_MACHO_NORMALIZED_FILE_BINARY_UTILS_H
214