#include "llvm/ADT/STLExtras.h"
#include "llvm/BinaryFormat/MachO.h"
#include "llvm/Object/MachO.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
#include <memory>
namespace llvm {
void MachOWriter::writeLoadCommands() {
uint8_t *Begin = B.getBufferStart() + headerSize();
- MachO::macho_load_command MLC;
for (const auto &LC : O.LoadCommands) {
+ // Construct a load command.
+ MachO::macho_load_command MLC = LC.MachOLoadCommand;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(MLC.segment_command_data);
+ memcpy(Begin, &MLC.segment_command_data, sizeof(MachO::segment_command));
+ Begin += sizeof(MachO::segment_command);
+
+ for (const auto &Sec : LC.Sections)
+ writeSectionInLoadCommand<MachO::section>(Sec, Begin);
+ continue;
+ case MachO::LC_SEGMENT_64:
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(MLC.segment_command_64_data);
+ memcpy(Begin, &MLC.segment_command_64_data,
+ sizeof(MachO::segment_command_64));
+ Begin += sizeof(MachO::segment_command_64);
+
+ for (const auto &Sec : LC.Sections)
+ writeSectionInLoadCommand<MachO::section_64>(Sec, Begin);
+ continue;
+ }
+
#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
case MachO::LCName: \
assert(sizeof(MachO::LCStruct) + LC.Payload.size() == \
- LC.MachOLoadCommand.load_command_data.cmdsize); \
- MLC = LC.MachOLoadCommand; \
+ MLC.load_command_data.cmdsize); \
if (IsLittleEndian != sys::IsLittleEndianHost) \
MachO::swapStruct(MLC.LCStruct##_data); \
memcpy(Begin, &MLC.LCStruct##_data, sizeof(MachO::LCStruct)); \
Begin += LC.Payload.size(); \
break;
- switch (LC.MachOLoadCommand.load_command_data.cmd) {
+ // Copy the load command as it is.
+ switch (MLC.load_command_data.cmd) {
default:
assert(sizeof(MachO::load_command) + LC.Payload.size() ==
- LC.MachOLoadCommand.load_command_data.cmdsize);
- MLC = LC.MachOLoadCommand;
+ MLC.load_command_data.cmdsize);
if (IsLittleEndian != sys::IsLittleEndianHost)
MachO::swapStruct(MLC.load_command_data);
memcpy(Begin, &MLC.load_command_data, sizeof(MachO::load_command));
}
}
+template <typename StructType>
+void MachOWriter::writeSectionInLoadCommand(const Section &Sec, uint8_t *&Out) {
+ StructType Temp;
+ assert(Sec.Segname.size() <= sizeof(Temp.segname) && "too long segment name");
+ assert(Sec.Sectname.size() <= sizeof(Temp.sectname) &&
+ "too long section name");
+ memset(&Temp, 0, sizeof(StructType));
+ memcpy(Temp.segname, Sec.Segname.data(), Sec.Segname.size());
+ memcpy(Temp.sectname, Sec.Sectname.data(), Sec.Sectname.size());
+ Temp.addr = Sec.Addr;
+ Temp.size = Sec.Size;
+ Temp.offset = Sec.Offset;
+ Temp.align = Sec.Align;
+ Temp.reloff = Sec.RelOff;
+ Temp.nreloc = Sec.NReloc;
+ Temp.flags = Sec.Flags;
+ Temp.reserved1 = Sec.Reserved1;
+ Temp.reserved2 = Sec.Reserved2;
+
+ if (IsLittleEndian != sys::IsLittleEndianHost)
+ MachO::swapStruct(Temp);
+ memcpy(Out, &Temp, sizeof(StructType));
+ Out += sizeof(StructType);
+}
+
void MachOWriter::writeSections() {
for (const auto &LC : O.LoadCommands)
for (const auto &Sec : LC.Sections) {
+ if (Sec.isVirtualSection())
+ continue;
+
assert(Sec.Offset && "Section offset can not be zero");
assert((Sec.Size == Sec.Content.size()) && "Incorrect section size");
memcpy(B.getBufferStart() + Sec.Offset, Sec.Content.data(),
(this->*WriteOp.second)();
}
+void MachOWriter::updateSizeOfCmds() {
+ auto Size = 0;
+ for (const auto &LC : O.LoadCommands) {
+ auto &MLC = LC.MachOLoadCommand;
+ auto cmd = MLC.load_command_data.cmd;
+
+ switch (cmd) {
+ case MachO::LC_SEGMENT:
+ Size += sizeof(MachO::segment_command) +
+ sizeof(MachO::section) * LC.Sections.size();
+ continue;
+ case MachO::LC_SEGMENT_64:
+ Size += sizeof(MachO::segment_command_64) +
+ sizeof(MachO::section_64) * LC.Sections.size();
+ continue;
+ }
+
+ switch (cmd) {
+#define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) \
+ case MachO::LCName: \
+ Size += sizeof(MachO::LCStruct); \
+ break;
+#include "llvm/BinaryFormat/MachO.def"
+#undef HANDLE_LOAD_COMMAND
+ }
+ }
+
+ O.Header.SizeOfCmds = Size;
+}
+
+// Updates the index and the number of local/external/undefined symbols. Here we
+// assume that MLC is a LC_DYSYMTAB and the nlist entries in the symbol table
+// are already sorted by the those types.
+void MachOWriter::updateDySymTab(MachO::macho_load_command &MLC) {
+ uint32_t NumLocalSymbols = 0;
+ auto Iter = O.SymTable.NameList.begin();
+ auto End = O.SymTable.NameList.end();
+ for (; Iter != End; Iter++) {
+ if (Iter->n_type & (MachO::N_EXT | MachO::N_PEXT))
+ break;
+
+ NumLocalSymbols++;
+ }
+
+ uint32_t NumExtDefSymbols = 0;
+ for (; Iter != End; Iter++) {
+ if ((Iter->n_type & MachO::N_TYPE) == MachO::N_UNDF)
+ break;
+
+ NumExtDefSymbols++;
+ }
+
+ MLC.dysymtab_command_data.ilocalsym = 0;
+ MLC.dysymtab_command_data.nlocalsym = NumLocalSymbols;
+ MLC.dysymtab_command_data.iextdefsym = NumLocalSymbols;
+ MLC.dysymtab_command_data.nextdefsym = NumExtDefSymbols;
+ MLC.dysymtab_command_data.iundefsym = NumLocalSymbols + NumExtDefSymbols;
+ MLC.dysymtab_command_data.nundefsym =
+ O.SymTable.NameList.size() - (NumLocalSymbols + NumExtDefSymbols);
+}
+
+// Recomputes and updates offset and size fields in load commands and sections
+// since they could be modified.
+Error MachOWriter::layout() {
+ auto SizeOfCmds = loadCommandsSize();
+ auto Offset = headerSize() + SizeOfCmds;
+ O.Header.NCmds = O.LoadCommands.size();
+ O.Header.SizeOfCmds = SizeOfCmds;
+
+ // Lay out sections.
+ for (auto &LC : O.LoadCommands) {
+ uint64_t FileOff = Offset;
+ uint64_t VMSize = 0;
+ uint64_t FileOffsetInSegment = 0;
+ for (auto &Sec : LC.Sections) {
+ if (!Sec.isVirtualSection()) {
+ auto FilePaddingSize =
+ OffsetToAlignment(FileOffsetInSegment, 1 << Sec.Align);
+ Sec.Offset = Offset + FileOffsetInSegment + FilePaddingSize;
+ Sec.Size = Sec.Content.size();
+ FileOffsetInSegment += FilePaddingSize + Sec.Size;
+ }
+
+ VMSize = std::max(VMSize, Sec.Addr + Sec.Size);
+ }
+
+ // TODO: Handle the __PAGEZERO segment.
+ auto &MLC = LC.MachOLoadCommand;
+ switch (MLC.load_command_data.cmd) {
+ case MachO::LC_SEGMENT:
+ MLC.segment_command_data.cmdsize =
+ sizeof(MachO::segment_command) +
+ sizeof(MachO::section) * LC.Sections.size();
+ MLC.segment_command_data.nsects = LC.Sections.size();
+ MLC.segment_command_data.fileoff = FileOff;
+ MLC.segment_command_data.vmsize = VMSize;
+ MLC.segment_command_data.filesize = FileOffsetInSegment;
+ break;
+ case MachO::LC_SEGMENT_64:
+ MLC.segment_command_64_data.cmdsize =
+ sizeof(MachO::segment_command_64) +
+ sizeof(MachO::section_64) * LC.Sections.size();
+ MLC.segment_command_64_data.nsects = LC.Sections.size();
+ MLC.segment_command_64_data.fileoff = FileOff;
+ MLC.segment_command_64_data.vmsize = VMSize;
+ MLC.segment_command_64_data.filesize = FileOffsetInSegment;
+ break;
+ }
+
+ Offset += FileOffsetInSegment;
+ }
+
+ // Lay out relocations.
+ for (auto &LC : O.LoadCommands)
+ for (auto &Sec : LC.Sections) {
+ Sec.RelOff = Sec.Relocations.empty() ? 0 : Offset;
+ Sec.NReloc = Sec.Relocations.size();
+ Offset += sizeof(MachO::any_relocation_info) * Sec.NReloc;
+ }
+
+ // Lay out tail stuff.
+ auto NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist);
+ for (auto &LC : O.LoadCommands) {
+ auto &MLC = LC.MachOLoadCommand;
+ auto cmd = MLC.load_command_data.cmd;
+ switch (cmd) {
+ case MachO::LC_SYMTAB:
+ MLC.symtab_command_data.symoff = Offset;
+ MLC.symtab_command_data.nsyms = O.SymTable.NameList.size();
+ Offset += NListSize * MLC.symtab_command_data.nsyms;
+ MLC.symtab_command_data.stroff = Offset;
+ Offset += MLC.symtab_command_data.strsize;
+ break;
+ case MachO::LC_DYSYMTAB: {
+ if (MLC.dysymtab_command_data.ntoc != 0 ||
+ MLC.dysymtab_command_data.nmodtab != 0 ||
+ MLC.dysymtab_command_data.nextrefsyms != 0 ||
+ MLC.dysymtab_command_data.nlocrel != 0 ||
+ MLC.dysymtab_command_data.nextrel != 0)
+ return createStringError(llvm::errc::not_supported,
+ "shared library is not yet supported");
+
+ if (MLC.dysymtab_command_data.nindirectsyms != 0)
+ return createStringError(llvm::errc::not_supported,
+ "indirect symbol table is not yet supported");
+
+ updateDySymTab(MLC);
+ break;
+ }
+ case MachO::LC_SEGMENT:
+ case MachO::LC_SEGMENT_64:
+ case MachO::LC_VERSION_MIN_MACOSX:
+ case MachO::LC_BUILD_VERSION:
+ case MachO::LC_ID_DYLIB:
+ case MachO::LC_LOAD_DYLIB:
+ case MachO::LC_UUID:
+ case MachO::LC_SOURCE_VERSION:
+ // Nothing to update.
+ break;
+ default:
+ // Abort if it's unsupported in order to prevent corrupting the object.
+ return createStringError(llvm::errc::not_supported,
+ "unsupported load command (cmd=0x%x)", cmd);
+ }
+ }
+
+ return Error::success();
+}
+
+Error MachOWriter::finalize() {
+ updateSizeOfCmds();
+
+ if (auto E = layout())
+ return E;
+
+ return Error::success();
+}
+
Error MachOWriter::write() {
if (Error E = B.allocate(totalSize()))
return E;