blob: a1f5dda9d9de089f10435a9b93ed46d91a148d43 [file] [log] [blame]
Chris Bienemanb83c1872016-05-11 22:07:48 +00001//===- yaml2macho - Convert YAML to a Mach object file --------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9///
10/// \file
11/// \brief The Mach component of yaml2obj.
12///
13//===----------------------------------------------------------------------===//
14
15#include "yaml2obj.h"
Chris Bienemana23b26f2016-05-12 17:44:48 +000016#include "llvm/ObjectYAML/MachOYAML.h"
17#include "llvm/Support/Error.h"
Chris Bienemane8e75552016-05-25 17:09:07 +000018#include "llvm/Support/LEB128.h"
Chris Bienemana23b26f2016-05-12 17:44:48 +000019#include "llvm/Support/MachO.h"
20#include "llvm/Support/YAMLTraits.h"
Chris Bienemanb83c1872016-05-11 22:07:48 +000021#include "llvm/Support/raw_ostream.h"
22
Chris Bienemane8e75552016-05-25 17:09:07 +000023#include "llvm/Support/Format.h"
24
Chris Bienemanb83c1872016-05-11 22:07:48 +000025using namespace llvm;
26
Chris Bienemana23b26f2016-05-12 17:44:48 +000027namespace {
28
29class MachOWriter {
30public:
Chris Bienemane18fee22016-05-20 22:31:50 +000031 MachOWriter(MachOYAML::Object &Obj) : Obj(Obj), is64Bit(true), fileStart(0) {
Chris Bienemana23b26f2016-05-12 17:44:48 +000032 is64Bit = Obj.Header.magic == MachO::MH_MAGIC_64 ||
33 Obj.Header.magic == MachO::MH_CIGAM_64;
Chris Bieneman26e6cea2016-05-12 18:02:13 +000034 memset(reinterpret_cast<void *>(&Header64), 0,
35 sizeof(MachO::mach_header_64));
Chris Bienemanfc889272016-05-12 18:21:09 +000036 assert((is64Bit || Obj.Header.reserved == 0xDEADBEEFu) &&
37 "32-bit MachO has reserved in header");
38 assert((!is64Bit || Obj.Header.reserved != 0xDEADBEEFu) &&
39 "64-bit MachO has missing reserved in header");
Chris Bienemana23b26f2016-05-12 17:44:48 +000040 }
41
42 Error writeMachO(raw_ostream &OS);
43
44private:
45 Error writeHeader(raw_ostream &OS);
Chris Bieneman8b5906e2016-05-13 17:41:41 +000046 Error writeLoadCommands(raw_ostream &OS);
Chris Bienemane18fee22016-05-20 22:31:50 +000047 Error writeSectionData(raw_ostream &OS);
Chris Bienemane8e75552016-05-25 17:09:07 +000048 Error writeLinkEditData(raw_ostream &OS);
49
50 void ZeroToOffset(raw_ostream &OS, size_t offset);
Chris Bienemana23b26f2016-05-12 17:44:48 +000051
Chris Bieneman8b5906e2016-05-13 17:41:41 +000052 MachOYAML::Object &Obj;
Chris Bienemana23b26f2016-05-12 17:44:48 +000053 bool is64Bit;
Chris Bienemane18fee22016-05-20 22:31:50 +000054 uint64_t fileStart;
Chris Bienemana23b26f2016-05-12 17:44:48 +000055
56 union {
57 MachO::mach_header_64 Header64;
58 MachO::mach_header Header;
59 };
60};
61
62Error MachOWriter::writeMachO(raw_ostream &OS) {
Chris Bienemane18fee22016-05-20 22:31:50 +000063 fileStart = OS.tell();
Chris Bienemana23b26f2016-05-12 17:44:48 +000064 if (auto Err = writeHeader(OS))
65 return Err;
Chris Bieneman8b5906e2016-05-13 17:41:41 +000066 if (auto Err = writeLoadCommands(OS))
67 return Err;
Chris Bienemane18fee22016-05-20 22:31:50 +000068 if (auto Err = writeSectionData(OS))
69 return Err;
Chris Bienemana23b26f2016-05-12 17:44:48 +000070 return Error::success();
71}
72
73Error MachOWriter::writeHeader(raw_ostream &OS) {
74 Header.magic = Obj.Header.magic;
75 Header.cputype = Obj.Header.cputype;
76 Header.cpusubtype = Obj.Header.cpusubtype;
77 Header.filetype = Obj.Header.filetype;
78 Header.ncmds = Obj.Header.ncmds;
79 Header.sizeofcmds = Obj.Header.sizeofcmds;
80 Header.flags = Obj.Header.flags;
Chris Bienemanfc889272016-05-12 18:21:09 +000081 Header64.reserved = Obj.Header.reserved;
Chris Bienemana23b26f2016-05-12 17:44:48 +000082
Chris Bieneman8b5906e2016-05-13 17:41:41 +000083 if (is64Bit)
Chris Bienemana23b26f2016-05-12 17:44:48 +000084 OS.write((const char *)&Header64, sizeof(MachO::mach_header_64));
85 else
86 OS.write((const char *)&Header, sizeof(MachO::mach_header));
87
88 return Error::success();
89}
90
Chris Bieneman2de17d42016-05-18 16:17:23 +000091template <typename SectionType>
92SectionType constructSection(MachOYAML::Section Sec) {
93 SectionType TempSec;
94 memcpy(reinterpret_cast<void *>(&TempSec.sectname[0]), &Sec.sectname[0], 16);
95 memcpy(reinterpret_cast<void *>(&TempSec.segname[0]), &Sec.segname[0], 16);
96 TempSec.addr = Sec.addr;
97 TempSec.size = Sec.size;
98 TempSec.offset = Sec.offset;
99 TempSec.align = Sec.align;
100 TempSec.reloff = Sec.reloff;
101 TempSec.nreloc = Sec.nreloc;
102 TempSec.flags = Sec.flags;
103 TempSec.reserved1 = Sec.reserved1;
104 TempSec.reserved2 = Sec.reserved2;
105 return TempSec;
106}
107
Chris Bieneman9f243e92016-05-19 20:54:43 +0000108template <typename StructType>
109size_t writeLoadCommandData(MachOYAML::LoadCommand &LC, raw_ostream &OS) {
110 return 0;
111}
112
113template <>
114size_t writeLoadCommandData<MachO::segment_command>(MachOYAML::LoadCommand &LC,
115 raw_ostream &OS) {
116 size_t BytesWritten = 0;
117 for (auto Sec : LC.Sections) {
118 auto TempSec = constructSection<MachO::section>(Sec);
119 OS.write(reinterpret_cast<const char *>(&(TempSec)),
120 sizeof(MachO::section));
121 BytesWritten += sizeof(MachO::section);
122 }
123 return BytesWritten;
124}
125
126template <>
127size_t
128writeLoadCommandData<MachO::segment_command_64>(MachOYAML::LoadCommand &LC,
129 raw_ostream &OS) {
130 size_t BytesWritten = 0;
131 for (auto Sec : LC.Sections) {
132 auto TempSec = constructSection<MachO::section_64>(Sec);
133 TempSec.reserved3 = Sec.reserved3;
134 OS.write(reinterpret_cast<const char *>(&(TempSec)),
135 sizeof(MachO::section_64));
136 BytesWritten += sizeof(MachO::section_64);
137 }
138 return BytesWritten;
139}
140
141size_t writePayloadString(MachOYAML::LoadCommand &LC, raw_ostream &OS) {
142 size_t BytesWritten = 0;
143 if (!LC.PayloadString.empty()) {
144 OS.write(LC.PayloadString.c_str(), LC.PayloadString.length());
145 BytesWritten = LC.PayloadString.length();
146 }
147 return BytesWritten;
148}
149
150template <>
151size_t writeLoadCommandData<MachO::dylib_command>(MachOYAML::LoadCommand &LC,
152 raw_ostream &OS) {
153 return writePayloadString(LC, OS);
154}
155
156template <>
157size_t writeLoadCommandData<MachO::dylinker_command>(MachOYAML::LoadCommand &LC,
158 raw_ostream &OS) {
159 return writePayloadString(LC, OS);
160}
161
Chris Bienemane18fee22016-05-20 22:31:50 +0000162void ZeroFillBytes(raw_ostream &OS, size_t Size) {
163 std::vector<uint8_t> FillData;
164 FillData.insert(FillData.begin(), Size, 0);
165 OS.write(reinterpret_cast<char *>(FillData.data()), Size);
166}
167
168void Fill(raw_ostream &OS, size_t Size, uint32_t Data) {
169 std::vector<uint32_t> FillData;
170 FillData.insert(FillData.begin(), (Size / 4) + 1, Data);
171 OS.write(reinterpret_cast<char *>(FillData.data()), Size);
172}
173
Chris Bienemane8e75552016-05-25 17:09:07 +0000174void MachOWriter::ZeroToOffset(raw_ostream &OS, size_t Offset) {
175 auto currOffset = OS.tell() - fileStart;
176 if (currOffset < Offset)
177 ZeroFillBytes(OS, Offset - currOffset);
178}
179
Chris Bieneman8b5906e2016-05-13 17:41:41 +0000180Error MachOWriter::writeLoadCommands(raw_ostream &OS) {
181 for (auto &LC : Obj.LoadCommands) {
Chris Bieneman3f2eb832016-05-17 19:44:06 +0000182 size_t BytesWritten = 0;
183#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
184 case MachO::LCName: \
185 OS.write(reinterpret_cast<const char *>(&(LC.Data.LCStruct##_data)), \
186 sizeof(MachO::LCStruct)); \
187 BytesWritten = sizeof(MachO::LCStruct); \
Chris Bieneman9f243e92016-05-19 20:54:43 +0000188 BytesWritten += writeLoadCommandData<MachO::LCStruct>(LC, OS); \
Chris Bieneman3f2eb832016-05-17 19:44:06 +0000189 break;
190
191 switch (LC.Data.load_command_data.cmd) {
192 default:
193 OS.write(reinterpret_cast<const char *>(&(LC.Data.load_command_data)),
194 sizeof(MachO::load_command));
195 BytesWritten = sizeof(MachO::load_command);
Chris Bieneman9f243e92016-05-19 20:54:43 +0000196 BytesWritten += writeLoadCommandData<MachO::load_command>(LC, OS);
Chris Bieneman3f2eb832016-05-17 19:44:06 +0000197 break;
198#include "llvm/Support/MachO.def"
199 }
200
Chris Bieneman9f243e92016-05-19 20:54:43 +0000201 if (LC.PayloadBytes.size() > 0) {
202 OS.write(reinterpret_cast<const char *>(LC.PayloadBytes.data()),
203 LC.PayloadBytes.size());
204 BytesWritten += LC.PayloadBytes.size();
Chris Bieneman2de17d42016-05-18 16:17:23 +0000205 }
206
Chris Bieneman9f243e92016-05-19 20:54:43 +0000207 if (LC.ZeroPadBytes > 0) {
Chris Bienemane18fee22016-05-20 22:31:50 +0000208 ZeroFillBytes(OS, LC.ZeroPadBytes);
Chris Bieneman9f243e92016-05-19 20:54:43 +0000209 BytesWritten += LC.ZeroPadBytes;
210 }
211
Chris Bieneman1abf0052016-05-19 23:26:31 +0000212 // Fill remaining bytes with 0. This will only get hit in partially
213 // specified test cases.
Chris Bieneman9f243e92016-05-19 20:54:43 +0000214 auto BytesRemaining = LC.Data.load_command_data.cmdsize - BytesWritten;
Chris Bieneman3f2eb832016-05-17 19:44:06 +0000215 if (BytesRemaining > 0) {
Chris Bienemane18fee22016-05-20 22:31:50 +0000216 ZeroFillBytes(OS, BytesRemaining);
217 }
218 }
219 return Error::success();
220}
221
222Error MachOWriter::writeSectionData(raw_ostream &OS) {
223 for (auto &LC : Obj.LoadCommands) {
224 switch (LC.Data.load_command_data.cmd) {
225 case MachO::LC_SEGMENT:
226 case MachO::LC_SEGMENT_64:
Chris Bienemane8e75552016-05-25 17:09:07 +0000227 auto currOffset = OS.tell() - fileStart;
228 auto segname = LC.Data.segment_command_data.segname;
Chris Bienemane18fee22016-05-20 22:31:50 +0000229 uint64_t segOff = is64Bit ? LC.Data.segment_command_64_data.fileoff
230 : LC.Data.segment_command_data.fileoff;
231
Chris Bienemane8e75552016-05-25 17:09:07 +0000232 if (0 == strncmp(&segname[0], "__LINKEDIT", 16)) {
233 if (auto Err = writeLinkEditData(OS))
234 return Err;
235 } else {
Chris Bienemane18fee22016-05-20 22:31:50 +0000236 // Zero Fill any data between the end of the last thing we wrote and the
237 // start of this section.
Chris Bienemane8e75552016-05-25 17:09:07 +0000238 if (currOffset < segOff) {
239 ZeroFillBytes(OS, segOff - currOffset);
Chris Bienemane18fee22016-05-20 22:31:50 +0000240 }
241
Chris Bienemane8e75552016-05-25 17:09:07 +0000242 for (auto &Sec : LC.Sections) {
243 // Zero Fill any data between the end of the last thing we wrote and
244 // the
245 // start of this section.
246 assert(
247 OS.tell() - fileStart <= Sec.offset &&
248 "Wrote too much data somewhere, section offsets don't line up.");
249 currOffset = OS.tell() - fileStart;
250 if (currOffset < Sec.offset) {
251 ZeroFillBytes(OS, Sec.offset - currOffset);
252 }
253
254 // Fills section data with 0xDEADBEEF
255 Fill(OS, Sec.size, 0xDEADBEEFu);
256 }
Chris Bienemane18fee22016-05-20 22:31:50 +0000257 }
258 uint64_t segSize = is64Bit ? LC.Data.segment_command_64_data.filesize
259 : LC.Data.segment_command_data.filesize;
Chris Bienemane8e75552016-05-25 17:09:07 +0000260 ZeroToOffset(OS, segOff + segSize);
Chris Bienemane18fee22016-05-20 22:31:50 +0000261 break;
Chris Bieneman8b5906e2016-05-13 17:41:41 +0000262 }
263 }
264 return Error::success();
265}
266
Chris Bienemane8e75552016-05-25 17:09:07 +0000267Error MachOWriter::writeLinkEditData(raw_ostream &OS) {
268 MachOYAML::LinkEditData &LinkEdit = Obj.LinkEdit;
269 MachO::dyld_info_command *DyldInfoOnlyCmd = 0;
270 MachO::symtab_command *SymtabCmd = 0;
271 for (auto &LC : Obj.LoadCommands) {
272 switch (LC.Data.load_command_data.cmd) {
273 case MachO::LC_SYMTAB:
274 SymtabCmd = &LC.Data.symtab_command_data;
275 break;
276 case MachO::LC_DYLD_INFO_ONLY:
277 DyldInfoOnlyCmd = &LC.Data.dyld_info_command_data;
278 break;
279 }
280 }
281
282 ZeroToOffset(OS, DyldInfoOnlyCmd->rebase_off);
283
284 for (auto Opcode : LinkEdit.RebaseOpcodes) {
285 uint8_t OpByte = Opcode.Opcode | Opcode.Imm;
286 OS.write(reinterpret_cast<char *>(&OpByte), 1);
287 for (auto Data : Opcode.ExtraData) {
288 encodeULEB128(Data, OS);
289 }
290 }
291
Chris Bieneman524243d2016-05-26 20:06:14 +0000292 ZeroToOffset(OS, DyldInfoOnlyCmd->bind_off);
293
294 for (auto Opcode : LinkEdit.BindOpcodes) {
295 uint8_t OpByte = Opcode.Opcode | Opcode.Imm;
296 OS.write(reinterpret_cast<char *>(&OpByte), 1);
297 for (auto Data : Opcode.ULEBExtraData) {
298 encodeULEB128(Data, OS);
299 }
300 for (auto Data : Opcode.SLEBExtraData) {
301 encodeSLEB128(Data, OS);
302 }
303 if(!Opcode.Symbol.empty()) {
304 OS.write(Opcode.Symbol.data(), Opcode.Symbol.size());
305 OS.write("\0", 1);
306 }
307 }
308
Chris Bienemane8e75552016-05-25 17:09:07 +0000309 // Fill to the end of the string table
310 ZeroToOffset(OS, SymtabCmd->stroff + SymtabCmd->strsize);
311
312 return Error::success();
313}
314
Chris Bienemana23b26f2016-05-12 17:44:48 +0000315} // end anonymous namespace
316
317int yaml2macho(yaml::Input &YIn, raw_ostream &Out) {
318 MachOYAML::Object Doc;
319 YIn >> Doc;
320 if (YIn.error()) {
321 errs() << "yaml2obj: Failed to parse YAML file!\n";
322 return 1;
323 }
324
325 MachOWriter Writer(Doc);
326 if (auto Err = Writer.writeMachO(Out)) {
327 errs() << toString(std::move(Err));
328 return 1;
329 }
330 return 0;
Chris Bienemanb83c1872016-05-11 22:07:48 +0000331}