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 
24 namespace lld {
25 namespace mach_o {
26 namespace normalized {
27 
28 class ByteBuffer {
29 public:
ByteBuffer()30   ByteBuffer() : _ostream(_bytes) { }
31 
append_byte(uint8_t b)32   void append_byte(uint8_t b) {
33     _ostream << b;
34   }
append_uleb128(uint64_t value)35   void append_uleb128(uint64_t value) {
36     llvm::encodeULEB128(value, _ostream);
37   }
append_uleb128Fixed(uint64_t value,unsigned byteCount)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   }
append_sleb128(int64_t value)44   void append_sleb128(int64_t value) {
45     llvm::encodeSLEB128(value, _ostream);
46   }
append_string(StringRef str)47   void append_string(StringRef str) {
48     _ostream << str;
49     append_byte(0);
50   }
align(unsigned alignment)51   void align(unsigned alignment) {
52     while ( (_ostream.tell() % alignment) != 0 )
53       append_byte(0);
54   }
size()55   size_t size() {
56     return _ostream.tell();
57   }
bytes()58   const uint8_t *bytes() {
59     return reinterpret_cast<const uint8_t*>(_ostream.str().data());
60   }
61 
62 private:
63   SmallVector<char, 128>        _bytes;
64   // Stream ivar must be after SmallVector ivar to construct properly.
65   llvm::raw_svector_ostream     _ostream;
66 };
67 
68 using namespace llvm::support::endian;
69 using llvm::sys::getSwappedBytes;
70 
71 template<typename T>
read16(const T * loc,bool isBig)72 static 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 
77 template<typename T>
read32(const T * loc,bool isBig)78 static 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 
83 template<typename T>
read64(const T * loc,bool isBig)84 static 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 
write16(uint8_t * loc,uint16_t value,bool isBig)89 inline 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 
write32(uint8_t * loc,uint32_t value,bool isBig)96 inline 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 
write64(uint8_t * loc,uint64_t value,bool isBig)103 inline 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 
110 inline uint32_t
bitFieldExtract(uint32_t value,bool isBigEndianBigField,uint8_t firstBit,uint8_t bitCount)111 bitFieldExtract(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 
118 inline void
bitFieldSet(uint32_t & bits,bool isBigEndianBigField,uint32_t newBits,uint8_t firstBit,uint8_t bitCount)119 bitFieldSet(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 
unpackRelocation(const llvm::MachO::any_relocation_info & r,bool isBigEndian)128 inline 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 
160 inline llvm::MachO::any_relocation_info
packRelocation(const Relocation & r,bool swap,bool isBigEndian)161 packRelocation(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 
getString16(const char s[16])187 inline 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 
setString16(StringRef str,char s[16])194 inline 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.
202 void 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