diff --git a/include/llvm/MC/MCAssembler.h b/include/llvm/MC/MCAssembler.h index b2a24159a75..a5739f2bc97 100644 --- a/include/llvm/MC/MCAssembler.h +++ b/include/llvm/MC/MCAssembler.h @@ -10,8 +10,11 @@ #ifndef LLVM_MC_MCASSEMBLER_H #define LLVM_MC_MCASSEMBLER_H +#include "llvm/ADT/SmallString.h" #include "llvm/ADT/ilist.h" #include "llvm/ADT/ilist_node.h" +#include "llvm/MC/MCValue.h" +#include "llvm/Support/Casting.h" #include "llvm/Support/DataTypes.h" namespace llvm { @@ -25,7 +28,211 @@ class MCFragment : public ilist_node { void operator=(const MCFragment&); // DO NOT IMPLEMENT public: - MCFragment(MCSectionData *SD = 0); + enum FragmentType { + FT_Data, + FT_Align, + FT_Fill, + FT_Org + }; + +private: + FragmentType Kind; + + /// @name Assembler Backend Data + /// @{ + // + // FIXME: This could all be kept private to the assembler implementation. + + /// FileOffset - The offset of this section in the object file. This is ~0 + /// until initialized. + uint64_t FileOffset; + + /// FileSize - The size of this section in the object file. This is ~0 until + /// initialized. + uint64_t FileSize; + + /// @} + +protected: + MCFragment(FragmentType _Kind, MCSectionData *SD = 0); + +public: + // Only for sentinel. + MCFragment(); + virtual ~MCFragment(); + + FragmentType getKind() const { return Kind; } + + // FIXME: This should be abstract, fix sentinel. + virtual unsigned getMaxFileSize() const { + assert(0 && "Invalid getMaxFileSize call !"); + }; + + /// @name Assembler Backend Support + /// @{ + // + // FIXME: This could all be kept private to the assembler implementation. + + unsigned getFileSize() const { + assert(FileSize != ~UINT64_C(0) && "File size not set!"); + return FileSize; + } + void setFileSize(uint64_t Value) { + assert(Value <= getMaxFileSize() && "Invalid file size!"); + FileSize = Value; + } + + uint64_t getFileOffset() const { + assert(FileOffset != ~UINT64_C(0) && "File offset not set!"); + return FileOffset; + } + void setFileOffset(uint64_t Value) { FileOffset = Value; } + + /// @} + + static bool classof(const MCFragment *O) { return true; } +}; + +class MCDataFragment : public MCFragment { + SmallString<32> Contents; + +public: + MCDataFragment(MCSectionData *SD = 0) : MCFragment(FT_Data, SD) {} + + /// @name Accessors + /// @{ + + unsigned getMaxFileSize() const { + return Contents.size(); + } + + SmallString<32> &getContents() { return Contents; } + const SmallString<32> &getContents() const { return Contents; } + + /// @} + + static bool classof(const MCFragment *F) { + return F->getKind() == MCFragment::FT_Data; + } + static bool classof(const MCDataFragment *) { return true; } +}; + +class MCAlignFragment : public MCFragment { + /// Alignment - The alignment to ensure, in bytes. + unsigned Alignment; + + /// Value - Value to use for filling padding bytes. + int64_t Value; + + /// ValueSize - The size of the integer (in bytes) of \arg Value. + unsigned ValueSize; + + /// MaxBytesToEmit - The maximum number of bytes to emit; if the alignment + /// cannot be satisfied in this width then this fragment is ignored. + unsigned MaxBytesToEmit; + +public: + MCAlignFragment(unsigned _Alignment, int64_t _Value, unsigned _ValueSize, + unsigned _MaxBytesToEmit, MCSectionData *SD = 0) + : MCFragment(FT_Align, SD), Alignment(_Alignment), + Value(_Value),ValueSize(_ValueSize), + MaxBytesToEmit(_MaxBytesToEmit) {} + + /// @name Accessors + /// @{ + + unsigned getMaxFileSize() const { + return std::max(Alignment - 1, MaxBytesToEmit); + } + + unsigned getAlignment() const { return Alignment; } + + int64_t getValue() const { return Value; } + + unsigned getValueSize() const { return ValueSize; } + + unsigned getMaxBytesToEmit() const { return MaxBytesToEmit; } + + /// @} + + static bool classof(const MCFragment *F) { + return F->getKind() == MCFragment::FT_Align; + } + static bool classof(const MCAlignFragment *) { return true; } +}; + +class MCFillFragment : public MCFragment { + /// Value - Value to use for filling bytes. + MCValue Value; + + /// ValueSize - The size (in bytes) of \arg Value to use when filling. + unsigned ValueSize; + + /// Count - The number of copies of \arg Value to insert. + uint64_t Count; + +public: + MCFillFragment(MCValue _Value, unsigned _ValueSize, uint64_t _Count, + MCSectionData *SD = 0) + : MCFragment(FT_Fill, SD), + Value(_Value), ValueSize(_ValueSize), Count(_Count) {} + + /// @name Accessors + /// @{ + + unsigned getMaxFileSize() const { + return ValueSize * Count; + } + + MCValue getValue() const { return Value; } + + unsigned getValueSize() const { return ValueSize; } + + uint64_t getCount() const { return Count; } + + /// @} + + static bool classof(const MCFragment *F) { + return F->getKind() == MCFragment::FT_Fill; + } + static bool classof(const MCFillFragment *) { return true; } +}; + +class MCOrgFragment : public MCFragment { + /// Offset - The offset this fragment should start at. + MCValue Offset; + + /// Value - Value to use for filling bytes. + int64_t Value; + + /// ValueSize - The size (in bytes) of \arg Value to use when filling. + unsigned ValueSize; + +public: + MCOrgFragment(MCValue _Offset, int64_t _Value, unsigned _ValueSize, + MCSectionData *SD = 0) + : MCFragment(FT_Org, SD), + Offset(_Offset), Value(_Value), ValueSize(_ValueSize) {} + /// @name Accessors + /// @{ + + unsigned getMaxFileSize() const { + // FIXME + return 0; + } + + MCValue getOffset() const { return Offset; } + + int64_t getValue() const { return Value; } + + unsigned getValueSize() const { return ValueSize; } + + /// @} + + static bool classof(const MCFragment *F) { + return F->getKind() == MCFragment::FT_Org; + } + static bool classof(const MCOrgFragment *) { return true; } }; // FIXME: Should this be a separate class, or just merged into MCSection? Since @@ -38,6 +245,9 @@ class MCSectionData : public ilist_node { public: typedef iplist FragmentListType; + typedef FragmentListType::const_iterator const_iterator; + typedef FragmentListType::iterator iterator; + private: iplist Fragments; const MCSection &Section; @@ -50,10 +260,12 @@ private: // // FIXME: This could all be kept private to the assembler implementation. - /// FileOffset - The offset of this section in the object file. + /// FileOffset - The offset of this section in the object file. This is ~0 + /// until initialized. uint64_t FileOffset; - /// FileSize - The size of this section in the object file. + /// FileSize - The size of this section in the object file. This is ~0 until + /// initialized. uint64_t FileSize; /// @} @@ -63,26 +275,44 @@ public: MCSectionData(); MCSectionData(const MCSection &Section, MCAssembler *A = 0); - const FragmentListType &getFragmentList() const { return Fragments; } - FragmentListType &getFragmentList() { return Fragments; } - const MCSection &getSection() const { return Section; } unsigned getAlignment() const { return Alignment; } void setAlignment(unsigned Value) { Alignment = Value; } + + /// @name Section List Access + /// @{ + + const FragmentListType &getFragmentList() const { return Fragments; } + FragmentListType &getFragmentList() { return Fragments; } + + iterator begin() { return Fragments.begin(); } + const_iterator begin() const { return Fragments.begin(); } + + iterator end() { return Fragments.end(); } + const_iterator end() const { return Fragments.end(); } + + size_t size() const { return Fragments.size(); } + + /// @} /// @name Assembler Backend Support /// @{ // // FIXME: This could all be kept private to the assembler implementation. - unsigned getFileSize() const { return FileSize; } + unsigned getFileSize() const { + assert(FileSize != ~UINT64_C(0) && "File size not set!"); + return FileSize; + } + void setFileSize(uint64_t Value) { FileSize = Value; } - uint64_t getFileOffset() const { return FileOffset; } + uint64_t getFileOffset() const { + assert(FileOffset != ~UINT64_C(0) && "File offset not set!"); + return FileOffset; + } void setFileOffset(uint64_t Value) { FileOffset = Value; } - void WriteFileData(raw_ostream &OS) const; - /// @} }; @@ -101,6 +331,12 @@ private: iplist Sections; +private: + /// LayoutSection - Assign offsets and sizes to the fragments in the section + /// \arg SD, and update the section size. The section file offset should + /// already have been computed. + void LayoutSection(MCSectionData &SD); + public: /// Construct a new assembler instance. /// diff --git a/lib/MC/MCAssembler.cpp b/lib/MC/MCAssembler.cpp index 7a1e935af48..883cabe45c3 100644 --- a/lib/MC/MCAssembler.cpp +++ b/lib/MC/MCAssembler.cpp @@ -10,6 +10,7 @@ #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCSectionMachO.h" #include "llvm/Support/DataTypes.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachOWriterInfo.h" @@ -48,17 +49,37 @@ public: /// @name Helper Methods /// @{ + void Write8(uint8_t Value) { + OS << char(Value); + } + + void Write16(uint16_t Value) { + if (IsLSB) { + Write8(uint8_t(Value >> 0)); + Write8(uint8_t(Value >> 8)); + } else { + Write8(uint8_t(Value >> 8)); + Write8(uint8_t(Value >> 0)); + } + } + void Write32(uint32_t Value) { if (IsLSB) { - OS << char(Value >> 0); - OS << char(Value >> 8); - OS << char(Value >> 16); - OS << char(Value >> 24); + Write16(uint16_t(Value >> 0)); + Write16(uint16_t(Value >> 16)); } else { - OS << char(Value >> 24); - OS << char(Value >> 16); - OS << char(Value >> 8); - OS << char(Value >> 0); + Write16(uint16_t(Value >> 16)); + Write16(uint16_t(Value >> 0)); + } + } + + void Write64(uint64_t Value) { + if (IsLSB) { + Write32(uint32_t(Value >> 0)); + Write32(uint32_t(Value >> 32)); + } else { + Write32(uint32_t(Value >> 32)); + Write32(uint32_t(Value >> 0)); } } @@ -115,7 +136,12 @@ public: Write32(CmdSize); } - void WriteSegmentLoadCommand32(unsigned NumSections) { + /// WriteSegmentLoadCommand32 - Write a 32-bit segment load command. + /// + /// \arg NumSections - The number of sections in this segment. + /// \arg SectionDataSize - The total size of the sections. + void WriteSegmentLoadCommand32(unsigned NumSections, + uint64_t SectionDataSize) { // struct segment_command (56 bytes) uint64_t Start = OS.tell(); @@ -126,10 +152,10 @@ public: WriteString("", 16); Write32(0); // vmaddr - Write32(0); // vmsize + Write32(SectionDataSize); // vmsize Write32(Header32Size + SegmentLoadCommand32Size + NumSections * Section32Size); // file offset - Write32(0); // file size + Write32(SectionDataSize); // file size Write32(0x7); // maxprot Write32(0x7); // initprot Write32(NumSections); @@ -169,12 +195,21 @@ public: /* *** */ -MCFragment::MCFragment(MCSectionData *SD) +MCFragment::MCFragment() : Kind(FragmentType(~0)) { +} + +MCFragment::MCFragment(FragmentType _Kind, MCSectionData *SD) + : Kind(_Kind), + FileOffset(~UINT64_C(0)), + FileSize(~UINT64_C(0)) { if (SD) SD->getFragmentList().push_back(this); } +MCFragment::~MCFragment() { +} + /* *** */ MCSectionData::MCSectionData() : Section(*(MCSection*)0) {} @@ -182,17 +217,13 @@ MCSectionData::MCSectionData() : Section(*(MCSection*)0) {} MCSectionData::MCSectionData(const MCSection &_Section, MCAssembler *A) : Section(_Section), Alignment(1), - FileOffset(0), - FileSize(0) + FileOffset(~UINT64_C(0)), + FileSize(~UINT64_C(0)) { if (A) A->getSectionList().push_back(this); } -void MCSectionData::WriteFileData(raw_ostream &OS) const { - -} - /* *** */ MCAssembler::MCAssembler(raw_ostream &_OS) : OS(_OS) {} @@ -200,21 +231,97 @@ MCAssembler::MCAssembler(raw_ostream &_OS) : OS(_OS) {} MCAssembler::~MCAssembler() { } +void MCAssembler::LayoutSection(MCSectionData &SD) { + uint64_t Offset = SD.getFileOffset(); + + for (MCSectionData::iterator it = SD.begin(), ie = SD.end(); it != ie; ++it) { + MCFragment &F = *it; + F.setFileOffset(Offset); + F.setFileSize(F.getMaxFileSize()); + Offset += F.getFileSize(); + } + + // FIXME: Pad section? + SD.setFileSize(Offset - SD.getFileOffset()); +} + +/// WriteFileData - Write the \arg F data to the output file. +static void WriteFileData(raw_ostream &OS, const MCFragment &F, + MachObjectWriter &MOW) { + uint64_t Start = OS.tell(); + (void) Start; + + // FIXME: Embed in fragments instead? + switch (F.getKind()) { + default: + assert(0 && "Invalid section kind!"); + + case MCFragment::FT_Data: + OS << cast(F).getContents().str(); + break; + + case MCFragment::FT_Align: + llvm_unreachable("FIXME: Not yet implemented!"); + + case MCFragment::FT_Fill: { + MCFillFragment &FF = cast(F); + + if (!FF.getValue().isAbsolute()) + llvm_unreachable("FIXME: Not yet implemented!"); + + for (uint64_t i = 0, e = FF.getCount(); i != e; ++i) { + switch (FF.getValueSize()) { + default: + assert(0 && "Invalid size!"); + case 1: MOW.Write8 (uint8_t (FF.getValue().getConstant())); break; + case 2: MOW.Write16(uint16_t(FF.getValue().getConstant())); break; + case 4: MOW.Write32(uint32_t(FF.getValue().getConstant())); break; + case 8: MOW.Write64(uint64_t(FF.getValue().getConstant())); break; + } + } + break; + } + + case MCFragment::FT_Org: + llvm_unreachable("FIXME: Not yet implemented!"); + } + + assert(OS.tell() - Start == F.getFileSize()); +} + +/// WriteFileData - Write the \arg SD data to the output file. +static void WriteFileData(raw_ostream &OS, const MCSectionData &SD, + MachObjectWriter &MOW) { + uint64_t Start = OS.tell(); + (void) Start; + + for (MCSectionData::const_iterator it = SD.begin(), + ie = SD.end(); it != ie; ++it) + WriteFileData(OS, *it, MOW); + + assert(OS.tell() - Start == SD.getFileSize()); +} + void MCAssembler::Finish() { unsigned NumSections = Sections.size(); - - // Compute the file offsets so we can write in a single pass. + + // Layout the sections and fragments. uint64_t Offset = MachObjectWriter::getPrologSize32(NumSections); + uint64_t SectionDataSize = 0; for (iterator it = begin(), ie = end(); it != ie; ++it) { it->setFileOffset(Offset); + + LayoutSection(*it); + Offset += it->getFileSize(); + SectionDataSize += it->getFileSize(); } MachObjectWriter MOW(OS); // Write the prolog, starting with the header and load command... MOW.WriteHeader32(NumSections); - MOW.WriteSegmentLoadCommand32(NumSections); + MOW.WriteSegmentLoadCommand32(NumSections, SectionDataSize); // ... and then the section headers. for (iterator it = begin(), ie = end(); it != ie; ++it) @@ -222,7 +329,7 @@ void MCAssembler::Finish() { // Finally, write the section data. for (iterator it = begin(), ie = end(); it != ie; ++it) - it->WriteFileData(OS); - + WriteFileData(OS, *it, MOW); + OS.flush(); } diff --git a/lib/MC/MCMachOStreamer.cpp b/lib/MC/MCMachOStreamer.cpp index f34fcd9a61f..0ad03273cf0 100644 --- a/lib/MC/MCMachOStreamer.cpp +++ b/lib/MC/MCMachOStreamer.cpp @@ -139,22 +139,28 @@ void MCMachOStreamer::EmitZerofill(MCSection *Section, MCSymbol *Symbol, } void MCMachOStreamer::EmitBytes(const StringRef &Data) { - llvm_unreachable("FIXME: Not yet implemented!"); + MCDataFragment *DF = new MCDataFragment(CurSectionData); + DF->getContents().append(Data.begin(), Data.end()); } void MCMachOStreamer::EmitValue(const MCValue &Value, unsigned Size) { - llvm_unreachable("FIXME: Not yet implemented!"); + new MCFillFragment(Value, Size, 1, CurSectionData); } void MCMachOStreamer::EmitValueToAlignment(unsigned ByteAlignment, int64_t Value, unsigned ValueSize, unsigned MaxBytesToEmit) { - llvm_unreachable("FIXME: Not yet implemented!"); + new MCAlignFragment(ByteAlignment, Value, ValueSize, MaxBytesToEmit, + CurSectionData); + + // Update the maximum alignment on the current section if necessary + if (ByteAlignment > CurSectionData->getAlignment()) + CurSectionData->setAlignment(ByteAlignment); } void MCMachOStreamer::EmitValueToOffset(const MCValue &Offset, unsigned char Value) { - llvm_unreachable("FIXME: Not yet implemented!"); + new MCOrgFragment(Offset, Value, 1, CurSectionData); } void MCMachOStreamer::EmitInstruction(const MCInst &Inst) { diff --git a/test/MC/MachO/data.s b/test/MC/MachO/data.s new file mode 100644 index 00000000000..3d39b53593c --- /dev/null +++ b/test/MC/MachO/data.s @@ -0,0 +1,60 @@ +// RUN: llvm-mc %s -filetype=obj -o - | macho-dump | FileCheck %s + + .data + .ascii "hello" + .byte 0xAB + .short 0xABCD + .long 0xABCDABCD + .quad 0xABCDABCDABCDABCD + +// CHECK: ('cputype', 7) +// CHECK: ('cpusubtype', 3) +// CHECK: ('filetype', 1) +// CHECK: ('num_load_commands', 1) +// CHECK: ('load_commands_size', 192) +// CHECK: ('flag', 0) +// CHECK: ('load_commands', [ +// CHECK: # Load Command 0 +// CHECK: (('command', 1) +// CHECK: ('size', 192) +// CHECK: ('segment_name', '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +// CHECK: ('vm_addr', 0) +// CHECK: ('vm_size', 20) +// CHECK: ('file_offset', 220) +// CHECK: ('file_size', 20) +// CHECK: ('maxprot', 7) +// CHECK: ('initprot', 7) +// CHECK: ('num_sections', 2) +// CHECK: ('flags', 0) +// CHECK: ('sections', [ +// CHECK: # Section 0 +// CHECK: (('section_name', '__text\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +// CHECK: ('segment_name', '__TEXT\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +// CHECK: ('address', 0) +// CHECK: ('size', 0) +// CHECK: ('offset', 220) +// CHECK: ('alignment', 0) +// CHECK: ('reloc_offset', 0) +// CHECK: ('num_reloc', 0) +// CHECK: ('flags', 0x80000000) +// CHECK: ('reserved1', 0) +// CHECK: ('reserved2', 0) +// CHECK: ), +// CHECK: # Section 1 +// CHECK: (('section_name', '__data\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +// CHECK: ('segment_name', '__DATA\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') +// CHECK: ('address', 0) +// CHECK: ('size', 20) +// CHECK: ('offset', 220) +// CHECK: ('alignment', 0) +// CHECK: ('reloc_offset', 0) +// CHECK: ('num_reloc', 0) +// CHECK: ('flags', 0x0) +// CHECK: ('reserved1', 0) +// CHECK: ('reserved2', 0) +// CHECK: ), +// CHECK: ]) +// CHECK: ), +// CHECK: ]) + +// FIXME: Dump contents, so we can check those too.