seccomp-tools 1.6.1 → 1.7.0

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.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +153 -0
  3. data/LICENSE +21 -0
  4. data/README.md +267 -46
  5. data/completions/_seccomp-tools +83 -0
  6. data/completions/seccomp-tools.bash +59 -0
  7. data/completions/seccomp-tools.fish +52 -0
  8. data/ext/ptrace/ptrace.c +2 -2
  9. data/lib/seccomp-tools/asm/asm.rb +9 -4
  10. data/lib/seccomp-tools/asm/compiler.rb +39 -9
  11. data/lib/seccomp-tools/asm/sasm.tab.rb +31 -21
  12. data/lib/seccomp-tools/asm/sasm.y +15 -7
  13. data/lib/seccomp-tools/asm/scalar.rb +50 -7
  14. data/lib/seccomp-tools/asm/scanner.rb +40 -8
  15. data/lib/seccomp-tools/asm/statement.rb +14 -5
  16. data/lib/seccomp-tools/asm/token.rb +19 -1
  17. data/lib/seccomp-tools/audit/catalog.rb +66 -0
  18. data/lib/seccomp-tools/audit/checks/arch_unchecked.rb +41 -0
  19. data/lib/seccomp-tools/audit/checks/dangerous_allow.rb +34 -0
  20. data/lib/seccomp-tools/audit/checks/orw_chain.rb +41 -0
  21. data/lib/seccomp-tools/audit/checks/permissive_default.rb +29 -0
  22. data/lib/seccomp-tools/audit/checks/syscall_alt_gap.rb +44 -0
  23. data/lib/seccomp-tools/audit/checks/x32_guard.rb +46 -0
  24. data/lib/seccomp-tools/audit/checks.rb +42 -0
  25. data/lib/seccomp-tools/audit/finding.rb +25 -0
  26. data/lib/seccomp-tools/audit/policy.rb +126 -0
  27. data/lib/seccomp-tools/audit/report.rb +98 -0
  28. data/lib/seccomp-tools/audit.rb +48 -0
  29. data/lib/seccomp-tools/bpf.rb +27 -17
  30. data/lib/seccomp-tools/cli/asm.rb +7 -3
  31. data/lib/seccomp-tools/cli/audit.rb +86 -0
  32. data/lib/seccomp-tools/cli/base.rb +51 -8
  33. data/lib/seccomp-tools/cli/cli.rb +10 -6
  34. data/lib/seccomp-tools/cli/completion.rb +40 -0
  35. data/lib/seccomp-tools/cli/disasm.rb +10 -6
  36. data/lib/seccomp-tools/cli/dump.rb +37 -52
  37. data/lib/seccomp-tools/cli/dumpable.rb +79 -0
  38. data/lib/seccomp-tools/cli/emu.rb +32 -9
  39. data/lib/seccomp-tools/cli/explain.rb +51 -0
  40. data/lib/seccomp-tools/cli/filter_input.rb +130 -0
  41. data/lib/seccomp-tools/const.rb +98 -15
  42. data/lib/seccomp-tools/consts/sys_nr/amd64.rb +1 -1
  43. data/lib/seccomp-tools/consts/sys_nr/riscv64.rb +332 -0
  44. data/lib/seccomp-tools/disasm/disasm.rb +31 -13
  45. data/lib/seccomp-tools/dumper.rb +67 -35
  46. data/lib/seccomp-tools/emulator.rb +41 -16
  47. data/lib/seccomp-tools/error.rb +4 -2
  48. data/lib/seccomp-tools/explain/analysis.rb +67 -0
  49. data/lib/seccomp-tools/explain/path_facts.rb +110 -0
  50. data/lib/seccomp-tools/explain/qword.rb +204 -0
  51. data/lib/seccomp-tools/explain/renderer.rb +128 -0
  52. data/lib/seccomp-tools/explain/summary.rb +218 -0
  53. data/lib/seccomp-tools/explain/verdict.rb +44 -0
  54. data/lib/seccomp-tools/explain.rb +38 -0
  55. data/lib/seccomp-tools/instruction/alu.rb +14 -9
  56. data/lib/seccomp-tools/instruction/base.rb +39 -10
  57. data/lib/seccomp-tools/instruction/jmp.rb +50 -23
  58. data/lib/seccomp-tools/instruction/ld.rb +46 -21
  59. data/lib/seccomp-tools/instruction/ldx.rb +4 -3
  60. data/lib/seccomp-tools/instruction/misc.rb +11 -9
  61. data/lib/seccomp-tools/instruction/ret.rb +12 -6
  62. data/lib/seccomp-tools/instruction/st.rb +15 -6
  63. data/lib/seccomp-tools/instruction/stx.rb +4 -3
  64. data/lib/seccomp-tools/logger.rb +15 -2
  65. data/lib/seccomp-tools/symbolic/constraint.rb +80 -0
  66. data/lib/seccomp-tools/symbolic/executor.rb +210 -0
  67. data/lib/seccomp-tools/symbolic/expr.rb +185 -0
  68. data/lib/seccomp-tools/symbolic/state.rb +82 -0
  69. data/lib/seccomp-tools/syscall.rb +74 -22
  70. data/lib/seccomp-tools/util.rb +70 -11
  71. data/lib/seccomp-tools/version.rb +1 -1
  72. data/lib/seccomp-tools.rb +10 -1
  73. metadata +83 -11
  74. data/lib/seccomp-tools/disasm/context.rb +0 -171
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SeccompTools
4
- # Define utility methods.
4
+ # Utility methods shared across the library: architecture detection and terminal colorizing.
5
5
  module Util
6
6
  module_function
7
7
 
8
8
  # Get currently supported architectures.
9
+ #
10
+ # Derived from the syscall tables shipped under +consts/sys_nr/+.
9
11
  # @return [Array<Symbol>]
10
- # Architectures.
12
+ # Architecture names, sorted.
11
13
  def supported_archs
12
14
  @supported_archs ||= Dir.glob(File.join(__dir__, 'consts', 'sys_nr', '*.rb'))
13
15
  .map { |f| File.basename(f, '.rb').to_sym }
@@ -16,23 +18,57 @@ module SeccompTools
16
18
 
17
19
  # Detect system architecture.
18
20
  # @return [Symbol]
21
+ # One of {supported_archs}, or +:unknown+ if the host CPU is not supported.
19
22
  def system_arch
20
23
  case RbConfig::CONFIG['host_cpu']
21
24
  when /x86_64/ then :amd64
22
25
  when /i386/ then :i386
23
26
  when /aarch64/ then :aarch64
27
+ when /riscv64/ then :riscv64
24
28
  when /s390x/ then :s390x
25
29
  else :unknown
26
30
  end
27
31
  end
28
32
 
33
+ # ELF +e_machine+ values (the halfword at offset 18) of the architectures seccomp-tools supports,
34
+ # as they are laid out in the file - so the big-endian s390x reads the other way around. The keys
35
+ # are binary strings (+.b+) to match what is read off the file: this source is UTF-8, where a
36
+ # literal such as +"\xb7\x00"+ would never compare equal to those bytes.
37
+ ELF_MACHINE = {
38
+ "\x03\x00".b => :i386,
39
+ "\x3e\x00".b => :amd64,
40
+ "\xb7\x00".b => :aarch64,
41
+ "\xf3\x00".b => :riscv64,
42
+ "\x00\x16".b => :s390x
43
+ }.freeze
44
+
45
+ # The architecture a running process was built for, read from the ELF header of its executable.
46
+ #
47
+ # Filters dumped from another process are numbered for *its* architecture, so this is what makes
48
+ # the syscall names right when it differs from the host.
49
+ # @param [Integer] pid
50
+ # Process identifier.
51
+ # @return [Symbol?]
52
+ # One of {supported_archs}, or +nil+ when the executable cannot be read (no +/proc+, the
53
+ # process is gone, permission denied) or its machine type is not one we know.
54
+ def process_arch(pid)
55
+ File.open("/proc/#{pid}/exe", 'rb') do |f|
56
+ f.pos = 18
57
+ ELF_MACHINE[f.read(2)]
58
+ end
59
+ rescue SystemCallError
60
+ nil
61
+ end
62
+
29
63
  # Enable colorize.
64
+ #
65
+ # Colors are still only emitted when the output is a tty, see {colorize_enabled?}.
30
66
  # @return [void]
31
67
  def enable_color!
32
68
  @disable_color = false
33
69
  end
34
70
 
35
- # Disable colorize.
71
+ # Disable colorize, {colorize} becomes a no-op regardless of the output being a tty.
36
72
  # @return [void]
37
73
  def disable_color!
38
74
  @disable_color = true
@@ -40,28 +76,35 @@ module SeccompTools
40
76
 
41
77
  # Is colorize enabled?
42
78
  # @return [Boolean]
79
+ # +true+ only if colors have not been disabled by {disable_color!} and +$stdout+ is a tty.
43
80
  def colorize_enabled?
44
81
  !@disable_color && $stdout.tty?
45
82
  end
46
83
 
47
84
  # color code of light yellow
48
85
  LIGHT_YELLOW = "\e[38;5;230m"
49
- # Color codes for pretty print.
86
+ # Color codes for pretty print. +error+, +warn+ and +info+ are severities rather than parts of a
87
+ # filter, shading from alarming to quiet; {SeccompTools::Logger} colors its own levels by the
88
+ # same names.
50
89
  COLOR_CODE = {
51
90
  esc_m: "\e[0m",
52
91
  syscall: "\e[38;5;120m", # light green
53
92
  arch: LIGHT_YELLOW,
54
93
  args: LIGHT_YELLOW,
55
94
  gray: "\e[2m",
56
- error: "\e[38;5;196m" # heavy red
95
+ error: "\e[38;5;196m", # heavy red
96
+ warn: LIGHT_YELLOW,
97
+ info: "\e[38;5;110m" # light blue
57
98
  }.freeze
58
- # Wrapper color codes.
59
- # @param [String] s
60
- # Contents to wrapper.
99
+ # Wrap contents with terminal color codes.
100
+ #
101
+ # Returns +s+ unchanged when {colorize_enabled?} is +false+.
102
+ # @param [#to_s] s
103
+ # Contents to be wrapped.
61
104
  # @param [Symbol?] t
62
- # Specific which kind of color to use, valid symbols are defined in {Util.COLOR_CODE}.
105
+ # Which kind of color to use, valid symbols are the keys of {Util::COLOR_CODE}.
63
106
  # @return [String]
64
- # Wrapper with color codes.
107
+ # +s+ wrapped with color codes.
65
108
  def colorize(s, t: nil)
66
109
  s = s.to_s
67
110
  return s unless colorize_enabled?
@@ -71,13 +114,29 @@ module SeccompTools
71
114
  "#{color}#{s.sub(cc[:esc_m], cc[:esc_m] + color)}#{cc[:esc_m]}"
72
115
  end
73
116
 
117
+ # Does the file at +path+ start with the ELF magic bytes?
118
+ #
119
+ # Used to tell an executable apart from a raw BPF blob.
120
+ # @param [String] path
121
+ # Path to the file to check.
122
+ # @return [Boolean]
123
+ # +false+ when the file cannot be read.
124
+ def elf?(path)
125
+ File.binread(path, 4) == "\x7fELF".b
126
+ rescue SystemCallError
127
+ false
128
+ end
129
+
74
130
  # Get content of filename under directory templates/.
75
131
  #
76
132
  # @param [String] filename
77
- # The filename.
133
+ # Basename of a file under +lib/seccomp-tools/templates/+.
78
134
  #
79
135
  # @return [String]
80
136
  # Content of the file.
137
+ #
138
+ # @raise [Errno::ENOENT]
139
+ # If no such template exists.
81
140
  def template(filename)
82
141
  File.binread(File.join(__dir__, 'templates', filename))
83
142
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module SeccompTools
4
4
  # Gem version.
5
- VERSION = '1.6.1'
5
+ VERSION = '1.7.0'
6
6
  end
data/lib/seccomp-tools.rb CHANGED
@@ -2,7 +2,16 @@
2
2
 
3
3
  # @author david942j
4
4
 
5
- # Main module.
5
+ # Toolkit for working with seccomp BPF filters.
6
+ #
7
+ # The library entry points are {Asm.asm} to compile assembly into raw BPF, {Disasm.disasm} to turn
8
+ # raw BPF back into readable assembly, {Dumper.dump} to capture the filters a process installs, and
9
+ # {Emulator} to run a filter against a hypothetical syscall.
10
+ #
11
+ # @example
12
+ # raw = SeccompTools::Asm.asm("return ALLOW\n", arch: :amd64)
13
+ # SeccompTools::Disasm.disasm(raw, arch: :amd64, display_bpf: false)
14
+ # #=> "0000: return ALLOW\n"
6
15
  module SeccompTools
7
16
  end
8
17
 
metadata CHANGED
@@ -1,15 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seccomp-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - david942j
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-12-25 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ostruct
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
13
26
  - !ruby/object:Gem::Dependency
14
27
  name: rake
15
28
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +85,14 @@ dependencies:
72
85
  requirements:
73
86
  - - "~>"
74
87
  - !ruby/object:Gem::Version
75
- version: '0.21'
88
+ version: '1.0'
76
89
  type: :development
77
90
  prerelease: false
78
91
  version_requirements: !ruby/object:Gem::Requirement
79
92
  requirements:
80
93
  - - "~>"
81
94
  - !ruby/object:Gem::Version
82
- version: '0.21'
95
+ version: '1.0'
83
96
  - !ruby/object:Gem::Dependency
84
97
  name: yard
85
98
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +107,20 @@ dependencies:
94
107
  - - "~>"
95
108
  - !ruby/object:Gem::Version
96
109
  version: '0.9'
110
+ - !ruby/object:Gem::Dependency
111
+ name: logger
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
97
124
  - !ruby/object:Gem::Dependency
98
125
  name: os
99
126
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +141,20 @@ dependencies:
114
141
  - - ">="
115
142
  - !ruby/object:Gem::Version
116
143
  version: 1.1.1
144
+ - !ruby/object:Gem::Dependency
145
+ name: racc
146
+ requirement: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '1.8'
151
+ type: :runtime
152
+ prerelease: false
153
+ version_requirements: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '1.8'
117
158
  description: |
118
159
  Provide useful tools to analyze seccomp rules.
119
160
  Visit https://github.com/david942j/seccomp-tools for more details.
@@ -125,8 +166,13 @@ extensions:
125
166
  - ext/ptrace/extconf.rb
126
167
  extra_rdoc_files: []
127
168
  files:
169
+ - CHANGELOG.md
170
+ - LICENSE
128
171
  - README.md
129
172
  - bin/seccomp-tools
173
+ - completions/_seccomp-tools
174
+ - completions/seccomp-tools.bash
175
+ - completions/seccomp-tools.fish
130
176
  - ext/ptrace/extconf.rb
131
177
  - ext/ptrace/ptrace.c
132
178
  - lib/seccomp-tools.rb
@@ -138,24 +184,48 @@ files:
138
184
  - lib/seccomp-tools/asm/scanner.rb
139
185
  - lib/seccomp-tools/asm/statement.rb
140
186
  - lib/seccomp-tools/asm/token.rb
187
+ - lib/seccomp-tools/audit.rb
188
+ - lib/seccomp-tools/audit/catalog.rb
189
+ - lib/seccomp-tools/audit/checks.rb
190
+ - lib/seccomp-tools/audit/checks/arch_unchecked.rb
191
+ - lib/seccomp-tools/audit/checks/dangerous_allow.rb
192
+ - lib/seccomp-tools/audit/checks/orw_chain.rb
193
+ - lib/seccomp-tools/audit/checks/permissive_default.rb
194
+ - lib/seccomp-tools/audit/checks/syscall_alt_gap.rb
195
+ - lib/seccomp-tools/audit/checks/x32_guard.rb
196
+ - lib/seccomp-tools/audit/finding.rb
197
+ - lib/seccomp-tools/audit/policy.rb
198
+ - lib/seccomp-tools/audit/report.rb
141
199
  - lib/seccomp-tools/bpf.rb
142
200
  - lib/seccomp-tools/cli/asm.rb
201
+ - lib/seccomp-tools/cli/audit.rb
143
202
  - lib/seccomp-tools/cli/base.rb
144
203
  - lib/seccomp-tools/cli/cli.rb
204
+ - lib/seccomp-tools/cli/completion.rb
145
205
  - lib/seccomp-tools/cli/disasm.rb
146
206
  - lib/seccomp-tools/cli/dump.rb
207
+ - lib/seccomp-tools/cli/dumpable.rb
147
208
  - lib/seccomp-tools/cli/emu.rb
209
+ - lib/seccomp-tools/cli/explain.rb
210
+ - lib/seccomp-tools/cli/filter_input.rb
148
211
  - lib/seccomp-tools/const.rb
149
212
  - lib/seccomp-tools/consts/sys_arg.rb
150
213
  - lib/seccomp-tools/consts/sys_nr/aarch64.rb
151
214
  - lib/seccomp-tools/consts/sys_nr/amd64.rb
152
215
  - lib/seccomp-tools/consts/sys_nr/i386.rb
216
+ - lib/seccomp-tools/consts/sys_nr/riscv64.rb
153
217
  - lib/seccomp-tools/consts/sys_nr/s390x.rb
154
- - lib/seccomp-tools/disasm/context.rb
155
218
  - lib/seccomp-tools/disasm/disasm.rb
156
219
  - lib/seccomp-tools/dumper.rb
157
220
  - lib/seccomp-tools/emulator.rb
158
221
  - lib/seccomp-tools/error.rb
222
+ - lib/seccomp-tools/explain.rb
223
+ - lib/seccomp-tools/explain/analysis.rb
224
+ - lib/seccomp-tools/explain/path_facts.rb
225
+ - lib/seccomp-tools/explain/qword.rb
226
+ - lib/seccomp-tools/explain/renderer.rb
227
+ - lib/seccomp-tools/explain/summary.rb
228
+ - lib/seccomp-tools/explain/verdict.rb
159
229
  - lib/seccomp-tools/instruction/alu.rb
160
230
  - lib/seccomp-tools/instruction/base.rb
161
231
  - lib/seccomp-tools/instruction/instruction.rb
@@ -167,6 +237,10 @@ files:
167
237
  - lib/seccomp-tools/instruction/st.rb
168
238
  - lib/seccomp-tools/instruction/stx.rb
169
239
  - lib/seccomp-tools/logger.rb
240
+ - lib/seccomp-tools/symbolic/constraint.rb
241
+ - lib/seccomp-tools/symbolic/executor.rb
242
+ - lib/seccomp-tools/symbolic/expr.rb
243
+ - lib/seccomp-tools/symbolic/state.rb
170
244
  - lib/seccomp-tools/syscall.rb
171
245
  - lib/seccomp-tools/templates/asm.amd64.asm
172
246
  - lib/seccomp-tools/templates/asm.c
@@ -174,7 +248,6 @@ files:
174
248
  - lib/seccomp-tools/templates/asm.s390x.asm
175
249
  - lib/seccomp-tools/util.rb
176
250
  - lib/seccomp-tools/version.rb
177
- homepage:
178
251
  licenses:
179
252
  - MIT
180
253
  metadata:
@@ -182,7 +255,7 @@ metadata:
182
255
  documentation_uri: https://www.rubydoc.info/github/david942j/seccomp-tools/master
183
256
  homepage_uri: https://github.com/david942j/seccomp-tools
184
257
  source_code_uri: https://github.com/david942j/seccomp-tools
185
- post_install_message:
258
+ rubygems_mfa_required: 'true'
186
259
  rdoc_options: []
187
260
  require_paths:
188
261
  - lib
@@ -190,15 +263,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
190
263
  requirements:
191
264
  - - ">="
192
265
  - !ruby/object:Gem::Version
193
- version: '2.6'
266
+ version: '3.1'
194
267
  required_rubygems_version: !ruby/object:Gem::Requirement
195
268
  requirements:
196
269
  - - ">="
197
270
  - !ruby/object:Gem::Version
198
271
  version: '0'
199
272
  requirements: []
200
- rubygems_version: 3.1.6
201
- signing_key:
273
+ rubygems_version: 3.6.9
202
274
  specification_version: 4
203
275
  summary: seccomp-tools
204
276
  test_files: []
@@ -1,171 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module SeccompTools
4
- module Disasm
5
- # @private
6
- #
7
- # Context for disassembler to analyze.
8
- #
9
- # This class maintains:
10
- # * if +reg/mem+ can be one of +data[*]+
11
- # * if +data[0]+ (i.e. sys_number) is a known value
12
- class Context
13
- # @private
14
- #
15
- # Records the type and value.
16
- class Value
17
- attr_reader :val # @return [Integer]
18
-
19
- # @param [:imm, :data, :mem] rel
20
- # @param [Integer?] val
21
- def initialize(rel: :imm, val: nil)
22
- @rel = rel
23
- @val = val
24
- end
25
-
26
- # @return [Boolean]
27
- def data?
28
- @rel == :data
29
- end
30
-
31
- # @return [Boolean]
32
- def imm?
33
- @rel == :imm && @val.is_a?(Integer)
34
- end
35
-
36
- # Defines hash function.
37
- # @return [Integer]
38
- def hash
39
- @rel.hash ^ @val.hash
40
- end
41
-
42
- # Defines +eql?+.
43
- #
44
- # @param [Context::Value] other
45
- # @return [Boolean]
46
- def eql?(other)
47
- @val == other.val && @rel == other.instance_variable_get(:@rel)
48
- end
49
- end
50
-
51
- # @return [{Integer, Symbol => Context::Value}] Records reg and mem values.
52
- attr_reader :values
53
- # @return [Array<Integer?>] Records the known value of data.
54
- attr_reader :known_data
55
-
56
- # Instantiate a {Context} object.
57
- # @param [{Integer, Symbol => Context::Value?}] values
58
- # Value to be set to +reg/mem+.
59
- # @param [Array<Integer?>] known_data
60
- # Records which index of data is known.
61
- # It's used for tracking when the syscall number is known, which can be used to display argument names of the
62
- # syscall.
63
- def initialize(values: {}, known_data: [])
64
- @values = values
65
- 16.times { |i| @values[i] ||= Value.new(rel: :mem, val: i) } # make @values always has all keys
66
- @values[:a] ||= Value.new
67
- @values[:x] ||= Value.new
68
- @known_data = known_data
69
- end
70
-
71
- # Is used for the ld/ldx instructions.
72
- #
73
- # @param [#downcase, :a, :x] reg
74
- # Register to be set
75
- # @return [void]
76
- def load(reg, rel: nil, val: nil)
77
- reg = reg.downcase.to_sym
78
- values[reg] = if rel == :mem
79
- values[val]
80
- else
81
- Value.new(rel: rel, val: val)
82
- end
83
- end
84
-
85
- # Is used for the st/stx instructions.
86
- #
87
- # @param [Integer] idx
88
- # Index of +mem+ array.
89
- # @param [#downcase, :a, :x] reg
90
- # Register.
91
- #
92
- # @return [void]
93
- def store(idx, reg)
94
- raise RangeError, "Expect 0 <= idx < 16, got #{idx}." unless idx.between?(0, 15)
95
-
96
- values[idx] = values[reg.downcase.to_sym]
97
- end
98
-
99
- # Hints context that current value of register A equals to +val+.
100
- #
101
- # @param [Integer, :x] val
102
- # An immediate value or the symbol x.
103
- # @return [self]
104
- # Returns the object itself.
105
- def eql!(val)
106
- tap do
107
- # only cares when A is fetched from data
108
- next unless a.data?
109
- next known_data[a.val] = val if val.is_a?(Integer)
110
- # A == X, we can handle these cases:
111
- # * X is an immi
112
- # * X is a known data
113
- next unless x.data? || x.imm?
114
- next known_data[a.val] = x.val if x.imm?
115
-
116
- known_data[a.val] = known_data[x.val]
117
- end
118
- end
119
-
120
- # Implements a deep dup.
121
- # @return [Context]
122
- def dup
123
- Context.new(values: values.dup, known_data: known_data.dup)
124
- end
125
-
126
- # Register A.
127
- # @return [Context::Value]
128
- def a
129
- values[:a]
130
- end
131
-
132
- # Register X.
133
- # @return [Context::Value]
134
- def x
135
- values[:x]
136
- end
137
-
138
- # For conveniently get instance variable.
139
- # @param [String, Symbol, Integer] key
140
- # @return [Context::Value]
141
- def [](key)
142
- return values[key] if key.is_a?(Integer) # mem
143
-
144
- values[key.downcase.to_sym]
145
- end
146
-
147
- # For conveniently set an instance variable.
148
- # @param [#downcase, :a, :x] reg
149
- # Can be +'A', 'a', :a, 'X', 'x', :x+.
150
- # @param [Value] val
151
- # Value to set.
152
- # @return [void]
153
- def []=(reg, val)
154
- values[reg.downcase.to_sym] = val
155
- end
156
-
157
- # For +Set+ to compare two {Context} objects.
158
- # @param [Context] other
159
- # @return [Boolean]
160
- def eql?(other)
161
- values.eql?(other.values) && known_data.eql?(other.known_data)
162
- end
163
-
164
- # For +Set+ to get the hash value.
165
- # @return [Integer]
166
- def hash
167
- values.hash ^ known_data.hash
168
- end
169
- end
170
- end
171
- end