1 //===- yaml2xcoff - Convert YAML to a xcoff object file -------------------===// 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 /// \file 10 /// The xcoff component of yaml2obj. 11 /// 12 //===----------------------------------------------------------------------===// 13 14 #include "llvm/ADT/DenseMap.h" 15 #include "llvm/BinaryFormat/XCOFF.h" 16 #include "llvm/MC/StringTableBuilder.h" 17 #include "llvm/Object/XCOFFObjectFile.h" 18 #include "llvm/ObjectYAML/ObjectYAML.h" 19 #include "llvm/ObjectYAML/yaml2obj.h" 20 #include "llvm/Support/EndianStream.h" 21 #include "llvm/Support/LEB128.h" 22 #include "llvm/Support/MemoryBuffer.h" 23 #include "llvm/Support/raw_ostream.h" 24 25 using namespace llvm; 26 27 namespace { 28 29 constexpr unsigned DefaultSectionAlign = 4; 30 constexpr int16_t MaxSectionIndex = INT16_MAX; 31 constexpr uint32_t MaxRawDataSize = UINT32_MAX; 32 33 class XCOFFWriter { 34 public: 35 XCOFFWriter(XCOFFYAML::Object &Obj, raw_ostream &OS, yaml::ErrorHandler EH) 36 : Obj(Obj), W(OS, support::big), ErrHandler(EH), 37 StrTblBuilder(StringTableBuilder::XCOFF) { 38 Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64; 39 } 40 bool writeXCOFF(); 41 42 private: 43 bool nameShouldBeInStringTable(StringRef SymbolName); 44 bool initFileHeader(uint64_t CurrentOffset); 45 bool initSectionHeader(uint64_t &CurrentOffset); 46 bool initRelocations(uint64_t &CurrentOffset); 47 bool initStringTable(); 48 bool assignAddressesAndIndices(); 49 void writeFileHeader(); 50 void writeSectionHeader(); 51 bool writeSectionData(); 52 bool writeRelocations(); 53 bool writeSymbols(); 54 void writeStringTable(); 55 56 XCOFFYAML::Object &Obj; 57 bool Is64Bit = false; 58 support::endian::Writer W; 59 yaml::ErrorHandler ErrHandler; 60 StringTableBuilder StrTblBuilder; 61 uint64_t StartOffset; 62 // Map the section name to its corrresponding section index. 63 DenseMap<StringRef, int16_t> SectionIndexMap = { 64 {StringRef("N_DEBUG"), XCOFF::N_DEBUG}, 65 {StringRef("N_ABS"), XCOFF::N_ABS}, 66 {StringRef("N_UNDEF"), XCOFF::N_UNDEF}}; 67 XCOFFYAML::FileHeader InitFileHdr = Obj.Header; 68 std::vector<XCOFFYAML::Section> InitSections = Obj.Sections; 69 }; 70 71 static void writeName(StringRef StrName, support::endian::Writer W) { 72 char Name[XCOFF::NameSize]; 73 memset(Name, 0, XCOFF::NameSize); 74 char SrcName[] = ""; 75 memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size()); 76 ArrayRef<char> NameRef(Name, XCOFF::NameSize); 77 W.write(NameRef); 78 } 79 80 bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) { 81 // For XCOFF64: The symbol name is always in the string table. 82 return (SymbolName.size() > XCOFF::NameSize) || Is64Bit; 83 } 84 85 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) { 86 for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { 87 if (!InitSections[I].Relocations.empty()) { 88 InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size(); 89 InitSections[I].FileOffsetToRelocations = CurrentOffset; 90 uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64 91 : XCOFF::RelocationSerializationSize32; 92 CurrentOffset += InitSections[I].NumberOfRelocations * RelSize; 93 if (CurrentOffset > MaxRawDataSize) { 94 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 95 "exceeded when writing relocation data"); 96 return false; 97 } 98 } 99 } 100 return true; 101 } 102 103 bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) { 104 uint64_t CurrentSecAddr = 0; 105 for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { 106 if (CurrentOffset > MaxRawDataSize) { 107 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 108 "exceeded when writing section data"); 109 return false; 110 } 111 112 // Assign indices for sections. 113 if (InitSections[I].SectionName.size() && 114 !SectionIndexMap[InitSections[I].SectionName]) { 115 // The section index starts from 1. 116 SectionIndexMap[InitSections[I].SectionName] = I + 1; 117 if ((I + 1) > MaxSectionIndex) { 118 ErrHandler("exceeded the maximum permitted section index of " + 119 Twine(MaxSectionIndex)); 120 return false; 121 } 122 } 123 124 // Calculate the physical/virtual address. This field should contain 0 for 125 // all sections except the text, data and bss sections. 126 if (InitSections[I].Flags != XCOFF::STYP_TEXT && 127 InitSections[I].Flags != XCOFF::STYP_DATA && 128 InitSections[I].Flags != XCOFF::STYP_BSS) 129 InitSections[I].Address = 0; 130 else 131 InitSections[I].Address = CurrentSecAddr; 132 133 // Calculate the FileOffsetToData and data size for sections. 134 if (InitSections[I].SectionData.binary_size()) { 135 InitSections[I].FileOffsetToData = CurrentOffset; 136 CurrentOffset += InitSections[I].SectionData.binary_size(); 137 // Ensure the offset is aligned to DefaultSectionAlign. 138 CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign); 139 InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData; 140 CurrentSecAddr += InitSections[I].Size; 141 } 142 } 143 return initRelocations(CurrentOffset); 144 } 145 146 bool XCOFFWriter::initStringTable() { 147 if (Obj.StrTbl.RawContent) { 148 size_t RawSize = Obj.StrTbl.RawContent->binary_size(); 149 if (Obj.StrTbl.Strings || Obj.StrTbl.Length) { 150 ErrHandler( 151 "can't specify Strings or Length when RawContent is specified"); 152 return false; 153 } 154 if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < RawSize) { 155 ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) + 156 ") is less than the RawContent data size (" + Twine(RawSize) + 157 ")"); 158 return false; 159 } 160 return true; 161 } 162 if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize <= 3) { 163 ErrHandler("ContentSize shouldn't be less than 4 without RawContent"); 164 return false; 165 } 166 167 // Build the string table. 168 StrTblBuilder.clear(); 169 170 if (Obj.StrTbl.Strings) { 171 // All specified strings should be added to the string table. 172 for (StringRef StringEnt : *Obj.StrTbl.Strings) 173 StrTblBuilder.add(StringEnt); 174 175 size_t StrTblIdx = 0; 176 size_t NumOfStrings = Obj.StrTbl.Strings->size(); 177 for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { 178 if (nameShouldBeInStringTable(YamlSym.SymbolName)) { 179 if (StrTblIdx < NumOfStrings) { 180 // Overwrite the symbol name with the specified string. 181 YamlSym.SymbolName = (*Obj.StrTbl.Strings)[StrTblIdx]; 182 ++StrTblIdx; 183 } else 184 // Names that are not overwritten are still stored in the string 185 // table. 186 StrTblBuilder.add(YamlSym.SymbolName); 187 } 188 } 189 } else { 190 for (XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { 191 if (nameShouldBeInStringTable(YamlSym.SymbolName)) 192 StrTblBuilder.add(YamlSym.SymbolName); 193 } 194 } 195 196 StrTblBuilder.finalize(); 197 198 size_t StrTblSize = StrTblBuilder.getSize(); 199 if (Obj.StrTbl.ContentSize && *Obj.StrTbl.ContentSize < StrTblSize) { 200 ErrHandler("specified ContentSize (" + Twine(*Obj.StrTbl.ContentSize) + 201 ") is less than the size of the data that would otherwise be " 202 "written (" + 203 Twine(StrTblSize) + ")"); 204 return false; 205 } 206 207 return true; 208 } 209 210 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) { 211 // The default format of the object file is XCOFF32. 212 InitFileHdr.Magic = XCOFF::XCOFF32; 213 InitFileHdr.NumberOfSections = Obj.Sections.size(); 214 InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size(); 215 216 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) 217 // Add the number of auxiliary symbols to the total number. 218 InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries; 219 220 // Calculate SymbolTableOffset for the file header. 221 if (InitFileHdr.NumberOfSymTableEntries) { 222 InitFileHdr.SymbolTableOffset = CurrentOffset; 223 CurrentOffset += 224 InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize; 225 if (CurrentOffset > MaxRawDataSize) { 226 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 227 "exceeded when writing symbols"); 228 return false; 229 } 230 } 231 // TODO: Calculate FileOffsetToLineNumbers when line number supported. 232 return true; 233 } 234 235 bool XCOFFWriter::assignAddressesAndIndices() { 236 uint64_t FileHdrSize = 237 Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32; 238 uint64_t SecHdrSize = 239 Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32; 240 uint64_t CurrentOffset = FileHdrSize /* TODO: + auxiliaryHeaderSize() */ + 241 InitSections.size() * SecHdrSize; 242 243 // Calculate section header info. 244 if (!initSectionHeader(CurrentOffset)) 245 return false; 246 247 // Calculate file header info. 248 if (!initFileHeader(CurrentOffset)) 249 return false; 250 251 // Initialize the string table. 252 return initStringTable(); 253 } 254 255 void XCOFFWriter::writeFileHeader() { 256 W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic); 257 W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections 258 : InitFileHdr.NumberOfSections); 259 W.write<int32_t>(Obj.Header.TimeStamp); 260 if (Is64Bit) { 261 W.write<uint64_t>(Obj.Header.SymbolTableOffset 262 ? Obj.Header.SymbolTableOffset 263 : InitFileHdr.SymbolTableOffset); 264 W.write<uint16_t>(Obj.Header.AuxHeaderSize); 265 W.write<uint16_t>(Obj.Header.Flags); 266 W.write<int32_t>(Obj.Header.NumberOfSymTableEntries 267 ? Obj.Header.NumberOfSymTableEntries 268 : InitFileHdr.NumberOfSymTableEntries); 269 } else { 270 W.write<uint32_t>(Obj.Header.SymbolTableOffset 271 ? Obj.Header.SymbolTableOffset 272 : InitFileHdr.SymbolTableOffset); 273 W.write<int32_t>(Obj.Header.NumberOfSymTableEntries 274 ? Obj.Header.NumberOfSymTableEntries 275 : InitFileHdr.NumberOfSymTableEntries); 276 W.write<uint16_t>(Obj.Header.AuxHeaderSize); 277 W.write<uint16_t>(Obj.Header.Flags); 278 } 279 } 280 281 void XCOFFWriter::writeSectionHeader() { 282 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 283 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 284 XCOFFYAML::Section DerivedSec = InitSections[I]; 285 writeName(YamlSec.SectionName, W); 286 // Virtual address is the same as physical address. 287 uint64_t SectionAddress = 288 YamlSec.Address ? YamlSec.Address : DerivedSec.Address; 289 if (Is64Bit) { 290 W.write<uint64_t>(SectionAddress); // Physical address 291 W.write<uint64_t>(SectionAddress); // Virtual address 292 W.write<uint64_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size); 293 W.write<uint64_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData 294 : DerivedSec.FileOffsetToData); 295 W.write<uint64_t>(YamlSec.FileOffsetToRelocations 296 ? YamlSec.FileOffsetToRelocations 297 : DerivedSec.FileOffsetToRelocations); 298 W.write<uint64_t>(YamlSec.FileOffsetToLineNumbers); 299 W.write<uint32_t>(YamlSec.NumberOfRelocations 300 ? YamlSec.NumberOfRelocations 301 : DerivedSec.NumberOfRelocations); 302 W.write<uint32_t>(YamlSec.NumberOfLineNumbers); 303 W.write<int32_t>(YamlSec.Flags); 304 W.OS.write_zeros(4); 305 } else { 306 W.write<uint32_t>(SectionAddress); // Physical address 307 W.write<uint32_t>(SectionAddress); // Virtual address 308 W.write<uint32_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size); 309 W.write<uint32_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData 310 : DerivedSec.FileOffsetToData); 311 W.write<uint32_t>(YamlSec.FileOffsetToRelocations 312 ? YamlSec.FileOffsetToRelocations 313 : DerivedSec.FileOffsetToRelocations); 314 W.write<uint32_t>(YamlSec.FileOffsetToLineNumbers); 315 W.write<uint16_t>(YamlSec.NumberOfRelocations 316 ? YamlSec.NumberOfRelocations 317 : DerivedSec.NumberOfRelocations); 318 W.write<uint16_t>(YamlSec.NumberOfLineNumbers); 319 W.write<int32_t>(YamlSec.Flags); 320 } 321 } 322 } 323 324 bool XCOFFWriter::writeSectionData() { 325 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 326 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 327 if (YamlSec.SectionData.binary_size()) { 328 // Fill the padding size with zeros. 329 int64_t PaddingSize = 330 InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset); 331 if (PaddingSize < 0) { 332 ErrHandler("redundant data was written before section data"); 333 return false; 334 } 335 W.OS.write_zeros(PaddingSize); 336 YamlSec.SectionData.writeAsBinary(W.OS); 337 } 338 } 339 return true; 340 } 341 342 bool XCOFFWriter::writeRelocations() { 343 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 344 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 345 if (!YamlSec.Relocations.empty()) { 346 int64_t PaddingSize = 347 InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset); 348 if (PaddingSize < 0) { 349 ErrHandler("redundant data was written before relocations"); 350 return false; 351 } 352 W.OS.write_zeros(PaddingSize); 353 for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) { 354 if (Is64Bit) 355 W.write<uint64_t>(YamlRel.VirtualAddress); 356 else 357 W.write<uint32_t>(YamlRel.VirtualAddress); 358 W.write<uint32_t>(YamlRel.SymbolIndex); 359 W.write<uint8_t>(YamlRel.Info); 360 W.write<uint8_t>(YamlRel.Type); 361 } 362 } 363 } 364 return true; 365 } 366 367 bool XCOFFWriter::writeSymbols() { 368 int64_t PaddingSize = 369 (uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset); 370 if (PaddingSize < 0) { 371 ErrHandler("redundant data was written before symbols"); 372 return false; 373 } 374 W.OS.write_zeros(PaddingSize); 375 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { 376 if (Is64Bit) { 377 W.write<uint64_t>(YamlSym.Value); 378 W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName)); 379 } else { 380 if (nameShouldBeInStringTable(YamlSym.SymbolName)) { 381 // For XCOFF32: A value of 0 indicates that the symbol name is in the 382 // string table. 383 W.write<int32_t>(0); 384 W.write<uint32_t>(StrTblBuilder.getOffset(YamlSym.SymbolName)); 385 } else { 386 writeName(YamlSym.SymbolName, W); 387 } 388 W.write<uint32_t>(YamlSym.Value); 389 } 390 W.write<int16_t>( 391 YamlSym.SectionName.size() ? SectionIndexMap[YamlSym.SectionName] : 0); 392 W.write<uint16_t>(YamlSym.Type); 393 W.write<uint8_t>(YamlSym.StorageClass); 394 W.write<uint8_t>(YamlSym.NumberOfAuxEntries); 395 396 // Now output the auxiliary entry. 397 for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) { 398 // TODO: Auxiliary entry is not supported yet. 399 // The auxiliary entries for a symbol follow its symbol table entry. The 400 // length of each auxiliary entry is the same as a symbol table entry (18 401 // bytes). The format and quantity of auxiliary entries depend on the 402 // storage class (n_sclass) and type (n_type) of the symbol table entry. 403 W.OS.write_zeros(XCOFF::SymbolTableEntrySize); 404 } 405 } 406 return true; 407 } 408 409 void XCOFFWriter::writeStringTable() { 410 if (Obj.StrTbl.RawContent) { 411 Obj.StrTbl.RawContent->writeAsBinary(W.OS); 412 if (Obj.StrTbl.ContentSize) { 413 assert(*Obj.StrTbl.ContentSize >= Obj.StrTbl.RawContent->binary_size() && 414 "Specified ContentSize is less than the RawContent size."); 415 W.OS.write_zeros(*Obj.StrTbl.ContentSize - 416 Obj.StrTbl.RawContent->binary_size()); 417 } 418 return; 419 } 420 421 size_t StrTblBuilderSize = StrTblBuilder.getSize(); 422 // If neither Length nor ContentSize is specified, write the StrTblBuilder 423 // directly, which contains the auto-generated Length value. 424 if (!Obj.StrTbl.Length && !Obj.StrTbl.ContentSize) { 425 if (StrTblBuilderSize <= 4) 426 return; 427 StrTblBuilder.write(W.OS); 428 return; 429 } 430 431 // Serialize the string table's content to a temporary buffer. 432 std::unique_ptr<WritableMemoryBuffer> Buf = 433 WritableMemoryBuffer::getNewMemBuffer(StrTblBuilderSize); 434 uint8_t *Ptr = reinterpret_cast<uint8_t *>(Buf->getBufferStart()); 435 StrTblBuilder.write(Ptr); 436 // Replace the first 4 bytes, which contain the auto-generated Length value, 437 // with the specified value. 438 memset(Ptr, 0, 4); 439 support::endian::write32be(Ptr, Obj.StrTbl.Length ? *Obj.StrTbl.Length 440 : *Obj.StrTbl.ContentSize); 441 // Copy the buffer content to the actual output stream. 442 W.OS.write(Buf->getBufferStart(), Buf->getBufferSize()); 443 // Add zeros as padding after strings. 444 if (Obj.StrTbl.ContentSize) { 445 assert(*Obj.StrTbl.ContentSize >= StrTblBuilderSize && 446 "Specified ContentSize is less than the StringTableBuilder size."); 447 W.OS.write_zeros(*Obj.StrTbl.ContentSize - StrTblBuilderSize); 448 } 449 } 450 451 bool XCOFFWriter::writeXCOFF() { 452 if (!assignAddressesAndIndices()) 453 return false; 454 StartOffset = W.OS.tell(); 455 writeFileHeader(); 456 if (!Obj.Sections.empty()) { 457 writeSectionHeader(); 458 if (!writeSectionData()) 459 return false; 460 if (!writeRelocations()) 461 return false; 462 } 463 if (!Obj.Symbols.empty() && !writeSymbols()) 464 return false; 465 writeStringTable(); 466 return true; 467 } 468 469 } // end anonymous namespace 470 471 namespace llvm { 472 namespace yaml { 473 474 bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) { 475 XCOFFWriter Writer(Doc, Out, EH); 476 return Writer.writeXCOFF(); 477 } 478 479 } // namespace yaml 480 } // namespace llvm 481