Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 1 | //===- yaml2obj - Convert YAML to a binary 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 | // This program takes a YAML description of an object file and outputs the |
| 11 | // binary equivalent. |
| 12 | // |
| 13 | // This is used for writing tests that require binary files. |
| 14 | // |
| 15 | //===----------------------------------------------------------------------===// |
| 16 | |
| 17 | #include "llvm/ADT/SmallString.h" |
| 18 | #include "llvm/ADT/StringExtras.h" |
| 19 | #include "llvm/ADT/StringMap.h" |
| 20 | #include "llvm/ADT/StringSwitch.h" |
Rafael Espindola | 2bbe378 | 2013-05-17 22:58:42 +0000 | [diff] [blame] | 21 | #include "llvm/Object/COFFYaml.h" |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 22 | #include "llvm/Support/Casting.h" |
| 23 | #include "llvm/Support/CommandLine.h" |
| 24 | #include "llvm/Support/Endian.h" |
| 25 | #include "llvm/Support/ManagedStatic.h" |
| 26 | #include "llvm/Support/MemoryBuffer.h" |
| 27 | #include "llvm/Support/PrettyStackTrace.h" |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 28 | #include "llvm/Support/Signals.h" |
| 29 | #include "llvm/Support/SourceMgr.h" |
Chandler Carruth | 4ffd89f | 2012-12-04 10:37:14 +0000 | [diff] [blame] | 30 | #include "llvm/Support/raw_ostream.h" |
| 31 | #include "llvm/Support/system_error.h" |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 32 | #include <vector> |
| 33 | |
| 34 | using namespace llvm; |
| 35 | |
| 36 | static cl::opt<std::string> |
| 37 | Input(cl::Positional, cl::desc("<input>"), cl::init("-")); |
| 38 | |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 39 | /// This parses a yaml stream that represents a COFF object file. |
| 40 | /// See docs/yaml2obj for the yaml scheema. |
| 41 | struct COFFParser { |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 42 | COFFParser(COFFYAML::Object &Obj) : Obj(Obj) { |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 43 | // A COFF string table always starts with a 4 byte size field. Offsets into |
| 44 | // it include this size, so allocate it now. |
| 45 | StringTable.append(4, 0); |
| 46 | } |
| 47 | |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 48 | bool parseSections() { |
| 49 | for (std::vector<COFFYAML::Section>::iterator i = Obj.Sections.begin(), |
| 50 | e = Obj.Sections.end(); i != e; ++i) { |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 51 | COFFYAML::Section &Sec = *i; |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 52 | |
| 53 | // If the name is less than 8 bytes, store it in place, otherwise |
| 54 | // store it in the string table. |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 55 | StringRef Name = Sec.Name; |
| 56 | |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 57 | if (Name.size() <= COFF::NameSize) { |
| 58 | std::copy(Name.begin(), Name.end(), Sec.Header.Name); |
| 59 | } else { |
| 60 | // Add string to the string table and format the index for output. |
| 61 | unsigned Index = getStringIndex(Name); |
| 62 | std::string str = utostr(Index); |
| 63 | if (str.size() > 7) { |
| 64 | errs() << "String table got too large"; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 65 | return false; |
| 66 | } |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 67 | Sec.Header.Name[0] = '/'; |
| 68 | std::copy(str.begin(), str.end(), Sec.Header.Name + 1); |
| 69 | } |
Rafael Espindola | 7098ae2 | 2013-05-06 20:11:21 +0000 | [diff] [blame] | 70 | |
| 71 | Sec.Header.Characteristics |= (Log2_32(Sec.Alignment) + 1) << 20; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 72 | } |
| 73 | return true; |
| 74 | } |
| 75 | |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 76 | bool parseSymbols() { |
| 77 | for (std::vector<COFFYAML::Symbol>::iterator i = Obj.Symbols.begin(), |
| 78 | e = Obj.Symbols.end(); i != e; ++i) { |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 79 | COFFYAML::Symbol &Sym = *i; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 80 | |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 81 | // If the name is less than 8 bytes, store it in place, otherwise |
| 82 | // store it in the string table. |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 83 | StringRef Name = Sym.Name; |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 84 | if (Name.size() <= COFF::NameSize) { |
| 85 | std::copy(Name.begin(), Name.end(), Sym.Header.Name); |
| 86 | } else { |
| 87 | // Add string to the string table and format the index for output. |
| 88 | unsigned Index = getStringIndex(Name); |
| 89 | *reinterpret_cast<support::aligned_ulittle32_t*>( |
| 90 | Sym.Header.Name + 4) = Index; |
| 91 | } |
| 92 | |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 93 | Sym.Header.Type = Sym.SimpleType; |
| 94 | Sym.Header.Type |= Sym.ComplexType << COFF::SCT_COMPLEX_TYPE_SHIFT; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 95 | } |
| 96 | return true; |
| 97 | } |
| 98 | |
| 99 | bool parse() { |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 100 | if (!parseSections()) |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 101 | return false; |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 102 | if (!parseSymbols()) |
| 103 | return false; |
| 104 | return true; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 105 | } |
| 106 | |
| 107 | unsigned getStringIndex(StringRef Str) { |
| 108 | StringMap<unsigned>::iterator i = StringTableMap.find(Str); |
| 109 | if (i == StringTableMap.end()) { |
| 110 | unsigned Index = StringTable.size(); |
| 111 | StringTable.append(Str.begin(), Str.end()); |
| 112 | StringTable.push_back(0); |
| 113 | StringTableMap[Str] = Index; |
| 114 | return Index; |
| 115 | } |
| 116 | return i->second; |
| 117 | } |
| 118 | |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 119 | COFFYAML::Object &Obj; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 120 | |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 121 | StringMap<unsigned> StringTableMap; |
| 122 | std::string StringTable; |
| 123 | }; |
| 124 | |
| 125 | // Take a CP and assign addresses and sizes to everything. Returns false if the |
| 126 | // layout is not valid to do. |
| 127 | static bool layoutCOFF(COFFParser &CP) { |
| 128 | uint32_t SectionTableStart = 0; |
| 129 | uint32_t SectionTableSize = 0; |
| 130 | |
| 131 | // The section table starts immediately after the header, including the |
| 132 | // optional header. |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 133 | SectionTableStart = sizeof(COFF::header) + CP.Obj.Header.SizeOfOptionalHeader; |
| 134 | SectionTableSize = sizeof(COFF::section) * CP.Obj.Sections.size(); |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 135 | |
| 136 | uint32_t CurrentSectionDataOffset = SectionTableStart + SectionTableSize; |
| 137 | |
| 138 | // Assign each section data address consecutively. |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 139 | for (std::vector<COFFYAML::Section>::iterator i = CP.Obj.Sections.begin(), |
| 140 | e = CP.Obj.Sections.end(); |
| 141 | i != e; ++i) { |
| 142 | if (!i->SectionData.empty()) { |
| 143 | i->Header.SizeOfRawData = i->SectionData.size()/2; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 144 | i->Header.PointerToRawData = CurrentSectionDataOffset; |
| 145 | CurrentSectionDataOffset += i->Header.SizeOfRawData; |
Rafael Espindola | 120cf57 | 2013-04-23 15:53:02 +0000 | [diff] [blame] | 146 | if (!i->Relocations.empty()) { |
| 147 | i->Header.PointerToRelocations = CurrentSectionDataOffset; |
| 148 | i->Header.NumberOfRelocations = i->Relocations.size(); |
| 149 | CurrentSectionDataOffset += i->Header.NumberOfRelocations * |
| 150 | COFF::RelocationSize; |
| 151 | } |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 152 | // TODO: Handle alignment. |
| 153 | } else { |
| 154 | i->Header.SizeOfRawData = 0; |
| 155 | i->Header.PointerToRawData = 0; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | uint32_t SymbolTableStart = CurrentSectionDataOffset; |
| 160 | |
| 161 | // Calculate number of symbols. |
| 162 | uint32_t NumberOfSymbols = 0; |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 163 | for (std::vector<COFFYAML::Symbol>::iterator i = CP.Obj.Symbols.begin(), |
| 164 | e = CP.Obj.Symbols.end(); |
| 165 | i != e; ++i) { |
Rafael Espindola | a0840c4 | 2013-04-23 19:39:34 +0000 | [diff] [blame] | 166 | unsigned AuxBytes = i->AuxiliaryData.size() / 2; |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 167 | if (AuxBytes % COFF::SymbolSize != 0) { |
Rafael Espindola | a0840c4 | 2013-04-23 19:39:34 +0000 | [diff] [blame] | 168 | errs() << "AuxiliaryData size not a multiple of symbol size!\n"; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 169 | return false; |
| 170 | } |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 171 | i->Header.NumberOfAuxSymbols = AuxBytes / COFF::SymbolSize; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 172 | NumberOfSymbols += 1 + i->Header.NumberOfAuxSymbols; |
| 173 | } |
| 174 | |
| 175 | // Store all the allocated start addresses in the header. |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 176 | CP.Obj.Header.NumberOfSections = CP.Obj.Sections.size(); |
| 177 | CP.Obj.Header.NumberOfSymbols = NumberOfSymbols; |
| 178 | CP.Obj.Header.PointerToSymbolTable = SymbolTableStart; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 179 | |
| 180 | *reinterpret_cast<support::ulittle32_t *>(&CP.StringTable[0]) |
| 181 | = CP.StringTable.size(); |
| 182 | |
| 183 | return true; |
| 184 | } |
| 185 | |
| 186 | template <typename value_type> |
| 187 | struct binary_le_impl { |
| 188 | value_type Value; |
| 189 | binary_le_impl(value_type V) : Value(V) {} |
| 190 | }; |
| 191 | |
| 192 | template <typename value_type> |
| 193 | raw_ostream &operator <<( raw_ostream &OS |
| 194 | , const binary_le_impl<value_type> &BLE) { |
| 195 | char Buffer[sizeof(BLE.Value)]; |
Michael J. Spencer | c8b18df | 2013-01-02 20:14:11 +0000 | [diff] [blame] | 196 | support::endian::write<value_type, support::little, support::unaligned>( |
| 197 | Buffer, BLE.Value); |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 198 | OS.write(Buffer, sizeof(BLE.Value)); |
| 199 | return OS; |
| 200 | } |
| 201 | |
| 202 | template <typename value_type> |
| 203 | binary_le_impl<value_type> binary_le(value_type V) { |
| 204 | return binary_le_impl<value_type>(V); |
| 205 | } |
| 206 | |
Rafael Espindola | 3d3cc32 | 2013-04-25 03:07:42 +0000 | [diff] [blame] | 207 | static bool writeHexData(StringRef Data, raw_ostream &OS) { |
| 208 | unsigned Size = Data.size(); |
| 209 | if (Size % 2) |
| 210 | return false; |
| 211 | |
| 212 | for (unsigned I = 0; I != Size; I += 2) { |
| 213 | uint8_t Byte; |
| 214 | if (Data.substr(I, 2).getAsInteger(16, Byte)) |
| 215 | return false; |
| 216 | OS.write(Byte); |
| 217 | } |
| 218 | |
| 219 | return true; |
| 220 | } |
| 221 | |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 222 | bool writeCOFF(COFFParser &CP, raw_ostream &OS) { |
| 223 | OS << binary_le(CP.Obj.Header.Machine) |
| 224 | << binary_le(CP.Obj.Header.NumberOfSections) |
| 225 | << binary_le(CP.Obj.Header.TimeDateStamp) |
| 226 | << binary_le(CP.Obj.Header.PointerToSymbolTable) |
| 227 | << binary_le(CP.Obj.Header.NumberOfSymbols) |
| 228 | << binary_le(CP.Obj.Header.SizeOfOptionalHeader) |
| 229 | << binary_le(CP.Obj.Header.Characteristics); |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 230 | |
| 231 | // Output section table. |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 232 | for (std::vector<COFFYAML::Section>::iterator i = CP.Obj.Sections.begin(), |
| 233 | e = CP.Obj.Sections.end(); |
| 234 | i != e; ++i) { |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 235 | OS.write(i->Header.Name, COFF::NameSize); |
| 236 | OS << binary_le(i->Header.VirtualSize) |
| 237 | << binary_le(i->Header.VirtualAddress) |
| 238 | << binary_le(i->Header.SizeOfRawData) |
| 239 | << binary_le(i->Header.PointerToRawData) |
| 240 | << binary_le(i->Header.PointerToRelocations) |
| 241 | << binary_le(i->Header.PointerToLineNumbers) |
| 242 | << binary_le(i->Header.NumberOfRelocations) |
| 243 | << binary_le(i->Header.NumberOfLineNumbers) |
| 244 | << binary_le(i->Header.Characteristics); |
| 245 | } |
| 246 | |
| 247 | // Output section data. |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 248 | for (std::vector<COFFYAML::Section>::iterator i = CP.Obj.Sections.begin(), |
| 249 | e = CP.Obj.Sections.end(); |
| 250 | i != e; ++i) { |
| 251 | if (!i->SectionData.empty()) { |
Rafael Espindola | 3d3cc32 | 2013-04-25 03:07:42 +0000 | [diff] [blame] | 252 | if (!writeHexData(i->SectionData, OS)) { |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 253 | errs() << "SectionData must be a collection of pairs of hex bytes"; |
| 254 | return false; |
| 255 | } |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 256 | } |
Rafael Espindola | 120cf57 | 2013-04-23 15:53:02 +0000 | [diff] [blame] | 257 | for (unsigned I2 = 0, E2 = i->Relocations.size(); I2 != E2; ++I2) { |
| 258 | const COFF::relocation &R = i->Relocations[I2]; |
| 259 | OS << binary_le(R.VirtualAddress) |
| 260 | << binary_le(R.SymbolTableIndex) |
| 261 | << binary_le(R.Type); |
| 262 | } |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 263 | } |
| 264 | |
| 265 | // Output symbol table. |
| 266 | |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 267 | for (std::vector<COFFYAML::Symbol>::const_iterator i = CP.Obj.Symbols.begin(), |
| 268 | e = CP.Obj.Symbols.end(); |
| 269 | i != e; ++i) { |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 270 | OS.write(i->Header.Name, COFF::NameSize); |
| 271 | OS << binary_le(i->Header.Value) |
| 272 | << binary_le(i->Header.SectionNumber) |
| 273 | << binary_le(i->Header.Type) |
| 274 | << binary_le(i->Header.StorageClass) |
| 275 | << binary_le(i->Header.NumberOfAuxSymbols); |
Rafael Espindola | a0840c4 | 2013-04-23 19:39:34 +0000 | [diff] [blame] | 276 | if (!i->AuxiliaryData.empty()) { |
Rafael Espindola | 3d3cc32 | 2013-04-25 03:07:42 +0000 | [diff] [blame] | 277 | if (!writeHexData(i->AuxiliaryData, OS)) { |
Rafael Espindola | a0840c4 | 2013-04-23 19:39:34 +0000 | [diff] [blame] | 278 | errs() << "AuxiliaryData must be a collection of pairs of hex bytes"; |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 279 | return false; |
| 280 | } |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 281 | } |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 282 | } |
| 283 | |
| 284 | // Output string table. |
| 285 | OS.write(&CP.StringTable[0], CP.StringTable.size()); |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 286 | return true; |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | int main(int argc, char **argv) { |
| 290 | cl::ParseCommandLineOptions(argc, argv); |
| 291 | sys::PrintStackTraceOnErrorSignal(); |
| 292 | PrettyStackTraceProgram X(argc, argv); |
| 293 | llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. |
| 294 | |
| 295 | OwningPtr<MemoryBuffer> Buf; |
| 296 | if (MemoryBuffer::getFileOrSTDIN(Input, Buf)) |
| 297 | return 1; |
| 298 | |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 299 | yaml::Input YIn(Buf->getBuffer()); |
| 300 | COFFYAML::Object Doc; |
| 301 | YIn >> Doc; |
| 302 | if (YIn.error()) { |
| 303 | errs() << "yaml2obj: Failed to parse YAML file!\n"; |
| 304 | return 1; |
| 305 | } |
| 306 | |
| 307 | COFFParser CP(Doc); |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 308 | if (!CP.parse()) { |
| 309 | errs() << "yaml2obj: Failed to parse YAML file!\n"; |
| 310 | return 1; |
| 311 | } |
Rafael Espindola | 8ec018c | 2013-04-02 23:56:40 +0000 | [diff] [blame] | 312 | |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 313 | if (!layoutCOFF(CP)) { |
| 314 | errs() << "yaml2obj: Failed to layout COFF file!\n"; |
| 315 | return 1; |
| 316 | } |
Rafael Espindola | c0f15f6 | 2013-04-23 19:26:43 +0000 | [diff] [blame] | 317 | if (!writeCOFF(CP, outs())) { |
| 318 | errs() << "yaml2obj: Failed to write COFF file!\n"; |
| 319 | return 1; |
| 320 | } |
Michael J. Spencer | a915f24 | 2012-08-02 19:16:56 +0000 | [diff] [blame] | 321 | } |