public void RunScript(binary_library.IBinaryFile output, IList <binary_library.IBinaryFile> inputs) { // First, identify the maximum sizes of common symbols Dictionary <string, SizeAlign> comm_syms = new Dictionary <string, SizeAlign>(); foreach (var iput in inputs) { var comm = iput.GetCommonSection(); if (comm != null) { for (int i = 0; i < comm.GetSymbolCount(); i++) { var comm_sym = comm.GetSymbol(i); if (comm_sym != null) { string name = comm_sym.Name; if (comm_sym.Type == SymbolType.Local) { name = iput.Filename + "." + name; } if (!comm_syms.ContainsKey(name) || comm_syms[name].Size < comm_sym.Size) { comm_syms[name] = new SizeAlign { Size = (int)comm_sym.Size, Align = (int)comm_sym.Offset, Sym = comm_sym } } ; } } } } int comm_len = 0; foreach (var k in comm_syms) { if ((comm_len % k.Value.Align) != 0) { comm_len = (comm_len / k.Value.Align) * k.Value.Align + k.Value.Align; } comm_len += k.Value.Size; } // Run the script LinkerScriptState state = new LinkerScriptState(); state.comm_length = comm_len; state.cur_section = output.GetGlobalSection(); foreach (ScriptEntry se in Script) { se.DoCommand(output, inputs, state); } if (Program.is_reloc) { output.IsExecutable = false; } else { output.IsExecutable = true; } Dictionary <string, int> globl_syms = new Dictionary <string, int>(); int sym_idx = 0; if (output.IsExecutable) { // Resolve weak symbols while (sym_idx < output.GetSymbolCount()) { ISymbol sym = output.GetSymbol(sym_idx); if (sym.Type == SymbolType.Weak) { // Whether to promote to a global symbol bool for_removal = false; // Have we found the appropriate global symbol before? if (globl_syms.ContainsKey(sym.Name)) { for_removal = true; // for removal } else { // Search from here onwards looking for a global // symbol with the same name for (int sym_idx_2 = sym_idx; sym_idx_2 < output.GetSymbolCount(); sym_idx_2++) { ISymbol sym_2 = output.GetSymbol(sym_idx_2); if (sym_2.Type == SymbolType.Global && sym_2.Name == sym.Name) { globl_syms[sym.Name] = sym_idx_2; for_removal = true; break; } } } if (for_removal) { output.RemoveSymbol(sym_idx); continue; } else { // Promote to a global symbol sym.Type = SymbolType.Global; globl_syms[sym.Name] = sym_idx; } } else if (sym.Type == SymbolType.Global) { globl_syms[sym.Name] = sym_idx; } sym_idx++; } // Resolve common symbols. If a global symbol is defined for // this, we use that, else we allocate a space in bss for it ulong cur_comm_sym_offset = state.comm_offset; foreach (var k in comm_syms.Keys) { if (!globl_syms.ContainsKey(k)) { // define a symbol in the common section for this if (state.comm_sect == null) { throw new Exception("Common sections used but no common section defined in linker script"); } uint align = (uint)comm_syms[k].Align; if ((cur_comm_sym_offset % align) != 0) { cur_comm_sym_offset = (cur_comm_sym_offset / align) * align + align; } ISymbol comm_sym = comm_syms[k].Sym; //comm_sym.DefinedIn = state.comm_sect; comm_sym.Offset = cur_comm_sym_offset; comm_sym.Name = k; cur_comm_sym_offset += (uint)comm_syms[k].Size; } } } // Now iterate through the data, saving to the appropriate sections foreach (KeyValuePair <binary_library.ISection, List <byte> > kvp in state.data) { if (kvp.Key != null) { if (kvp.Key.HasData) { kvp.Key.Length = kvp.Value.Count; for (int i = 0; i < kvp.Value.Count; i++) { kvp.Key.Data[i] = kvp.Value[i]; } } } } // Resolve relocations foreach (IBinaryFile ifile in inputs) { int reloc_count = ifile.GetRelocationCount(); for (int i = 0; i < reloc_count; i++) { IRelocation reloc = ifile.GetRelocation(i); // Is the location of the relocation included in the output? if (state.included_sections.Contains(reloc.DefinedIn)) { if (output.IsExecutable == false) { // Create a new relocation for the output file IRelocation new_copy_reloc = output.CreateRelocation(); new_copy_reloc.DefinedIn = state.input_section_locations[reloc.DefinedIn].OutputSection; new_copy_reloc.Offset = reloc.Offset + state.input_section_locations[reloc.DefinedIn].OutputSectionOffset; new_copy_reloc.Addend = reloc.Addend; new_copy_reloc.References = reloc.References; new_copy_reloc.Type = reloc.Type; output.AddRelocation(new_copy_reloc); continue; } // Where is the value to be relocated? LinkerScriptState.InputSectionLocation isl = state.input_section_locations[reloc.DefinedIn]; // Get the target of the relocation ISymbol reloc_target = reloc.References; ISection target_section = null; ulong target_section_offset = 0; if (reloc_target.DefinedIn == null) { // Try and find the requested symbol ISymbol found_target = output.FindSymbol(reloc_target.Name); if (found_target == null) { throw new Exception("Label '" + reloc_target.Name + "' not found"); } target_section = found_target.DefinedIn; target_section_offset = found_target.Offset; } else if (reloc_target.DefinedIn == reloc_target.DefinedIn.File.GetCommonSection()) { // Get full name of symbol string name = reloc_target.Name; if (reloc_target.Type == SymbolType.Local) { name = reloc_target.DefinedIn.File.Filename + "." + name; } target_section = state.comm_sect; if (globl_syms.ContainsKey(name)) { ISymbol found_target = output.FindSymbol(reloc_target.Name); if (found_target == null) { throw new Exception("Label '" + reloc_target.Name + "' not found"); } target_section = found_target.DefinedIn; target_section_offset = found_target.Offset; } else { target_section = state.comm_sect; target_section_offset = reloc_target.Offset; } } else { // Use the one stored in the relocation // First find out where the input section is located in the output if (!state.included_sections.Contains(reloc_target.DefinedIn)) { throw new Exception(); } LinkerScriptState.InputSectionLocation target_isl = state.input_section_locations[reloc_target.DefinedIn]; target_section = target_isl.OutputSection; target_section_offset = target_isl.OutputSectionOffset + reloc_target.Offset; } // Create a new relocation for the output file IRelocation new_reloc = output.CreateRelocation(); new_reloc.DefinedIn = isl.OutputSection; new_reloc.Offset = isl.OutputSectionOffset + reloc.Offset; new_reloc.Addend = reloc.Addend; new_reloc.References = output.CreateSymbol(); new_reloc.References.Offset = target_section_offset; new_reloc.References.Size = reloc_target.Size; new_reloc.References.Name = reloc_target.Name; target_section.AddSymbol(new_reloc.References); new_reloc.Type = reloc.Type; // Evaluate the relocation long val = new_reloc.Type.Evaluate(new_reloc); // Does it fit in the provided space? bool fits = true; ulong uval = BitConverter.ToUInt64(BitConverter.GetBytes(val), 0); var t = new_reloc.Type; ulong ext_mask = 0xffffffffffffffffUL << (t.BitLength + t.BitOffset); if ((t.BitLength + t.BitOffset) >= 64) { ext_mask = 0; } if (new_reloc.Type.IsSigned) { var sign_bit = (uval >> (t.BitLength + t.BitOffset - 1)) & 0x1; if (sign_bit == 1 && (uval & ext_mask) != ext_mask) { fits = false; } else if (sign_bit == 0 && (uval & ext_mask) != 0) { fits = false; } } else if ((uval & ext_mask) != 0) { fits = false; } if (fits == false) { throw new Exception("Relocation truncated to fit: " + reloc_target.Name + " against " + t.Name); } // Build the new value to reinsert in the data stream byte[] orig = new byte[8]; for (int idx = 0; idx < new_reloc.Type.Length; idx++) { orig[idx] = new_reloc.DefinedIn.Data[(int)new_reloc.Offset + idx]; } ulong orig_val = BitConverter.ToUInt64(orig, 0); // Mask out those bits we don't want orig_val &= new_reloc.Type.KeepMask; // Only set those bits we want uval &= new_reloc.Type.SetMask; uval |= orig_val; byte[] uvalb = BitConverter.GetBytes(uval); for (int idx = 0; idx < new_reloc.Type.Length; idx++) { new_reloc.DefinedIn.Data[(int)new_reloc.Offset + idx] = uvalb[idx]; } } } } // Remove local and undefined symbols from the output file sym_idx = 0; while (sym_idx < output.GetSymbolCount()) { ISymbol sym = output.GetSymbol(sym_idx); if (sym.Type == SymbolType.Local || sym.Type == SymbolType.Undefined) { output.RemoveSymbol(sym_idx); } else { sym_idx++; } } }
public void RunScript(binary_library.IBinaryFile output, IList<binary_library.IBinaryFile> inputs) { // First, identify the maximum sizes of common symbols Dictionary<string, SizeAlign> comm_syms = new Dictionary<string, SizeAlign>(); foreach(var iput in inputs) { var comm = iput.GetCommonSection(); if(comm != null) { for(int i = 0; i < comm.GetSymbolCount(); i++) { var comm_sym = comm.GetSymbol(i); if(comm_sym != null) { string name = comm_sym.Name; if (comm_sym.Type == SymbolType.Local) name = iput.Filename + "." + name; if (!comm_syms.ContainsKey(name) || comm_syms[name].Size < comm_sym.Size) comm_syms[name] = new SizeAlign { Size = (int)comm_sym.Size, Align = (int)comm_sym.Offset, Sym = comm_sym }; } } } } int comm_len = 0; foreach (var k in comm_syms) { if ((comm_len % k.Value.Align) != 0) comm_len = (comm_len / k.Value.Align) * k.Value.Align + k.Value.Align; comm_len += k.Value.Size; } // Run the script LinkerScriptState state = new LinkerScriptState(); state.comm_length = comm_len; state.cur_section = output.GetGlobalSection(); foreach (ScriptEntry se in Script) se.DoCommand(output, inputs, state); // Resolve weak symbols Dictionary<string, int> globl_syms = new Dictionary<string, int>(); int sym_idx = 0; while(sym_idx < output.GetSymbolCount()) { ISymbol sym = output.GetSymbol(sym_idx); if(sym.Type == SymbolType.Weak) { // Whether to promote to a global symbol bool for_removal = false; // Have we found the appropriate global symbol before? if (globl_syms.ContainsKey(sym.Name)) for_removal = true; // for removal else { // Search from here onwards looking for a global // symbol with the same name for(int sym_idx_2 = sym_idx; sym_idx_2 < output.GetSymbolCount(); sym_idx_2++) { ISymbol sym_2 = output.GetSymbol(sym_idx_2); if(sym_2.Type == SymbolType.Global && sym_2.Name == sym.Name) { globl_syms[sym.Name] = sym_idx_2; for_removal = true; break; } } } if(for_removal) { output.RemoveSymbol(sym_idx); continue; } else { // Promote to a global symbol sym.Type = SymbolType.Global; globl_syms[sym.Name] = sym_idx; } } else if (sym.Type == SymbolType.Global) globl_syms[sym.Name] = sym_idx; sym_idx++; } // Resolve common symbols. If a global symbol is defined for // this, we use that, else we allocate a space in bss for it ulong cur_comm_sym_offset = state.comm_offset; foreach(var k in comm_syms.Keys) { if(!globl_syms.ContainsKey(k)) { // define a symbol in the common section for this if (state.comm_sect == null) throw new Exception("Common sections used but no common section defined in linker script"); uint align = (uint)comm_syms[k].Align; if ((cur_comm_sym_offset % align) != 0) cur_comm_sym_offset = (cur_comm_sym_offset / align) * align + align; ISymbol comm_sym = comm_syms[k].Sym; //comm_sym.DefinedIn = state.comm_sect; comm_sym.Offset = cur_comm_sym_offset; comm_sym.Name = k; cur_comm_sym_offset += (uint)comm_syms[k].Size; } } // Now iterate through the data, saving to the appropriate sections foreach (KeyValuePair<binary_library.ISection, List<byte>> kvp in state.data) { if (kvp.Key != null) { if (kvp.Key.HasData) { kvp.Key.Length = kvp.Value.Count; for (int i = 0; i < kvp.Value.Count; i++) kvp.Key.Data[i] = kvp.Value[i]; } } } // Resolve relocations foreach (IBinaryFile ifile in inputs) { int reloc_count = ifile.GetRelocationCount(); for (int i = 0; i < reloc_count; i++) { IRelocation reloc = ifile.GetRelocation(i); // Is the location of the relocation included in the output? if (state.included_sections.Contains(reloc.DefinedIn)) { // Where is the value to be relocated? LinkerScriptState.InputSectionLocation isl = state.input_section_locations[reloc.DefinedIn]; // Get the target of the relocation ISymbol reloc_target = reloc.References; ISection target_section = null; ulong target_section_offset = 0; if (reloc_target.DefinedIn == null) { // Try and find the requested symbol ISymbol found_target = output.FindSymbol(reloc_target.Name); if (found_target == null) throw new Exception("Label '" + reloc_target.Name + "' not found"); target_section = found_target.DefinedIn; target_section_offset = found_target.Offset; } else if(reloc_target.DefinedIn == reloc_target.DefinedIn.File.GetCommonSection()) { // Get full name of symbol string name = reloc_target.Name; if (reloc_target.Type == SymbolType.Local) name = reloc_target.DefinedIn.File.Filename + "." + name; target_section = state.comm_sect; if(globl_syms.ContainsKey(name)) { ISymbol found_target = output.FindSymbol(reloc_target.Name); if(found_target == null) throw new Exception("Label '" + reloc_target.Name + "' not found"); target_section = found_target.DefinedIn; target_section_offset = found_target.Offset; } else { target_section = state.comm_sect; target_section_offset = reloc_target.Offset; } } else { // Use the one stored in the relocation // First find out where the input section is located in the output if (!state.included_sections.Contains(reloc_target.DefinedIn)) throw new Exception(); LinkerScriptState.InputSectionLocation target_isl = state.input_section_locations[reloc_target.DefinedIn]; target_section = target_isl.OutputSection; target_section_offset = target_isl.OutputSectionOffset + reloc_target.Offset; } // Create a new relocation for the output file IRelocation new_reloc = output.CreateRelocation(); new_reloc.DefinedIn = isl.OutputSection; new_reloc.Offset = isl.OutputSectionOffset + reloc.Offset; new_reloc.Addend = reloc.Addend; new_reloc.References = output.CreateSymbol(); new_reloc.References.DefinedIn = target_section; new_reloc.References.Offset = target_section_offset; new_reloc.References.Size = reloc_target.Size; new_reloc.References.Name = reloc_target.Name; new_reloc.Type = reloc.Type; // Evaluate the relocation long val = new_reloc.Type.Evaluate(new_reloc); // Does it fit in the provided space? bool fits = true; ulong uval = BitConverter.ToUInt64(BitConverter.GetBytes(val), 0); var t = new_reloc.Type; ulong ext_mask = 0xffffffffffffffffUL << (t.BitLength + t.BitOffset); if (new_reloc.Type.IsSigned) { var sign_bit = (uval >> (t.BitLength + t.BitOffset - 1)) & 0x1; if (sign_bit == 1 && (uval & ext_mask) != ext_mask) fits = false; else if (sign_bit == 0 && (uval & ext_mask) != 0) fits = false; } else if ((uval & ext_mask) != 0) fits = false; if (fits == false) throw new Exception("Relocation truncated to fit: " + reloc_target.Name + " against " + t.Name); // Build the new value to reinsert in the data stream byte[] orig = new byte[8]; for (int idx = 0; idx < new_reloc.Type.Length; idx++) orig[idx] = new_reloc.DefinedIn.Data[(int)new_reloc.Offset + idx]; ulong orig_val = BitConverter.ToUInt64(orig, 0); // Mask out those bits we don't want orig_val &= new_reloc.Type.KeepMask; // Only set those bits we want uval &= new_reloc.Type.SetMask; uval |= orig_val; byte[] uvalb = BitConverter.GetBytes(uval); for (int idx = 0; idx < new_reloc.Type.Length; idx++) new_reloc.DefinedIn.Data[(int)new_reloc.Offset + idx] = uvalb[idx]; } } } // Remove local and undefined symbols from the output file sym_idx = 0; while(sym_idx < output.GetSymbolCount()) { ISymbol sym = output.GetSymbol(sym_idx); if (sym.Type == SymbolType.Local || sym.Type == SymbolType.Undefined) output.RemoveSymbol(sym_idx); else sym_idx++; } // Mark the output as executable output.IsExecutable = true; }