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 return SymbolName.size() > XCOFF::NameSize; 79 } 80 81 bool XCOFFWriter::initRelocations(uint64_t &CurrentOffset) { 82 for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { 83 if (!InitSections[I].Relocations.empty()) { 84 InitSections[I].NumberOfRelocations = InitSections[I].Relocations.size(); 85 InitSections[I].FileOffsetToRelocations = CurrentOffset; 86 CurrentOffset += InitSections[I].NumberOfRelocations * 87 XCOFF::RelocationSerializationSize32; 88 if (CurrentOffset > MaxRawDataSize) { 89 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 90 "exceeded when writing relocation data"); 91 return false; 92 } 93 } 94 } 95 return true; 96 } 97 98 bool XCOFFWriter::initSectionHeader(uint64_t &CurrentOffset) { 99 uint64_t CurrentSecAddr = 0; 100 for (uint16_t I = 0, E = InitSections.size(); I < E; ++I) { 101 if (CurrentOffset > MaxRawDataSize) { 102 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 103 "exceeded when writing section data"); 104 return false; 105 } 106 107 // Assign indices for sections. 108 if (InitSections[I].SectionName.size() && 109 !SectionIndexMap[InitSections[I].SectionName]) { 110 // The section index starts from 1. 111 SectionIndexMap[InitSections[I].SectionName] = I + 1; 112 if ((I + 1) > MaxSectionIndex) { 113 ErrHandler("exceeded the maximum permitted section index of " + 114 Twine(MaxSectionIndex)); 115 return false; 116 } 117 } 118 119 // Calculate the physical/virtual address. This field should contain 0 for 120 // all sections except the text, data and bss sections. 121 if (InitSections[I].Flags != XCOFF::STYP_TEXT && 122 InitSections[I].Flags != XCOFF::STYP_DATA && 123 InitSections[I].Flags != XCOFF::STYP_BSS) 124 InitSections[I].Address = 0; 125 else 126 InitSections[I].Address = CurrentSecAddr; 127 128 // Calculate the FileOffsetToData and data size for sections. 129 if (InitSections[I].SectionData.binary_size()) { 130 InitSections[I].FileOffsetToData = CurrentOffset; 131 CurrentOffset += InitSections[I].SectionData.binary_size(); 132 // Ensure the offset is aligned to DefaultSectionAlign. 133 CurrentOffset = alignTo(CurrentOffset, DefaultSectionAlign); 134 InitSections[I].Size = CurrentOffset - InitSections[I].FileOffsetToData; 135 CurrentSecAddr += InitSections[I].Size; 136 } 137 } 138 return initRelocations(CurrentOffset); 139 } 140 141 bool XCOFFWriter::initFileHeader(uint64_t CurrentOffset) { 142 // The default format of the object file is XCOFF32. 143 InitFileHdr.Magic = XCOFF::XCOFF32; 144 InitFileHdr.NumberOfSections = Obj.Sections.size(); 145 InitFileHdr.NumberOfSymTableEntries = Obj.Symbols.size(); 146 147 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { 148 // Add the number of auxiliary symbols to the total number. 149 InitFileHdr.NumberOfSymTableEntries += YamlSym.NumberOfAuxEntries; 150 if (nameShouldBeInStringTable(YamlSym.SymbolName)) 151 Strings.add(YamlSym.SymbolName); 152 } 153 // Finalize the string table. 154 Strings.finalize(); 155 156 // Calculate SymbolTableOffset for the file header. 157 if (InitFileHdr.NumberOfSymTableEntries) { 158 InitFileHdr.SymbolTableOffset = CurrentOffset; 159 CurrentOffset += 160 InitFileHdr.NumberOfSymTableEntries * XCOFF::SymbolTableEntrySize; 161 if (CurrentOffset > MaxRawDataSize) { 162 ErrHandler("maximum object size of" + Twine(MaxRawDataSize) + 163 "exceeded when writing symbols"); 164 return false; 165 } 166 } 167 // TODO: Calculate FileOffsetToLineNumbers when line number supported. 168 return true; 169 } 170 171 bool XCOFFWriter::assignAddressesAndIndices() { 172 Strings.clear(); 173 uint64_t CurrentOffset = 174 XCOFF::FileHeaderSize32 /* TODO: + auxiliaryHeaderSize() */ + 175 InitSections.size() * XCOFF::SectionHeaderSize32; 176 177 // Calculate section header info. 178 if (!initSectionHeader(CurrentOffset)) 179 return false; 180 // Calculate file header info. 181 return initFileHeader(CurrentOffset); 182 } 183 184 void XCOFFWriter::writeFileHeader() { 185 W.write<uint16_t>(Obj.Header.Magic ? Obj.Header.Magic : InitFileHdr.Magic); 186 W.write<uint16_t>(Obj.Header.NumberOfSections ? Obj.Header.NumberOfSections 187 : InitFileHdr.NumberOfSections); 188 W.write<int32_t>(Obj.Header.TimeStamp); 189 W.write<uint32_t>(Obj.Header.SymbolTableOffset 190 ? Obj.Header.SymbolTableOffset 191 : InitFileHdr.SymbolTableOffset); 192 W.write<int32_t>(Obj.Header.NumberOfSymTableEntries 193 ? Obj.Header.NumberOfSymTableEntries 194 : InitFileHdr.NumberOfSymTableEntries); 195 W.write<uint16_t>(Obj.Header.AuxHeaderSize); 196 W.write<uint16_t>(Obj.Header.Flags); 197 } 198 199 void XCOFFWriter::writeSectionHeader() { 200 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 201 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 202 XCOFFYAML::Section DerivedSec = InitSections[I]; 203 writeName(YamlSec.SectionName, W); 204 // Virtual address is the same as physical address. 205 uint32_t SectionAddress = 206 YamlSec.Address ? YamlSec.Address : DerivedSec.Address; 207 W.write<uint32_t>(SectionAddress); // Physical address 208 W.write<uint32_t>(SectionAddress); // Virtual address 209 W.write<uint32_t>(YamlSec.Size ? YamlSec.Size : DerivedSec.Size); 210 W.write<uint32_t>(YamlSec.FileOffsetToData ? YamlSec.FileOffsetToData 211 : DerivedSec.FileOffsetToData); 212 W.write<uint32_t>(YamlSec.FileOffsetToRelocations 213 ? YamlSec.FileOffsetToRelocations 214 : DerivedSec.FileOffsetToRelocations); 215 W.write<uint32_t>(YamlSec.FileOffsetToLineNumbers); 216 W.write<uint16_t>(YamlSec.NumberOfRelocations 217 ? YamlSec.NumberOfRelocations 218 : DerivedSec.NumberOfRelocations); 219 W.write<uint16_t>(YamlSec.NumberOfLineNumbers); 220 W.write<int32_t>(YamlSec.Flags); 221 } 222 } 223 224 bool XCOFFWriter::writeSectionData() { 225 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 226 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 227 if (YamlSec.SectionData.binary_size()) { 228 // Fill the padding size with zeros. 229 int64_t PaddingSize = 230 InitSections[I].FileOffsetToData - (W.OS.tell() - StartOffset); 231 if (PaddingSize < 0) { 232 ErrHandler("redundant data was written before section data"); 233 return false; 234 } 235 if (PaddingSize > 0) 236 W.OS.write_zeros(PaddingSize); 237 YamlSec.SectionData.writeAsBinary(W.OS); 238 } 239 } 240 return true; 241 } 242 243 bool XCOFFWriter::writeRelocations() { 244 for (uint16_t I = 0, E = Obj.Sections.size(); I < E; ++I) { 245 XCOFFYAML::Section YamlSec = Obj.Sections[I]; 246 if (!YamlSec.Relocations.empty()) { 247 int64_t PaddingSize = 248 InitSections[I].FileOffsetToRelocations - (W.OS.tell() - StartOffset); 249 if (PaddingSize < 0) { 250 ErrHandler("redundant data was written before relocations"); 251 return false; 252 } 253 if (PaddingSize > 0) 254 W.OS.write_zeros(PaddingSize); 255 for (const XCOFFYAML::Relocation &YamlRel : YamlSec.Relocations) { 256 W.write<uint32_t>(YamlRel.VirtualAddress); 257 W.write<uint32_t>(YamlRel.SymbolIndex); 258 W.write<uint8_t>(YamlRel.Info); 259 W.write<uint8_t>(YamlRel.Type); 260 } 261 } 262 } 263 return true; 264 } 265 266 bool XCOFFWriter::writeSymbols() { 267 int64_t PaddingSize = 268 (uint64_t)InitFileHdr.SymbolTableOffset - (W.OS.tell() - StartOffset); 269 if (PaddingSize < 0) { 270 ErrHandler("redundant data was written before symbols"); 271 return false; 272 } 273 if (PaddingSize > 0) 274 W.OS.write_zeros(PaddingSize); 275 for (const XCOFFYAML::Symbol &YamlSym : Obj.Symbols) { 276 if (nameShouldBeInStringTable(YamlSym.SymbolName)) { 277 // For XCOFF32: A value of 0 indicates that the symbol name is in the 278 // string table. 279 W.write<int32_t>(0); 280 W.write<uint32_t>(Strings.getOffset(YamlSym.SymbolName)); 281 } else { 282 writeName(YamlSym.SymbolName, W); 283 } 284 W.write<uint32_t>(YamlSym.Value); 285 W.write<int16_t>( 286 YamlSym.SectionName.size() ? SectionIndexMap[YamlSym.SectionName] : 0); 287 W.write<uint16_t>(YamlSym.Type); 288 W.write<uint8_t>(YamlSym.StorageClass); 289 W.write<uint8_t>(YamlSym.NumberOfAuxEntries); 290 291 // Now output the auxiliary entry. 292 for (uint8_t I = 0, E = YamlSym.NumberOfAuxEntries; I < E; ++I) { 293 // TODO: Auxiliary entry is not supported yet. 294 // The auxiliary entries for a symbol follow its symbol table entry. The 295 // length of each auxiliary entry is the same as a symbol table entry (18 296 // bytes). The format and quantity of auxiliary entries depend on the 297 // storage class (n_sclass) and type (n_type) of the symbol table entry. 298 W.OS.write_zeros(18); 299 } 300 } 301 return true; 302 } 303 304 bool XCOFFWriter::writeXCOFF() { 305 if (Is64Bit) { 306 ErrHandler("only XCOFF32 is currently supported"); 307 return false; 308 } 309 if (!assignAddressesAndIndices()) 310 return false; 311 StartOffset = W.OS.tell(); 312 writeFileHeader(); 313 if (!Obj.Sections.empty()) { 314 writeSectionHeader(); 315 if (!writeSectionData()) 316 return false; 317 if (!writeRelocations()) 318 return false; 319 } 320 if (!Obj.Symbols.empty() && !writeSymbols()) 321 return false; 322 // Write the string table. 323 if (Strings.getSize() > 4) 324 Strings.write(W.OS); 325 return true; 326 } 327 328 } // end anonymous namespace 329 330 namespace llvm { 331 namespace yaml { 332 333 bool yaml2xcoff(XCOFFYAML::Object &Doc, raw_ostream &Out, ErrorHandler EH) { 334 XCOFFWriter Writer(Doc, Out, EH); 335 return Writer.writeXCOFF(); 336 } 337 338 } // namespace yaml 339 } // namespace llvm 340