@@ -111,7 +111,7 @@ module LoadCommands
111111 # "reserved for internal use only", no public struct
112112 :LC_PREPAGE => "LoadCommand" ,
113113 :LC_DYSYMTAB => "DysymtabCommand" ,
114- :LC_LOAD_DYLIB => "DylibCommand " ,
114+ :LC_LOAD_DYLIB => "DylibUseCommand " ,
115115 :LC_ID_DYLIB => "DylibCommand" ,
116116 :LC_LOAD_DYLINKER => "DylinkerCommand" ,
117117 :LC_ID_DYLINKER => "DylinkerCommand" ,
@@ -123,7 +123,7 @@ module LoadCommands
123123 :LC_SUB_LIBRARY => "SubLibraryCommand" ,
124124 :LC_TWOLEVEL_HINTS => "TwolevelHintsCommand" ,
125125 :LC_PREBIND_CKSUM => "PrebindCksumCommand" ,
126- :LC_LOAD_WEAK_DYLIB => "DylibCommand " ,
126+ :LC_LOAD_WEAK_DYLIB => "DylibUseCommand " ,
127127 :LC_SEGMENT_64 => "SegmentCommand64" ,
128128 :LC_ROUTINES_64 => "RoutinesCommand64" ,
129129 :LC_UUID => "UUIDCommand" ,
@@ -195,6 +195,20 @@ module LoadCommands
195195 :SG_READ_ONLY => 0x10 ,
196196 } . freeze
197197
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+
198212 # The top-level Mach-O load command structure.
199213 #
200214 # This is the most generic load command -- only the type ID and size are
@@ -233,6 +247,13 @@ def self.create(cmd_sym, *args)
233247 # cmd will be filled in, view and cmdsize will be left unpopulated
234248 klass_arity = klass . min_args - 3
235249
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+
236257 raise LoadCommandCreationArityError . new ( cmd_sym , klass_arity , args . size ) if klass_arity > args . size
237258
238259 klass . new ( nil , cmd , nil , *args )
@@ -528,6 +549,23 @@ class DylibCommand < LoadCommand
528549 # @return [Integer] the library's compatibility version number
529550 field :compatibility_version , :uint32
530551
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+
531569 # @param context [SerializationContext]
532570 # the context
533571 # @return [String] the serialized fields of the load command
@@ -553,6 +591,65 @@ def to_h
553591 end
554592 end
555593
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+
556653 # A load command representing some aspect of the dynamic linker, depending
557654 # on filetype. Corresponds to LC_ID_DYLINKER, LC_LOAD_DYLINKER, and
558655 # LC_DYLD_ENVIRONMENT.
0 commit comments