class GdbFlasher::ServerConnection
Public Class Methods
new()
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 14 def initialize @socket = nil end
Public Instance Methods
close()
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 80 def close @socket.close @socket = nil end
connect(address)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 18 def connect(address) raise "Already connected" unless @socket.nil? delimiter = address.rindex ':' raise ArgumentError, "Port must be specified" if delimiter.nil? host = address[0...delimiter] port = address[delimiter + 1..-1].to_i @buf = "" @features = {} @need_ack = true begin @socket = TCPSocket.new host, port @socket.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1 # Query stub features command("qSupported").split(';').each do |feature| if feature[-1] == '-' @features.delete feature[0...-1].intern elsif feature[-1] == '+' @features[feature[0...-1].intern] = true else sep = feature.index '=' if sep.nil? raise "Unexpected item in qSupported response: #{sep}" end @features[feature[0...sep].intern] = feature[sep + 1..-1] end end if @features[:PacketSize].nil? raise "PacketSize isn't present in qSupported response" end if @features[:QStartNoAckMode] response = command("QStartNoAckMode") if response != "OK" raise "Unable to enter NoAck mode: #{response}" end @need_ack = false end # Load target description if @features[:"qXfer:features:read"] description = read_xfer "features", "target.xml" # TODO: parse target description and use register map from it. end rescue Exception => e @socket.close unless @socket.nil? @socket = nil raise e end end
continue()
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 167 def continue parse_stop_reply command("c") end
get_stop_reply()
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 183 def get_stop_reply parse_stop_reply command("?") end
insert_breakpoint(type, address, kind)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 151 def insert_breakpoint(type, address, kind) response = command "Z#{type.to_s 16},#{address.to_s 16},#{kind.to_s 16}" if response != "OK" raise "Breakpoint insertion failed: #{response}" end end
read_memory(base, size)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 125 def read_memory(base, size) max_size = (@features[:PacketSize].to_i - 19) & ~1 offset = 0 data = "" size *= 2 while offset < size chunk_size = [ max_size, size - offset ].min response = command "m#{base.to_s 16},#{(chunk_size / 2).to_s 16}" if response[0] == "E" raise "Memory read failed: #{response}" end data += response base += chunk_size / 2 offset += chunk_size end Array.new(data.length / 2) do |i| data[i * 2..i * 2 + 1].to_i 16 end.pack("C*") end
read_registers()
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 85 def read_registers response = command "g" if (response[0] != 'E' || response.length != 3) && response.length % 8 != 0 raise "Malformed 'g' response" end if response[0] == 'E' raise "GDB server error: #{response[1..2].to_i 16}" end Array.new response.length / 8 do |i| big2native response[i * 8...(i + 1) * 8].to_i(16) end end
remove_breakpoint(type, address, kind)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 159 def remove_breakpoint(type, address, kind) response = command "z#{type.to_s 16},#{address.to_s 16},#{kind.to_s 16}" if response != "OK" raise "Breakpoint removal failed: #{response}" end end
reset()
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 175 def reset reply = command "r" if reply != "OK" raise "Reset failed: #{reply}" end end
step()
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 171 def step parse_stop_reply command("s") end
write_memory(base, string)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 105 def write_memory(base, string) max_size = (@features[:PacketSize].to_i - 19) & ~1 offset = 0 data = string.unpack("C*").map { |byte| sprintf "%02X", byte }.join while offset < data.length chunk_size = [ max_size, data.length - offset ].min response = command "M#{base.to_s 16},#{(chunk_size / 2).to_s 16}:#{data[offset...offset + chunk_size]}" if response != "OK" raise "Memory write failed: #{response}" end base += chunk_size / 2 offset += chunk_size end end
write_register(reg, value)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 187 def write_register(reg, value) reply = command sprintf("P%x=%08x", reg, big2native(value)) if reply != "OK" raise "Register write failed: #{reply}" end end
write_registers(regs)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 101 def write_registers(regs) send_packet "G" + regs.map { |r| sprintf "%08x", native2big(r) }.join end
Protected Instance Methods
big2native(v)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 330 def big2native(v) [ v ].pack("L").unpack("N")[0] end
command(s, extra = {})
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 244 def command(s, extra = {}) send_packet s, extra response = receive_packet end
native2big(v)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 326 def native2big(v) [ v ].pack("N").unpack("L")[0] end
parse_stop_reply(reply)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 197 def parse_stop_reply(reply) case reply[0] when 'S' StopReply.new reply[1..2].to_i(16) when 'T' pairs = reply[3..-1].split ';' values = {} pairs.each do |pair| key, value = pair.split ':' if key.match /^[0-9a-fA-F]+/ values[key.to_i 16] = value.to_i 16 else values[key] = value end end StopReply.new reply[1..2].to_i(16), values else raise "Unknown stop reply: #{reply}" end end
read_xfer(object, annex)
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 222 def read_xfer(object, annex) offset = 0 size = @features[:PacketSize].to_i - 4 contents = "" loop do response = command "qXfer:#{object}:read:#{annex}:#{offset}:#{size}" offset += response.length - 1 contents += response[1..-1] if response[0] == 'l' break elsif response[0] != 'm' raise "qXfer failed: #{response}" end end contents end
receive_packet()
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 295 def receive_packet loop do @buf += @socket.readpartial 4096 if @buf[0] != '$' raise "Invalid response from server" end idx = @buf.index '#' if !idx.nil? && idx + 2 <= @buf.length message = @buf.slice! 0..idx + 2 data = message[1..-4] if @need_ack checksum = data.sum 8 if checksum != message[-2..-1].to_i(16) @socket.write '-' else @socket.write '+' return data end else return data end end end end
send_packet(data, extra = {})
click to toggle source
# File lib/gdbflasher/server_connection.rb, line 250 def send_packet(data, extra = {}) data = data.dup if extra[:escape] i = 0 while i < data.length byte = data[i] if byte == '$' || byte == '#' || byte == 0x7d.chr data.insert i, 0x7d.chr i += 1 end i += 1 end end message = sprintf "$%s#%02x", data, data.sum(8) if !@features[:PacketSize].nil? && @features[:PacketSize].to_i < message.length raise "Internal error: message is too long" end @socket.write message if @need_ack loop do ack = @socket.read 1 case ack when '+' break when '-' @socket.write message else raise "Unexpected response from server: #{ack}" end end end end