indis-core 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -17,6 +17,7 @@
17
17
  ##############################################################################
18
18
 
19
19
  require 'indis-core/version'
20
+ require 'indis-core/target'
20
21
 
21
22
  module Indis
22
23
  end
@@ -21,13 +21,16 @@ module Indis
21
21
  class Section
22
22
  attr_reader :name, :segment
23
23
  attr_reader :vmaddr, :vmsize, :bytes
24
+ attr_reader :type, :attributes
24
25
 
25
- def initialize(seg, name, vmaddr, vmsize, iooff)
26
+ def initialize(seg, name, vmaddr, vmsize, iooff, type, attrs)
26
27
  @segment = seg
27
28
  @name = name
28
29
  @vmaddr = vmaddr
29
30
  @vmsize = vmsize
30
31
  @iooff = iooff
32
+ @type = type
33
+ @attributes = attrs
31
34
  end
32
35
 
33
36
  def to_vmrange
@@ -28,6 +28,10 @@ module Indis
28
28
  @format_sym = format_sym
29
29
  @vmaddr = vmaddr
30
30
  end
31
+
32
+ def to_s
33
+ "#<Indis::Symbol #{@name} at #{@vmaddr.to_s 16}>"
34
+ end
31
35
  end
32
36
 
33
37
  end
@@ -38,7 +38,7 @@ module Indis
38
38
  # @raise [AttributeError] if the file does not exist
39
39
  # @raise [RuntimeError] if there is no known format for magic or there are several matching formats
40
40
  def initialize(filename)
41
- raise AttributeError, "File does not exist" unless FileTest.file?(filename)
41
+ raise ArgumentError, "File does not exist" unless FileTest.file?(filename)
42
42
  @filename = filename
43
43
  @io = StringIO.new(File.open(filename).read().force_encoding('BINARY'))
44
44
 
@@ -50,9 +50,23 @@ module Indis
50
50
  raise RuntimeError, "Unknown format for magic #{magic.to_s(16)}" if fmts.length == 0
51
51
  raise RuntimeError, "Several possible formats: #{fmts}" if fmts.length > 1
52
52
 
53
- @format = fmts.first.new(self, @io)
53
+ @format_class = fmts.first
54
+ @format_load_complete = false
55
+ end
56
+
57
+ # Perform format load and set up a vm map
58
+ def load
59
+ raise RuntimeError, "Already loaded" unless @format_class
60
+ @format = @format_class.new(self, @io)
61
+ @format_class = nil
54
62
 
55
63
  @vmmap = VMMap.new(self)
64
+
65
+ @format_load_complete = true
66
+ replay_queue
67
+ publish_event :target_load_complete
68
+
69
+ self
56
70
  end
57
71
 
58
72
  # A target can consist of several other targets (e.g. fat mach-o). In such
@@ -63,6 +77,60 @@ module Indis
63
77
  def meta?
64
78
  @subtargets && @subtargets.length > 0
65
79
  end
80
+
81
+ # External class can subscribe for events happening with target
82
+ #
83
+ # @param [Symbol] event event name
84
+ # @param listener event listener
85
+ def subscribe_for_event(event, listener)
86
+ subscriptions_array(event) << listener
87
+ end
88
+
89
+ # Post an event with optional payload. All events queue up until #load ends
90
+ #
91
+ # @param [Symbol] event event name
92
+ def publish_event(event, *args)
93
+ if @format_load_complete == true
94
+ subscriptions_array(event).each { |s| s.send(event, *args) }
95
+ else
96
+ enqueue_event(event, args)
97
+ end
98
+ end
99
+
100
+ # Resolves a symbol at given virtual address. Targe does so by first checking
101
+ # the locally available symbols, and falls back to format specific resolver
102
+ # if nothing found
103
+ #
104
+ # @param [Fixnum] vmaddr virtual address
105
+ # @return [Indis::Symbol, nil] resolved symbol or nil if none found
106
+ def resolve_symbol_at_address(vmaddr)
107
+ s = @symbols.find { |sym| sym.vmaddr == vmaddr }
108
+ return s if s
109
+ return @format.resolve_symbol_at_address(vmaddr) if @format.respond_to?(:resolve_symbol_at_address)
110
+ end
111
+
112
+ private
113
+ def enqueue_event(event, args)
114
+ @event_queue ||= []
115
+ @event_queue << [event, args]
116
+ end
117
+
118
+ def replay_queue
119
+ return unless @event_queue
120
+ @event_queue.each { |(e, args)| publish_event(e, *args) }
121
+ @event_queue = nil
122
+ end
123
+
124
+ def subscriptions_array(event)
125
+ @subscriptions ||= {}
126
+
127
+ a = @subscriptions[event]
128
+ unless a
129
+ a = []
130
+ @subscriptions[event] = a
131
+ end
132
+ a
133
+ end
66
134
  end
67
135
 
68
136
  end
@@ -17,5 +17,5 @@
17
17
  ##############################################################################
18
18
 
19
19
  module Indis
20
- VERSION = "0.1.1"
20
+ VERSION = "0.1.2"
21
21
  end
@@ -81,7 +81,7 @@ module Indis
81
81
  # Maps an {Indis::Entity entity} (based on its offset).
82
82
  # @raise [ArgumentError] if the range is occupied by another entity
83
83
  def map(e)
84
- raise ArgumentError unless has_unmapped(e.vmaddr, e.size)
84
+ raise ArgumentError, "Tried to map #{e} at #{e.vmaddr}+#{e.size} on top of #{self[e.vmaddr...(e.vmaddr+e.size)]}" unless has_unmapped(e.vmaddr, e.size)
85
85
  @blocks[e.vmaddr] = e
86
86
  end
87
87
 
@@ -2,13 +2,13 @@ require 'indis-core/section'
2
2
 
3
3
  describe Indis::Section do
4
4
  it "should provide a correct range for its vm region" do
5
- sect = Indis::Section.new(double('Segment'), '__text', 0x4096, 120, 0)
5
+ sect = Indis::Section.new(double('Segment'), '__text', 0x4096, 120, 0, :undef, [])
6
6
  sect.to_vmrange.should == (0x4096..0x410e)
7
7
  end
8
8
 
9
9
  it "should provide bytes value from segment based on io offset" do
10
10
  seg = double('Segment', bytes: 'qwertyuiop', iooff: 100)
11
- sect = Indis::Section.new(seg, '__text', 0x4096, 4, 102)
11
+ sect = Indis::Section.new(seg, '__text', 0x4096, 4, 102, :undef, [])
12
12
  sect.bytes.should == 'erty'
13
13
  end
14
14
  end
@@ -6,13 +6,58 @@ describe Indis::Target do
6
6
  end
7
7
 
8
8
  it "should load file for known format" do
9
- fmt = double('MachO', magic: 0xfeedface, name: 'Mach-O', new: nil)
9
+ fmt = double('MachO Class', magic: 0xfeedface, name: 'Mach-O', new: double('MachO'))
10
10
  Indis::BinaryFormat.stub(:known_formats).and_return([fmt])
11
11
  t = Indis::Target.new("spec/fixtures/single-object.o")
12
+ t.load
12
13
  t.io.length.should_not == 0
14
+ t.format.should_not be_nil
13
15
  end
14
16
 
15
17
  it "should raise if there is no known format to process binary" do
16
18
  expect { Indis::Target.new("spec/fixtures/single-object.o") }.to raise_error(RuntimeError)
17
19
  end
20
+
21
+ it "should trigger load event" do
22
+ fmt = double('MachO Class', magic: 0xfeedface, name: 'Mach-O', new: double('MachO'))
23
+ Indis::BinaryFormat.stub(:known_formats).and_return([fmt])
24
+ t = Indis::Target.new("spec/fixtures/single-object.o")
25
+
26
+ lis = double('Listener')
27
+ lis.should_receive(:target_load_complete)
28
+
29
+ t.subscribe_for_event(:target_load_complete, lis)
30
+
31
+ t.load
32
+ end
33
+
34
+ it "should queue up events that happen before load" do
35
+ fmt = double('MachO Class', magic: 0xfeedface, name: 'Mach-O', new: double('MachO'))
36
+ Indis::BinaryFormat.stub(:known_formats).and_return([fmt])
37
+ t = Indis::Target.new("spec/fixtures/single-object.o")
38
+
39
+ lis = double('Listener')
40
+ lis.should_receive(:early_event).twice
41
+
42
+ t.subscribe_for_event(:early_event, lis)
43
+
44
+ t.publish_event(:early_event)
45
+ t.publish_event(:early_event)
46
+
47
+ t.load
48
+ end
49
+
50
+ it "should pass arguments to event subscriptions" do
51
+ fmt = double('MachO Class', magic: 0xfeedface, name: 'Mach-O', new: double('MachO'))
52
+ Indis::BinaryFormat.stub(:known_formats).and_return([fmt])
53
+ t = Indis::Target.new("spec/fixtures/single-object.o")
54
+ t.load
55
+
56
+ lis = double('Listener')
57
+ lis.should_receive(:event_with_args).with(1, 2).once
58
+
59
+ t.subscribe_for_event(:event_with_args, lis)
60
+
61
+ t.publish_event(:event_with_args, 1, 2)
62
+ end
18
63
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: indis-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-04-16 00:00:00.000000000 Z
12
+ date: 2012-04-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec