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/raw_ostream.h" 22 #include "llvm/Support/LEB128.h" 23 24 using namespace llvm; 25 26 namespace { 27 28 constexpr unsigned DefaultSectionAlign = 4; 29 constexpr int16_t MaxSectionIndex = INT16_MAX; 30 constexpr uint32_t MaxRawDataSize = UINT32_MAX; 31 32 class XCOFFWriter { 33 public: 34 XCOFFWriter(XCOFFYAML::Object &Obj, raw_ostream &OS, yaml::ErrorHandler EH) 35 : Obj(Obj), W(OS, support::big), ErrHandler(EH), 36 Strings(StringTableBuilder::XCOFF) { 37 Is64Bit = Obj.Header.Magic == (llvm::yaml::Hex16)XCOFF::XCOFF64; 38 } 39 bool writeXCOFF(); 40 41 private: 42 bool nameShouldBeInStringTable(StringRef SymbolName); 43 bool initFileHeader(uint64_t CurrentOffset); 44 bool initSectionHeader(uint64_t &CurrentOffset); 45 bool initRelocations(uint64_t &CurrentOffset); 46 bool assignAddressesAndIndices(); 47 void writeFileHeader(); 48 void writeSectionHeader(); 49 bool writeSectionData(); 50 bool writeRelocations(); 51 bool writeSymbols(); 52 53 XCOFFYAML::Object &Obj; 54 bool Is64Bit = false; 55 support::endian::Writer W; 56 yaml::ErrorHandler ErrHandler; 57 StringTableBuilder Strings; 58 uint64_t StartOffset; 59 // Map the section name to its corrresponding section index. 60 DenseMap<StringRef, int16_t> SectionIndexMap = { 61 {StringRef("N_DEBUG"), XCOFF::N_DEBUG}, 62 {StringRef("N_ABS"), XCOFF::N_ABS}, 63 {StringRef("N_UNDEF"), XCOFF::N_UNDEF}}; 64 XCOFFYAML::FileHeader InitFileHdr = Obj.Header; 65 std::vector<XCOFFYAML::Section> InitSections = Obj.Sections; 66 }; 67 68 static void writeName(StringRef StrName, support::endian::Writer W) { 69 char Name[XCOFF::NameSize]; 70 memset(Name, 0, XCOFF::NameSize); 71 char SrcName[] = ""; 72 memcpy(Name, StrName.size() ? StrName.data() : SrcName, StrName.size()); 73 ArrayRef<char> NameRef(Name, XCOFF::NameSize); 74 W.write(NameRef); 75 } 76 77 bool XCOFFWriter::nameShouldBeInStringTable(StringRef SymbolName) { 78 // For XCOFF64: The symbol name is always in the string table. 79 return (SymbolName.size() > XCOFF::NameSize) || Is64Bit; 80 } 81 82 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) { 83 for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { 84 if (!InitSections[I].Relocations.empty()) { 85 InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size(); 86 InitSections[I].FileOffsetToRelocations = CurrentOffset; 87 uint64_t RelSize = Is64Bit ? XCOFF::RelocationSerializationSize64 88 : XCOFF::RelocationSerializationSize32; 89 CurrentOffset += InitSections[I].NumberOfRelocations * RelSize; 90 if (CurrentOffset > MaxRawDataSize) { 91 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 92 "exceeded when writing relocation data"); 93 return false; 94 } 95 } 96 } 97 return true; 98 } 99 100 bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) { 101 uint64_t CurrentSecAddr = 0; 102 for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { 103 if (CurrentOffset > MaxRawDataSize) { 104 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 105 "exceeded when writing section data"); 106 return false; 107 } 108 109 // Assign indices for sections. 110 if (InitSections[I].SectionName.size() && 111 !SectionIndexMap[InitSections[I].SectionName]) { 112 // The section index starts from 1. 113 SectionIndexMap[InitSections[I].SectionName] = I + 1; 114 if ((I + 1) > MaxSectionIndex) { 115 ErrHandler("exceeded the maximum permitted section index of " + 116 Twine(MaxSectionIndex)); 117 return false; 118 } 119 } 120 121 // Calculate the physical/virtual address. This field should contain 0 for 122 // all sections except the text, data and bss sections. 123 if (InitSections[I].Flags != XCOFF::STYP_TEXT && 124 InitSections[I].Flags != XCOFF::STYP_DATA && 125 InitSections[I].Flags != XCOFF::STYP_BSS) 126 InitSections[I].Address = 0; 127 else 128 InitSections[I].Address = CurrentSecAddr; 129 130 // Calculate the FileOffsetToData and data size for sections. 131 if (InitSections[I].SectionData.binary_size()) { 132 InitSections[I].FileOffsetToData = CurrentOffset; 133 CurrentOffset += InitSections[I].SectionData.binary_size(); 134 // Ensure the offset is aligned to DefaultSectionAlign. 135 CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign); 136 InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData; 137 CurrentSecAddr += InitSections[I].Size; 138 } 139 } 140 return initRelocations(CurrentOffset); 141 } 142 143 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) { 144 // The default format of the object file is XCOFF32. 145 InitFileHdr.Magic = XCOFF::XCOFF32; 146 InitFileHdr.NumberOfSections = Obj.Sections.size(); 147 InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size(); 148 149 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { 150 // Add the number of auxiliary symbols to the total number. 151 InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries; 152 if (nameShouldBeInStringTable(YamlSym.SymbolName)) 153 Strings.add(YamlSym.SymbolName); 154 } 155 // Finalize the string table. 156 Strings.finalize(); 157 158 // Calculate SymbolTableOffset for the file header. 159 if (InitFileHdr.NumberOfSymTableEntries) { 160 InitFileHdr.SymbolTableOffset = CurrentOffset; 161 CurrentOffset += 162 InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize; 163 if (CurrentOffset > MaxRawDataSize) { 164 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 165 "exceeded when writing symbols"); 166 return false; 167 } 168 } 169 // TODO: Calculate FileOffsetToLineNumbers when line number supported. 170 return true; 171 } 172 173 bool XCOFFWriter::assignAddressesAndIndices() { 174 Strings.clear(); 175 uint64_t FileHdrSize = 176 Is64Bit ? XCOFF::FileHeaderSize64 : XCOFF::FileHeaderSize32; 177 uint64_t SecHdrSize = 178 Is64Bit ? XCOFF::SectionHeaderSize64 : XCOFF::SectionHeaderSize32; 179 uint64_t CurrentOffset = FileHdrSize /* TODO: + auxiliaryHeaderSize() */ + 180 InitSections.size() * SecHdrSize; 181 182 // Calculate section header info. 183 if (!initSectionHeader(CurrentOffset)) 184 return false; 185 // Calculate file header info. 186 return initFileHeader(CurrentOffset); 187 } 188 189 void XCOFFWriter::writeFileHeader() { 190 W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic); 191 W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections 192 : InitFileHdr.NumberOfSections); 193 W.write<int32_t>(Obj.Header.TimeStamp); 194 if (Is64Bit) { 195 W.write<uint64_t>(Obj.Header.SymbolTableOffset 196 ? Obj.Header.SymbolTableOffset 197 : InitFileHdr.SymbolTableOffset); 198 W.write<uint16_t>(Obj.Header.AuxHeaderSize); 199 W.write<uint16_t>(Obj.Header.Flags); 200 W.write<int32_t>(Obj.Header.NumberOfSymTableEntries 201 ? Obj.Header.NumberOfSymTableEntries 202 : InitFileHdr.NumberOfSymTableEntries); 203 } else { 204 W.write<uint32_t>(Obj.Header.SymbolTableOffset 205 ? Obj.Header.SymbolTableOffset 206 : InitFileHdr.SymbolTableOffset); 207 W.write<int32_t>(Obj.Header.NumberOfSymTableEntries 208 ? Obj.Header.NumberOfSymTableEntries 209 : InitFileHdr.NumberOfSymTableEntries); 210 W.write<uint16_t>(Obj.Header.AuxHeaderSize); 211 W.write<uint16_t>(Obj.Header.Flags); 212 } 213 } 214 215 void XCOFFWriter::writeSectionHeader() { 216 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 217 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 218 XCOFFYAML::Section DerivedSec = InitSections[I]; 219 writeName(YamlSec.SectionName, W); 220 // Virtual address is the same as physical address. 221 uint64_t SectionAddress = 222 YamlSec.Address ? YamlSec.Address : DerivedSec.Address; 223 if (Is64Bit) { 224 W.write<uint64_t>(SectionAddress); // Physical address 225 W.write<uint64_t>(SectionAddress); // Virtual address 226 W.write<uint64_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size); 227 W.write<uint64_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData 228 : DerivedSec.FileOffsetToData); 229 W.write<uint64_t>(YamlSec.FileOffsetToRelocations 230 ? YamlSec.FileOffsetToRelocations 231 : DerivedSec.FileOffsetToRelocations); 232 W.write<uint64_t>(YamlSec.FileOffsetToLineNumbers); 233 W.write<uint32_t>(YamlSec.NumberOfRelocations 234 ? YamlSec.NumberOfRelocations 235 : DerivedSec.NumberOfRelocations); 236 W.write<uint32_t>(YamlSec.NumberOfLineNumbers); 237 W.write<int32_t>(YamlSec.Flags); 238 W.OS.write_zeros(4); 239 } else { 240 W.write<uint32_t>(SectionAddress); // Physical address 241 W.write<uint32_t>(SectionAddress); // Virtual address 242 W.write<uint32_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size); 243 W.write<uint32_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData 244 : DerivedSec.FileOffsetToData); 245 W.write<uint32_t>(YamlSec.FileOffsetToRelocations 246 ? YamlSec.FileOffsetToRelocations 247 : DerivedSec.FileOffsetToRelocations); 248 W.write<uint32_t>(YamlSec.FileOffsetToLineNumbers); 249 W.write<uint16_t>(YamlSec.NumberOfRelocations 250 ? YamlSec.NumberOfRelocations 251 : DerivedSec.NumberOfRelocations); 252 W.write<uint16_t>(YamlSec.NumberOfLineNumbers); 253 W.write<int32_t>(YamlSec.Flags); 254 } 255 } 256 } 257 258 bool XCOFFWriter::writeSectionData() { 259 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 260 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 261 if (YamlSec.SectionData.binary_size()) { 262 // Fill the padding size with zeros. 263 int64_t PaddingSize = 264 InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset); 265 if (PaddingSize < 0) { 266 ErrHandler("redundant data was written before section data"); 267 return false; 268 } 269 W.OS.write_zeros(PaddingSize); 270 YamlSec.SectionData.writeAsBinary(W.OS); 271 } 272 } 273 return true; 274 } 275 276 bool XCOFFWriter::writeRelocations() { 277 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 278 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 279 if (!YamlSec.Relocations.empty()) { 280 int64_t PaddingSize = 281 InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset); 282 if (PaddingSize < 0) { 283 ErrHandler("redundant data was written before relocations"); 284 return false; 285 } 286 W.OS.write_zeros(PaddingSize); 287 for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) { 288 if (Is64Bit) 289 W.write<uint64_t>(YamlRel.VirtualAddress); 290 else 291 W.write<uint32_t>(YamlRel.VirtualAddress); 292 W.write<uint32_t>(YamlRel.SymbolIndex); 293 W.write<uint8_t>(YamlRel.Info); 294 W.write<uint8_t>(YamlRel.Type); 295 } 296 } 297 } 298 return true; 299 } 300 301 bool XCOFFWriter::writeSymbols() { 302 int64_t PaddingSize = 303 (uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset); 304 if (PaddingSize < 0) { 305 ErrHandler("redundant data was written before symbols"); 306 return false; 307 } 308 W.OS.write_zeros(PaddingSize); 309 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { 310 if (Is64Bit) { 311 W.write<uint64_t>(YamlSym.Value); 312 W.write<uint32_t>(Strings.getOffset(YamlSym.SymbolName)); 313 } else { 314 if (nameShouldBeInStringTable(YamlSym.SymbolName)) { 315 // For XCOFF32: A value of 0 indicates that the symbol name is in the 316 // string table. 317 W.write<int32_t>(0); 318 W.write<uint32_t>(Strings.getOffset(YamlSym.SymbolName)); 319 } else { 320 writeName(YamlSym.SymbolName, W); 321 } 322 W.write<uint32_t>(YamlSym.Value); 323 } 324 W.write<int16_t>( 325 YamlSym.SectionName.size() ? SectionIndexMap[YamlSym.SectionName] : 0); 326 W.write<uint16_t>(YamlSym.Type); 327 W.write<uint8_t>(YamlSym.StorageClass); 328 W.write<uint8_t>(YamlSym.NumberOfAuxEntries); 329 330 // Now output the auxiliary entry. 331 for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) { 332 // TODO: Auxiliary entry is not supported yet. 333 // The auxiliary entries for a symbol follow its symbol table entry. The 334 // length of each auxiliary entry is the same as a symbol table entry (18 335 // bytes). The format and quantity of auxiliary entries depend on the 336 // storage class (n_sclass) and type (n_type) of the symbol table entry. 337 W.OS.write_zeros(XCOFF::SymbolTableEntrySize); 338 } 339 } 340 return true; 341 } 342 343 bool XCOFFWriter::writeXCOFF() { 344 if (!assignAddressesAndIndices()) 345 return false; 346 StartOffset = W.OS.tell(); 347 writeFileHeader(); 348 if (!Obj.Sections.empty()) { 349 writeSectionHeader(); 350 if (!writeSectionData()) 351 return false; 352 if (!writeRelocations()) 353 return false; 354 } 355 if (!Obj.Symbols.empty() && !writeSymbols()) 356 return false; 357 // Write the string table. 358 if (Strings.getSize() > 4) 359 Strings.write(W.OS); 360 return true; 361 } 362 363 } // end anonymous namespace 364 365 namespace llvm { 366 namespace yaml { 367 368 bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) { 369 XCOFFWriter Writer(Doc, Out, EH); 370 return Writer.writeXCOFF(); 371 } 372 373 } // namespace yaml 374 } // namespace llvm 375