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 §ionName, 205 SectionType §ionType, 206 SectionAttr §ionAttrs, 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