indis-core 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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