@@ -111,7 +111,7 @@ module LoadCommands
111
111
# "reserved for internal use only", no public struct
112
112
:LC_PREPAGE => "LoadCommand" ,
113
113
:LC_DYSYMTAB => "DysymtabCommand" ,
114
- :LC_LOAD_DYLIB => "DylibCommand " ,
114
+ :LC_LOAD_DYLIB => "DylibUseCommand " ,
115
115
:LC_ID_DYLIB => "DylibCommand" ,
116
116
:LC_LOAD_DYLINKER => "DylinkerCommand" ,
117
117
:LC_ID_DYLINKER => "DylinkerCommand" ,
@@ -123,7 +123,7 @@ module LoadCommands
123
123
:LC_SUB_LIBRARY => "SubLibraryCommand" ,
124
124
:LC_TWOLEVEL_HINTS => "TwolevelHintsCommand" ,
125
125
:LC_PREBIND_CKSUM => "PrebindCksumCommand" ,
126
- :LC_LOAD_WEAK_DYLIB => "DylibCommand " ,
126
+ :LC_LOAD_WEAK_DYLIB => "DylibUseCommand " ,
127
127
:LC_SEGMENT_64 => "SegmentCommand64" ,
128
128
:LC_ROUTINES_64 => "RoutinesCommand64" ,
129
129
:LC_UUID => "UUIDCommand" ,
@@ -195,6 +195,20 @@ module LoadCommands
195
195
:SG_READ_ONLY => 0x10 ,
196
196
} . freeze
197
197
198
+ # association of dylib use flag symbols to values
199
+ # @api private
200
+ DYLIB_USE_FLAGS = {
201
+ :DYLIB_USE_WEAK_LINK => 0x1 ,
202
+ :DYLIB_USE_REEXPORT => 0x2 ,
203
+ :DYLIB_USE_UPWARD => 0x4 ,
204
+ :DYLIB_USE_DELAYED_INIT => 0x8 ,
205
+ } . freeze
206
+
207
+ # the marker used to denote a newer style dylib use command.
208
+ # the value is the timestamp 24 January 1984 18:12:16
209
+ # @api private
210
+ DYLIB_USE_MARKER = 0x1a741800
211
+
198
212
# The top-level Mach-O load command structure.
199
213
#
200
214
# This is the most generic load command -- only the type ID and size are
@@ -233,6 +247,13 @@ def self.create(cmd_sym, *args)
233
247
# cmd will be filled in, view and cmdsize will be left unpopulated
234
248
klass_arity = klass . min_args - 3
235
249
250
+ # macOS 15 introduces a new dylib load command that adds a flags field to the end.
251
+ # It uses the same commands with it dynamically being created if the dylib has a flags field
252
+ if klass == DylibUseCommand && ( args [ 1 ] != DYLIB_USE_MARKER || args . size <= DylibCommand . min_args - 3 )
253
+ klass = DylibCommand
254
+ klass_arity = klass . min_args - 3
255
+ end
256
+
236
257
raise LoadCommandCreationArityError . new ( cmd_sym , klass_arity , args . size ) if klass_arity > args . size
237
258
238
259
klass . new ( nil , cmd , nil , *args )
@@ -528,6 +549,23 @@ class DylibCommand < LoadCommand
528
549
# @return [Integer] the library's compatibility version number
529
550
field :compatibility_version , :uint32
530
551
552
+ # @example
553
+ # puts "this dylib is weakly loaded" if dylib_command.flag?(:DYLIB_USE_WEAK_LINK)
554
+ # @param flag [Symbol] a dylib use command flag symbol
555
+ # @return [Boolean] true if `flag` applies to this dylib command
556
+ def flag? ( flag )
557
+ case cmd
558
+ when LOAD_COMMAND_CONSTANTS [ :LC_LOAD_WEAK_DYLIB ]
559
+ flag == :DYLIB_USE_WEAK_LINK
560
+ when LOAD_COMMAND_CONSTANTS [ :LC_REEXPORT_DYLIB ]
561
+ flag == :DYLIB_USE_REEXPORT
562
+ when LOAD_COMMAND_CONSTANTS [ :LC_LOAD_UPWARD_DYLIB ]
563
+ flag == :DYLIB_USE_UPWARD
564
+ else
565
+ false
566
+ end
567
+ end
568
+
531
569
# @param context [SerializationContext]
532
570
# the context
533
571
# @return [String] the serialized fields of the load command
@@ -553,6 +591,65 @@ def to_h
553
591
end
554
592
end
555
593
594
+ # The newer format of load command representing some aspect of shared libraries,
595
+ # depending on filetype. Corresponds to LC_LOAD_DYLIB or LC_LOAD_WEAK_DYLIB.
596
+ class DylibUseCommand < DylibCommand
597
+ # @return [Integer] any flags associated with this dylib use command
598
+ field :flags , :uint32
599
+
600
+ alias marker timestamp
601
+
602
+ # Instantiates a new DylibCommand or DylibUseCommand.
603
+ # macOS 15 and later use a new format for dylib commands (DylibUseCommand),
604
+ # which is determined based on a special timestamp and the name offset.
605
+ # @param view [MachO::MachOView] the load command's raw view
606
+ # @return [DylibCommand] the new dylib load command
607
+ # @api private
608
+ def self . new_from_bin ( view )
609
+ dylib_command = DylibCommand . new_from_bin ( view )
610
+
611
+ if dylib_command . timestamp == DYLIB_USE_MARKER &&
612
+ dylib_command . name . to_i == DylibUseCommand . bytesize
613
+ super ( view )
614
+ else
615
+ dylib_command
616
+ end
617
+ end
618
+
619
+ # @example
620
+ # puts "this dylib is weakly loaded" if dylib_command.flag?(:DYLIB_USE_WEAK_LINK)
621
+ # @param flag [Symbol] a dylib use command flag symbol
622
+ # @return [Boolean] true if `flag` applies to this dylib command
623
+ def flag? ( flag )
624
+ flag = DYLIB_USE_FLAGS [ flag ]
625
+
626
+ return false if flag . nil?
627
+
628
+ flags & flag == flag
629
+ end
630
+
631
+ # @param context [SerializationContext]
632
+ # the context
633
+ # @return [String] the serialized fields of the load command
634
+ # @api private
635
+ def serialize ( context )
636
+ format = Utils . specialize_format ( self . class . format , context . endianness )
637
+ string_payload , string_offsets = Utils . pack_strings ( self . class . bytesize ,
638
+ context . alignment ,
639
+ :name => name . to_s )
640
+ cmdsize = self . class . bytesize + string_payload . bytesize
641
+ [ cmd , cmdsize , string_offsets [ :name ] , marker , current_version ,
642
+ compatibility_version , flags ] . pack ( format ) + string_payload
643
+ end
644
+
645
+ # @return [Hash] a hash representation of this {DylibUseCommand}
646
+ def to_h
647
+ {
648
+ "flags" => flags ,
649
+ } . merge super
650
+ end
651
+ end
652
+
556
653
# A load command representing some aspect of the dynamic linker, depending
557
654
# on filetype. Corresponds to LC_ID_DYLINKER, LC_LOAD_DYLINKER, and
558
655
# LC_DYLD_ENVIRONMENT.
0 commit comments