
require 'nm'
require 'lsmod'

class KSym
  attr_reader :nm_sym, :mod_name, :addr
  attr_accessor :size

  def initialize(nm_sym, mod_name, addr)
    @nm_sym = nm_sym
    @mod_name = mod_name
    @addr = addr
    @size = 0
  end

  def <=> (other)
    @addr <=> other.addr
  end

  def == (other)
    return false if other == nil
    return (@nm_sym == other.nm_sym && @mod_name == other.mod_name &&
            @addr == other.addr)
  end
end

class KSym_Map
  def initialize
    @map = []
    @map.push(KSym.new(NM_Sym.new("00000000", '?', 0), '', 0))
    @map.push(KSym.new(NM_Sym.new("00000001", '?', 0), '', 1))
    @map.push(KSym.new(NM_Sym.new("00000002", '?', 0), '', 2))
    @map.push(KSym.new(NM_Sym.new("ffffffff", '?', 0xffffffff+1), '', 0xffffffff+1))
    @sorted = 1
  end

  def add_symbol(nm_sym, mod_name, base_addr)
    @sorted = 0
    @map.push(KSym.new(nm_sym, mod_name, base_addr + nm_sym.value))
  end

  def addr2sym(addr)
    @map.sort! if @sorted == 0

    left = 0
    right = @map.length - 1
    while left <= right
      middle = (left + right)/2
      if @map[middle].addr <= addr
	left = middle+1
      elsif
	right = middle-1
      end
    end

    @map[right].size = @map[right+1].addr - @map[right].addr
    @map[right]
  end
end


class KSyms
  def initialize(ksyms_file='/proc/ksyms', system_map='/boot/System.map')

    modules_info = Proc_Ksyms_Reader.All_KMod_Load_Info(ksyms_file)
    @kaddr_map = KSym_Map.new
    
    # add kernel symbols
    
    syms = NM_Reader.All_Syms_File(system_map)
    syms.each do |sym|
      if (sym.type.upcase == 'T')
        @kaddr_map.add_symbol(sym, '', 0)
      elsif (sym.name == '_stext' || sym.name == '_etext')
	@kaddr_map.add_symbol(sym, '', sym.value)
      end
    end

    # add module symbols
    
    modules_info.each_pair do |mod_name,mod_info|
      syms = NM_Reader.All_Syms_Obj(mod_info.file)
      syms.each do |sym|
        next if (sym.type.upcase != 'T')
        @kaddr_map.add_symbol(sym, mod_info.name, mod_info.section_addr('.text'))
      end
      # XXX: add end module symbol and start module symbol
    end

    @@cache = {}
  end

  def addr2ksym(addr)
    if @@cache[addr] != nil
      return @@cache[addr]
    end
    @@cache[addr] = @kaddr_map.addr2sym(addr)
  end

  def addr2symname(addr)
    if @@cache[addr] != nil
      ksym = @@cache[addr]
    else
      @@cache[addr] = ksym = @kaddr_map.addr2sym(addr)
    end

    sym = ksym.nm_sym.name
    off = addr - ksym.addr
    mod = ksym.mod_name
    sym += '+' + off.to_s if off > 0
    sym += '[' + mod + ']' if mod != ''
    return sym
  end
end

