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