public static void CreateObjectCode(Stream toStream, IArchitecture architecture, List <IRInstruction> ir, Dictionary <string, StringConstant> stringConstants, Dictionary <string, Variable> globalVariables, Dictionary <string, Function> functions) { var header = new ObjectCodeHeader(); header.RelocationAddresses = new List <int>(); header.LabelAddresses = new List <LabelAddressTableEntry>(); var code = new List <byte>(); //name => labelIndex header.ExportedSymbols = new Dictionary <string, int>(); #region Functions if (functions != null) { foreach (var function in functions) { if (function.Value.IsExported) { header.ExportedSymbols.Add(function.Key, function.Value.Address.Value); } else if (function.Value.IsExtern) { header.LabelAddresses.Add( new LabelAddressTableEntry() { Index = function.Value.Address.Value, IsExtern = true, SymbolName = function.Key } ); } else { if (function.Key.Equals("main", StringComparison.CurrentCultureIgnoreCase)) { header.HasEntryPoint = true; header.EntryPointFunctionLabel = function.Value.Address.Value; } } } } #endregion int codeAddress = 0; foreach (var i in ir) { if (i is IRComment) { continue; } if (i is IRLabel) { header.LabelAddresses.Add( new LabelAddressTableEntry() { Index = ((IRLabel)i).Index, Address = codeAddress } ); } if (i is IRelocatableAddressValue && ((IRelocatableAddressValue)i).HasRelocatableAddressValue()) { int relocOffset = architecture.GetRelocationOffset(i); header.RelocationAddresses.Add(codeAddress + relocOffset); } byte[] machineInstruction = i.GetImplementation(architecture); code.AddRange(machineInstruction.ToList()); codeAddress += machineInstruction.Length; } int offset = codeAddress; int initializedDataSize = 0; #region String Constants if (stringConstants != null) { foreach (var str in stringConstants) { header.LabelAddresses.Add( new LabelAddressTableEntry() { Index = str.Value.LabelAddress, Address = offset + initializedDataSize } ); initializedDataSize += str.Value.Value.Length + 1; } } #endregion offset += initializedDataSize; int uninitializedDataSize = 0; #region Global Variables if (globalVariables != null) { foreach (var variable in globalVariables) { if (variable.Value.IsExported) { header.ExportedSymbols.Add(variable.Key, variable.Value.Address.Value); } if (variable.Value.IsExtern) { header.LabelAddresses.Add( new LabelAddressTableEntry() { Index = variable.Value.Address.Value, IsExtern = true, SymbolName = variable.Key } ); } else { header.LabelAddresses.Add( new LabelAddressTableEntry() { Index = variable.Value.Address.Value, IsExtern = false, Address = offset + uninitializedDataSize } ); uninitializedDataSize += variable.Value.Type.GetSize(); } } } #endregion offset += uninitializedDataSize; header.SizeOfDataAndCode = offset; using (var sw = new BinaryWriter(toStream)) { ObjectCodeUtils.WriteObjectFileHeader(header, sw); //Code section foreach (byte b in code) { sw.Write(b); } //Initialized data if (stringConstants != null) { foreach (var str in stringConstants) { foreach (var ch in str.Value.Value.ToCharArray()) { sw.Write((byte)ch); } sw.Write((byte)0); //0 terminated strings } } //Uninitialized data for (int i = 0; i < uninitializedDataSize; i++) { sw.Write((byte)0); } } }
public static void Link(Stream toStream, List <byte[]> objectFileCodes, bool createExecutable, int loadAddress = 0) { var headers = new List <ObjectCodeHeader>(); var objDataOffsets = new List <int>(); int objDataOffset = 0; bool foundEntryPoint = false; for (int i = 0; i < objectFileCodes.Count; i++) { objDataOffsets.Add(objDataOffset + ((createExecutable && i == 0) ? 4 : 0)); var header = ObjectCodeUtils.ReadObjectCodeHeader(objectFileCodes[i]); headers.Add(header); if (header.HasEntryPoint) { if (foundEntryPoint) { throw new Exception("Multiple entry points detected in source object files"); } else { foundEntryPoint = true; } } objDataOffset += header.SizeOfDataAndCode; } if (createExecutable && !foundEntryPoint) { throw new Exception("No entry point found in any source object files"); } using (var stream = new MemoryStream()) using (var sw = new BinaryWriter(stream)) using (var sr = new BinaryReader(stream)) { if (createExecutable) { loadAddress += 4; sw.Write(0); //temporary until entry point address is resolved } for (int i = 0; i < objectFileCodes.Count; i++) { //Write data and code to output file using (var objStream = new BinaryReader(new MemoryStream(objectFileCodes[i]))) { objStream.BaseStream.Seek(headers[i].DataStart, SeekOrigin.Begin); byte[] dataAndCode = new byte[objStream.BaseStream.Length - headers[i].DataStart]; objStream.Read(dataAndCode, 0, dataAndCode.Length); sw.Seek(objDataOffsets[i], SeekOrigin.Begin); sw.Write(dataAndCode); } //Perform relocations and symbol resolution foreach (int address in headers[i].RelocationAddresses) { sr.BaseStream.Seek(address + objDataOffsets[i], SeekOrigin.Begin); int labelIndex = BitConverter.ToInt32(BitConverter.GetBytes(sr.ReadInt32()).Reverse().ToArray(), 0); var label = headers[i].LabelAddresses[labelIndex]; int newAddress = -1; if (label.IsExtern) { bool resolved = false; //search all other header symbol tables for (int j = 0; j < objectFileCodes.Count; j++) { if (i == j) { continue; } if (headers[j].ExportedSymbols.ContainsKey(label.SymbolName)) { resolved = true; int extLabelIndex = headers[j].ExportedSymbols[label.SymbolName]; newAddress = headers[j].LabelAddresses[extLabelIndex].Address; newAddress += objDataOffsets[j] + loadAddress; } } if (!resolved) { throw new UnresolvedExternalSymbolException(label.SymbolName); } } else { //Write new address newAddress = label.Address + objDataOffsets[i] + loadAddress; if (createExecutable) { if (headers[i].HasEntryPoint && headers[i].EntryPointFunctionLabel == labelIndex) { //Write entry point address at position 0 sw.Seek(0, SeekOrigin.Begin); sw.Write(BitConverter.GetBytes(newAddress).Reverse().ToArray()); } } } sw.Seek(address + objDataOffsets[i], SeekOrigin.Begin); sw.Write(BitConverter.GetBytes(newAddress).Reverse().ToArray()); } } stream.Seek(0, SeekOrigin.Begin); stream.CopyTo(toStream); } }