ruby-macho 1.4.0 → 2.0.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.
- checksums.yaml +4 -4
- data/lib/macho.rb +1 -1
- data/lib/macho/exceptions.rb +0 -10
- data/lib/macho/fat_file.rb +16 -36
- data/lib/macho/headers.rb +6 -78
- data/lib/macho/load_commands.rb +0 -5
- data/lib/macho/macho_file.rb +0 -1
- data/lib/macho/sections.rb +0 -2
- data/lib/macho/tools.rb +2 -3
- data/lib/macho/utils.rb +5 -19
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6341c1a29aeae7169fea7f1e7fc11339331f21c2b07a3a19e7f68b5dd76af03d
|
4
|
+
data.tar.gz: 9b5dd29c4a03c18bf6261e20641c95c584958ee142d4d64a37f9db8352065727
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c3964af02f0b1abf51de625e1d69a8fd966c30d5bf93cfde410ac127fab57864ae6331441d2afa1acdd100c7187f5d05883ecd8736d88076507823b16627ab10
|
7
|
+
data.tar.gz: c5a8ec50db69dccef0161dd96d6b7f23a47713a0fb8551d1bae10afa2690ed6837b52f9f3f49bd0826610b7c7a1841fadda2d1c53f6ab91f118732e36a3a4bd7
|
data/lib/macho.rb
CHANGED
@@ -12,7 +12,7 @@ require_relative "macho/tools"
|
|
12
12
|
# The primary namespace for ruby-macho.
|
13
13
|
module MachO
|
14
14
|
# release version
|
15
|
-
VERSION = "
|
15
|
+
VERSION = "2.0.0".freeze
|
16
16
|
|
17
17
|
# Opens the given filename as a MachOFile or FatFile, depending on its magic.
|
18
18
|
# @param filename [String] the file being opened
|
data/lib/macho/exceptions.rb
CHANGED
@@ -194,14 +194,4 @@ module MachO
|
|
194
194
|
super "Unimplemented: #{thing}"
|
195
195
|
end
|
196
196
|
end
|
197
|
-
|
198
|
-
# Raised when attempting to create a {FatFile} from one or more {MachOFile}s
|
199
|
-
# whose offsets will not fit within the resulting 32-bit {Headers::FatArch#offset} fields.
|
200
|
-
class FatArchOffsetOverflowError < MachOError
|
201
|
-
# @param offset [Integer] the offending offset
|
202
|
-
def initialize(offset)
|
203
|
-
super "Offset #{offset} exceeds the 32-bit width of a fat_arch offset." \
|
204
|
-
" Consider merging with `fat64: true`"
|
205
|
-
end
|
206
|
-
end
|
207
197
|
end
|
data/lib/macho/fat_file.rb
CHANGED
@@ -14,7 +14,7 @@ module MachO
|
|
14
14
|
# @return [Headers::FatHeader] the file's header
|
15
15
|
attr_reader :header
|
16
16
|
|
17
|
-
# @return [Array<Headers::FatArch
|
17
|
+
# @return [Array<Headers::FatArch>] an array of fat architectures
|
18
18
|
attr_reader :fat_archs
|
19
19
|
|
20
20
|
# @return [Array<MachOFile>] an array of Mach-O binaries
|
@@ -22,53 +22,37 @@ module MachO
|
|
22
22
|
|
23
23
|
# Creates a new FatFile from the given (single-arch) Mach-Os
|
24
24
|
# @param machos [Array<MachOFile>] the machos to combine
|
25
|
-
# @param fat64 [Boolean] whether to use {Headers::FatArch64}s to represent each slice
|
26
25
|
# @return [FatFile] a new FatFile containing the give machos
|
27
26
|
# @raise [ArgumentError] if less than one Mach-O is given
|
28
|
-
|
29
|
-
# in a 32-bit {Headers::FatArch} and `fat64` is `false`.
|
30
|
-
def self.new_from_machos(*machos, fat64: false)
|
27
|
+
def self.new_from_machos(*machos)
|
31
28
|
raise ArgumentError, "expected at least one Mach-O" if machos.empty?
|
32
29
|
|
33
|
-
fa_klass, magic = if fat64
|
34
|
-
[Headers::FatArch64, Headers::FAT_MAGIC_64]
|
35
|
-
else
|
36
|
-
[Headers::FatArch, Headers::FAT_MAGIC]
|
37
|
-
end
|
38
|
-
|
39
30
|
# put the smaller alignments further forwards in fat macho, so that we do less padding
|
40
31
|
machos = machos.sort_by(&:segment_alignment)
|
41
32
|
|
42
|
-
bin =
|
43
|
-
+""
|
44
|
-
else
|
45
|
-
""
|
46
|
-
end
|
33
|
+
bin = +""
|
47
34
|
|
48
|
-
bin << Headers::FatHeader.new(
|
49
|
-
offset = Headers::FatHeader.bytesize + (machos.size *
|
35
|
+
bin << Headers::FatHeader.new(Headers::FAT_MAGIC, machos.size).serialize
|
36
|
+
offset = Headers::FatHeader.bytesize + (machos.size * Headers::FatArch.bytesize)
|
50
37
|
|
51
38
|
macho_pads = {}
|
39
|
+
macho_bins = {}
|
52
40
|
|
53
41
|
machos.each do |macho|
|
54
|
-
macho_offset
|
55
|
-
|
56
|
-
if !fat64 && macho_offset > (2**32 - 1)
|
57
|
-
raise FatArchOffsetOverflowError, macho_offset
|
58
|
-
end
|
59
|
-
|
42
|
+
macho_offset = Utils.round(offset, 2**macho.segment_alignment)
|
60
43
|
macho_pads[macho] = Utils.padding_for(offset, 2**macho.segment_alignment)
|
44
|
+
macho_bins[macho] = macho.serialize
|
61
45
|
|
62
|
-
bin <<
|
63
|
-
|
64
|
-
|
46
|
+
bin << Headers::FatArch.new(macho.header.cputype, macho.header.cpusubtype,
|
47
|
+
macho_offset, macho_bins[macho].bytesize,
|
48
|
+
macho.segment_alignment).serialize
|
65
49
|
|
66
|
-
offset += (macho.
|
50
|
+
offset += (macho_bins[macho].bytesize + macho_pads[macho])
|
67
51
|
end
|
68
52
|
|
69
53
|
machos.each do |macho|
|
70
54
|
bin << Utils.nullpad(macho_pads[macho])
|
71
|
-
bin << macho
|
55
|
+
bin << macho_bins[macho]
|
72
56
|
end
|
73
57
|
|
74
58
|
new_from_bin(bin)
|
@@ -294,7 +278,6 @@ module MachO
|
|
294
278
|
# @note Overwrites all data in the file!
|
295
279
|
def write!
|
296
280
|
raise MachOError, "no initial file to write to" if filename.nil?
|
297
|
-
|
298
281
|
File.open(@filename, "wb") { |f| f.write(@raw_data) }
|
299
282
|
end
|
300
283
|
|
@@ -344,12 +327,10 @@ module MachO
|
|
344
327
|
def populate_fat_archs
|
345
328
|
archs = []
|
346
329
|
|
347
|
-
|
348
|
-
|
349
|
-
fa_len = fa_klass.bytesize
|
350
|
-
|
330
|
+
fa_off = Headers::FatHeader.bytesize
|
331
|
+
fa_len = Headers::FatArch.bytesize
|
351
332
|
header.nfat_arch.times do |i|
|
352
|
-
archs <<
|
333
|
+
archs << Headers::FatArch.new_from_bin(:big, @raw_data[fa_off + (fa_len * i), fa_len])
|
353
334
|
end
|
354
335
|
|
355
336
|
archs
|
@@ -399,7 +380,6 @@ module MachO
|
|
399
380
|
|
400
381
|
# Strict mode: Immediately re-raise. Otherwise: Retain, check later.
|
401
382
|
raise error if strict
|
402
|
-
|
403
383
|
errors << error
|
404
384
|
end
|
405
385
|
end
|
data/lib/macho/headers.rb
CHANGED
@@ -6,19 +6,11 @@ module MachO
|
|
6
6
|
FAT_MAGIC = 0xcafebabe
|
7
7
|
|
8
8
|
# little-endian fat magic
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# this is defined, but should never appear in ruby-macho code because
|
10
|
+
# fat headers are always big-endian and therefore always unpacked as such.
|
11
11
|
# @api private
|
12
12
|
FAT_CIGAM = 0xbebafeca
|
13
13
|
|
14
|
-
# 64-bit big-endian fat magic
|
15
|
-
FAT_MAGIC_64 = 0xcafebabf
|
16
|
-
|
17
|
-
# 64-bit little-endian fat magic
|
18
|
-
# @note This is defined for completeness, but should never appear in ruby-macho code,
|
19
|
-
# since fat headers are always big-endian.
|
20
|
-
FAT_CIGAM_64 = 0xbfbafeca
|
21
|
-
|
22
14
|
# 32-bit big-endian magic
|
23
15
|
# @api private
|
24
16
|
MH_MAGIC = 0xfeedface
|
@@ -39,7 +31,6 @@ module MachO
|
|
39
31
|
# @api private
|
40
32
|
MH_MAGICS = {
|
41
33
|
FAT_MAGIC => "FAT_MAGIC",
|
42
|
-
FAT_MAGIC_64 => "FAT_MAGIC_64",
|
43
34
|
MH_MAGIC => "MH_MAGIC",
|
44
35
|
MH_CIGAM => "MH_CIGAM",
|
45
36
|
MH_MAGIC_64 => "MH_MAGIC_64",
|
@@ -50,11 +41,6 @@ module MachO
|
|
50
41
|
# @api private
|
51
42
|
CPU_ARCH_ABI64 = 0x01000000
|
52
43
|
|
53
|
-
# mask for CPUs with 64-bit architectures (when running a 32-bit ABI?)
|
54
|
-
# @see https://github.com/Homebrew/ruby-macho/issues/113
|
55
|
-
# @api private
|
56
|
-
CPU_ARCH_ABI32 = 0x02000000
|
57
|
-
|
58
44
|
# any CPU (unused?)
|
59
45
|
# @api private
|
60
46
|
CPU_TYPE_ANY = -1
|
@@ -83,10 +69,6 @@ module MachO
|
|
83
69
|
# @api private
|
84
70
|
CPU_TYPE_ARM64 = (CPU_TYPE_ARM | CPU_ARCH_ABI64)
|
85
71
|
|
86
|
-
# 64-bit ARM compatible CPUs (running in 32-bit mode?)
|
87
|
-
# @see https://github.com/Homebrew/ruby-macho/issues/113
|
88
|
-
CPU_TYPE_ARM64_32 = (CPU_TYPE_ARM | CPU_ARCH_ABI32)
|
89
|
-
|
90
72
|
# PowerPC compatible CPUs
|
91
73
|
# @api private
|
92
74
|
CPU_TYPE_POWERPC = 0x12
|
@@ -103,7 +85,6 @@ module MachO
|
|
103
85
|
CPU_TYPE_X86_64 => :x86_64,
|
104
86
|
CPU_TYPE_ARM => :arm,
|
105
87
|
CPU_TYPE_ARM64 => :arm64,
|
106
|
-
CPU_TYPE_ARM64_32 => :arm64_32,
|
107
88
|
CPU_TYPE_POWERPC => :ppc,
|
108
89
|
CPU_TYPE_POWERPC64 => :ppc64,
|
109
90
|
}.freeze
|
@@ -237,14 +218,6 @@ module MachO
|
|
237
218
|
# @api private
|
238
219
|
CPU_SUBTYPE_ARM64_V8 = 1
|
239
220
|
|
240
|
-
# the v8 sub-type for `CPU_TYPE_ARM64_32`
|
241
|
-
# @api private
|
242
|
-
CPU_SUBTYPE_ARM64_32_V8 = 1
|
243
|
-
|
244
|
-
# the e (A12) sub-type for `CPU_TYPE_ARM64`
|
245
|
-
# @api private
|
246
|
-
CPU_SUBTYPE_ARM64E = 2
|
247
|
-
|
248
221
|
# the lowest common sub-type for `CPU_TYPE_MC88000`
|
249
222
|
# @api private
|
250
223
|
CPU_SUBTYPE_MC88000_ALL = 0
|
@@ -354,10 +327,6 @@ module MachO
|
|
354
327
|
CPU_TYPE_ARM64 => {
|
355
328
|
CPU_SUBTYPE_ARM64_ALL => :arm64,
|
356
329
|
CPU_SUBTYPE_ARM64_V8 => :arm64v8,
|
357
|
-
CPU_SUBTYPE_ARM64E => :arm64e,
|
358
|
-
}.freeze,
|
359
|
-
CPU_TYPE_ARM64_32 => {
|
360
|
-
CPU_SUBTYPE_ARM64_32_V8 => :arm64_32v8,
|
361
330
|
}.freeze,
|
362
331
|
CPU_TYPE_POWERPC => {
|
363
332
|
CPU_SUBTYPE_POWERPC_ALL => :ppc,
|
@@ -517,10 +486,8 @@ module MachO
|
|
517
486
|
end
|
518
487
|
end
|
519
488
|
|
520
|
-
#
|
521
|
-
#
|
522
|
-
# @note "32-bit" indicates the fact that this structure stores 32-bit offsets, not that the
|
523
|
-
# Mach-Os that it points to necessarily *are* 32-bit.
|
489
|
+
# Fat binary header architecture structure. A Fat binary has one or more of
|
490
|
+
# these, representing one or more internal Mach-O blobs.
|
524
491
|
# @see MachO::Headers::FatHeader
|
525
492
|
class FatArch < MachOStructure
|
526
493
|
# @return [Integer] the CPU type of the Mach-O
|
@@ -538,10 +505,10 @@ module MachO
|
|
538
505
|
# @return [Integer] the alignment, as a power of 2
|
539
506
|
attr_reader :align
|
540
507
|
|
541
|
-
#
|
508
|
+
# always big-endian
|
542
509
|
# @see MachOStructure::FORMAT
|
543
510
|
# @api private
|
544
|
-
FORMAT = "
|
511
|
+
FORMAT = "N5".freeze
|
545
512
|
|
546
513
|
# @see MachOStructure::SIZEOF
|
547
514
|
# @api private
|
@@ -575,43 +542,6 @@ module MachO
|
|
575
542
|
end
|
576
543
|
end
|
577
544
|
|
578
|
-
# 64-bit fat binary header architecture structure. A 64-bit fat Mach-O has one or more of
|
579
|
-
# these, indicating one or more internal Mach-O blobs.
|
580
|
-
# @note "64-bit" indicates the fact that this structure stores 64-bit offsets, not that the
|
581
|
-
# Mach-Os that it points to necessarily *are* 64-bit.
|
582
|
-
# @see MachO::Headers::FatHeader
|
583
|
-
class FatArch64 < FatArch
|
584
|
-
# @return [void]
|
585
|
-
attr_reader :reserved
|
586
|
-
|
587
|
-
# @note Always big endian.
|
588
|
-
# @see MachOStructure::FORMAT
|
589
|
-
# @api private
|
590
|
-
FORMAT = "L>2Q>2L>2".freeze
|
591
|
-
|
592
|
-
# @see MachOStructure::SIZEOF
|
593
|
-
# @api private
|
594
|
-
SIZEOF = 32
|
595
|
-
|
596
|
-
# @api private
|
597
|
-
def initialize(cputype, cpusubtype, offset, size, align, reserved = 0)
|
598
|
-
super(cputype, cpusubtype, offset, size, align)
|
599
|
-
@reserved = reserved
|
600
|
-
end
|
601
|
-
|
602
|
-
# @return [String] the serialized fields of the fat arch
|
603
|
-
def serialize
|
604
|
-
[cputype, cpusubtype, offset, size, align, reserved].pack(FORMAT)
|
605
|
-
end
|
606
|
-
|
607
|
-
# @return [Hash] a hash representation of this {FatArch64}
|
608
|
-
def to_h
|
609
|
-
{
|
610
|
-
"reserved" => reserved,
|
611
|
-
}.merge super
|
612
|
-
end
|
613
|
-
end
|
614
|
-
|
615
545
|
# 32-bit Mach-O file header structure
|
616
546
|
class MachHeader < MachOStructure
|
617
547
|
# @return [Integer] the magic number
|
@@ -663,9 +593,7 @@ module MachO
|
|
663
593
|
# @return [Boolean] true if `flag` is present in the header's flag section
|
664
594
|
def flag?(flag)
|
665
595
|
flag = MH_FLAGS[flag]
|
666
|
-
|
667
596
|
return false if flag.nil?
|
668
|
-
|
669
597
|
flags & flag == flag
|
670
598
|
end
|
671
599
|
|
data/lib/macho/load_commands.rb
CHANGED
@@ -242,7 +242,6 @@ module MachO
|
|
242
242
|
# @api private
|
243
243
|
def serialize(context)
|
244
244
|
raise LoadCommandNotSerializableError, LOAD_COMMANDS[cmd] unless serializable?
|
245
|
-
|
246
245
|
format = Utils.specialize_format(FORMAT, context.endianness)
|
247
246
|
[cmd, SIZEOF].pack(format)
|
248
247
|
end
|
@@ -299,9 +298,7 @@ module MachO
|
|
299
298
|
lc_end = view.offset + lc.cmdsize - 1
|
300
299
|
raw_string = view.raw_data.slice(lc_str_abs..lc_end)
|
301
300
|
@string, null_byte, _padding = raw_string.partition("\x00")
|
302
|
-
|
303
301
|
raise LCStrMalformedError, lc if null_byte.empty?
|
304
|
-
|
305
302
|
@string_offset = lc_str
|
306
303
|
else
|
307
304
|
@string = lc_str
|
@@ -476,9 +473,7 @@ module MachO
|
|
476
473
|
# @return [Boolean] true if `flag` is present in the segment's flag field
|
477
474
|
def flag?(flag)
|
478
475
|
flag = SEGMENT_FLAGS[flag]
|
479
|
-
|
480
476
|
return false if flag.nil?
|
481
|
-
|
482
477
|
flags & flag == flag
|
483
478
|
end
|
484
479
|
|
data/lib/macho/macho_file.rb
CHANGED
data/lib/macho/sections.rb
CHANGED
data/lib/macho/tools.rb
CHANGED
@@ -89,9 +89,8 @@ module MachO
|
|
89
89
|
# Merge multiple Mach-Os into one universal (Fat) binary.
|
90
90
|
# @param filename [String] the fat binary to create
|
91
91
|
# @param files [Array<String>] the files to merge
|
92
|
-
# @param fat64 [Boolean] whether to use {Headers::FatArch64}s to represent each slice
|
93
92
|
# @return [void]
|
94
|
-
def self.merge_machos(filename, *files
|
93
|
+
def self.merge_machos(filename, *files)
|
95
94
|
machos = files.map do |file|
|
96
95
|
macho = MachO.open(file)
|
97
96
|
case macho
|
@@ -102,7 +101,7 @@ module MachO
|
|
102
101
|
end
|
103
102
|
end.flatten
|
104
103
|
|
105
|
-
fat_macho = MachO::FatFile.new_from_machos(*machos
|
104
|
+
fat_macho = MachO::FatFile.new_from_machos(*machos)
|
106
105
|
fat_macho.write(filename)
|
107
106
|
end
|
108
107
|
end
|
data/lib/macho/utils.rb
CHANGED
@@ -27,7 +27,7 @@ module MachO
|
|
27
27
|
# @return [String] the null string (or empty string, for `size = 0`)
|
28
28
|
# @raise [ArgumentError] if a non-positive nullpad is requested
|
29
29
|
def self.nullpad(size)
|
30
|
-
raise ArgumentError, "size < 0: #{size}" if size
|
30
|
+
raise ArgumentError, "size < 0: #{size}" if size.negative?
|
31
31
|
|
32
32
|
"\x00" * size
|
33
33
|
end
|
@@ -75,49 +75,35 @@ module MachO
|
|
75
75
|
# @param num [Integer] the number being checked
|
76
76
|
# @return [Boolean] whether `num` is a valid Fat magic number
|
77
77
|
def self.fat_magic?(num)
|
78
|
-
[Headers::FAT_MAGIC, Headers::FAT_MAGIC_64].include? num
|
79
|
-
end
|
80
|
-
|
81
|
-
# Compares the given number to valid 32-bit Fat magic numbers.
|
82
|
-
# @param num [Integer] the number being checked
|
83
|
-
# @return [Boolean] whether `num` is a valid 32-bit fat magic number
|
84
|
-
def self.fat_magic32?(num)
|
85
78
|
num == Headers::FAT_MAGIC
|
86
79
|
end
|
87
80
|
|
88
|
-
# Compares the given number to valid 64-bit Fat magic numbers.
|
89
|
-
# @param num [Integer] the number being checked
|
90
|
-
# @return [Boolean] whether `num` is a valid 64-bit fat magic number
|
91
|
-
def self.fat_magic64?(num)
|
92
|
-
num == Headers::FAT_MAGIC_64
|
93
|
-
end
|
94
|
-
|
95
81
|
# Compares the given number to valid 32-bit Mach-O magic numbers.
|
96
82
|
# @param num [Integer] the number being checked
|
97
83
|
# @return [Boolean] whether `num` is a valid 32-bit magic number
|
98
84
|
def self.magic32?(num)
|
99
|
-
|
85
|
+
num == Headers::MH_MAGIC || num == Headers::MH_CIGAM
|
100
86
|
end
|
101
87
|
|
102
88
|
# Compares the given number to valid 64-bit Mach-O magic numbers.
|
103
89
|
# @param num [Integer] the number being checked
|
104
90
|
# @return [Boolean] whether `num` is a valid 64-bit magic number
|
105
91
|
def self.magic64?(num)
|
106
|
-
|
92
|
+
num == Headers::MH_MAGIC_64 || num == Headers::MH_CIGAM_64
|
107
93
|
end
|
108
94
|
|
109
95
|
# Compares the given number to valid little-endian magic numbers.
|
110
96
|
# @param num [Integer] the number being checked
|
111
97
|
# @return [Boolean] whether `num` is a valid little-endian magic number
|
112
98
|
def self.little_magic?(num)
|
113
|
-
|
99
|
+
num == Headers::MH_CIGAM || num == Headers::MH_CIGAM_64
|
114
100
|
end
|
115
101
|
|
116
102
|
# Compares the given number to valid big-endian magic numbers.
|
117
103
|
# @param num [Integer] the number being checked
|
118
104
|
# @return [Boolean] whether `num` is a valid big-endian magic number
|
119
105
|
def self.big_magic?(num)
|
120
|
-
|
106
|
+
num == Headers::MH_CIGAM || num == Headers::MH_CIGAM_64
|
121
107
|
end
|
122
108
|
end
|
123
109
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-macho
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Woodruff
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-07-03 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A library for viewing and manipulating Mach-O files in Ruby.
|
14
14
|
email: william@yossarian.net
|
@@ -42,14 +42,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
42
42
|
requirements:
|
43
43
|
- - ">="
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: '2.
|
45
|
+
version: '2.1'
|
46
46
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
47
|
requirements:
|
48
48
|
- - ">="
|
49
49
|
- !ruby/object:Gem::Version
|
50
50
|
version: '0'
|
51
51
|
requirements: []
|
52
|
-
|
52
|
+
rubyforge_project:
|
53
|
+
rubygems_version: 2.7.6
|
53
54
|
signing_key:
|
54
55
|
specification_version: 4
|
55
56
|
summary: ruby-macho - Mach-O file analyzer.
|