patchelf 1.5.1 → 1.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e6864f1200743dc0168be4dfba4f26745189fd922a8c8bee181de7a25be206a1
4
- data.tar.gz: c5f80780027865b91dd5f4ab32fd4f24aa04c89e38d73c65190de7cbd529620f
3
+ metadata.gz: 64cae3b6070bf26b6716a82886fcb183f20213bf0c396501141e93c1e7951ffa
4
+ data.tar.gz: 49793e2b2bf154c0d4ff70f1533130495a0161db418248fc39105997159c59c8
5
5
  SHA512:
6
- metadata.gz: d68242284575b512eba346ceb18bf6d399a50ab3bf6da6a5dcea1a69fc7eccbc20d08d502e9f13e54756aa914a38e82539d05256336b3999350567981ca46455
7
- data.tar.gz: 67757566f0d8861cd91efb6d5cab270a64f2e09920c94c099bf0bf9cb5ae57b87ff696f4fe8e1418575a6a4f39e0b0036a9aaedcdcd05a473303cd892263e8e3
6
+ metadata.gz: d6b6dc9f7caa90f2abe18013a778e4ede2206f848e377e3cfce635b83ffc75b4dbc66c9b318899040bffa9553e8f0820cab95f92af445adc783efbbe47e0395f
7
+ data.tar.gz: b9566cc7487e8225e07f5ea7a29698e1d280969fde9f93ef0c0b1cd08d11595e92243883ed6123ef3918c1776003939c16a49d89aed6fc5da7a685e92a4a6a5a
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2019 david942j
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
- [![Build Status](https://github.com/david942j/patchelf.rb/workflows/build/badge.svg)](https://github.com/david942j/patchelf.rb/actions)
2
- [![Dependabot Status](https://api.dependabot.com/badges/status?host=github&repo=david942j/patchelf.rb)](https://dependabot.com)
3
- [![Code Climate](https://codeclimate.com/github/david942j/patchelf.rb/badges/gpa.svg)](https://codeclimate.com/github/david942j/patchelf.rb)
4
- [![Issue Count](https://codeclimate.com/github/david942j/patchelf.rb/badges/issue_count.svg)](https://codeclimate.com/github/david942j/patchelf.rb)
5
- [![Test Coverage](https://codeclimate.com/github/david942j/patchelf.rb/badges/coverage.svg)](https://codeclimate.com/github/david942j/patchelf.rb/coverage)
6
- [![Inline docs](https://inch-ci.org/github/david942j/patchelf.rb.svg?branch=master)](https://inch-ci.org/github/david942j/patchelf.rb)
7
- [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](http://choosealicense.com/licenses/mit/)
1
+ [![Downloads](https://img.shields.io/endpoint?url=https://gem-badge-h3lg.onrender.com/downloads/patchelf)](https://rubygems.org/gems/patchelf)
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/patchelf.svg)](https://badge.fury.io/rb/patchelf)
4
+ [![CI](https://github.com/Homebrew/patchelf.rb/actions/workflows/tests.yml/badge.svg)](https://github.com/Homebrew/patchelf.rb/actions/workflows/tests.yml)
5
+ [![Coverage Status](https://codecov.io/gh/Homebrew/patchelf.rb/graph/badge.svg)](https://codecov.io/gh/Homebrew/patchelf.rb)
6
+ [![Yard Docs](https://img.shields.io/badge/yard-docs-blue.svg)](https://www.rubydoc.info/gems/patchelf)
7
+ [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](https://choosealicense.com/licenses/mit/)
8
8
 
9
9
  # patchelf.rb
10
10
 
@@ -135,3 +135,16 @@ patcher.save('ls.patch')
135
135
  ## Environment
136
136
 
137
137
  patchelf.rb is implemented in pure Ruby, so it should work in all environments include Linux, macOS, and Windows!
138
+
139
+ ## Project Authors
140
+
141
+ The git history remains the definitive record of all contributions.
142
+
143
+ ### Original Author and Project Founder
144
+
145
+ * david942j (@david942j)
146
+
147
+ ### Maintainers
148
+
149
+ * Homebrew maintainers
150
+ * david942j (@david942j)
@@ -5,6 +5,7 @@ require 'elftools/elf_file'
5
5
  require 'elftools/structs'
6
6
  require 'elftools/util'
7
7
  require 'fileutils'
8
+ require 'objspace'
8
9
 
9
10
  require 'patchelf/helper'
10
11
 
@@ -57,7 +58,7 @@ module PatchELF
57
58
  @out_file = out_file
58
59
  @set = set
59
60
 
60
- f = File.open(in_file, 'rb')
61
+ f = File.open(in_file, 'rb') # rubocop:disable Style/FileOpen
61
62
  # the +@buffer+ and +@elf+ both could work on same +StringIO+ stream,
62
63
  # the updating of @buffer in place blocks us from looking up old values.
63
64
  # TODO: cache the values needed later, use same stream for +@buffer+ and +@elf+.
@@ -65,6 +66,9 @@ module PatchELF
65
66
  @elf = ELFTools::ELFFile.new(f)
66
67
  @buffer = StringIO.new(f.tap(&:rewind).read) # StringIO makes easier to work with Bindata
67
68
 
69
+ # Ensure file is closed when the {AltSaver} object is garbage collected.
70
+ ObjectSpace.define_finalizer(self, Helper.close_file_proc(f))
71
+
68
72
  @ehdr = @elf.header
69
73
  @endian = @elf.endian
70
74
  @elf_class = @elf.elf_class
@@ -126,14 +130,12 @@ module PatchELF
126
130
  find_section '.dynstr'
127
131
  end
128
132
 
129
- # yields dynamic tag, and offset in buffer
133
+ # Yields dynamic tag, and its offset in @buffer.
134
+ # @yieldparam [ELFTools::Structs::ELF_Dyn] dyn
135
+ # @yieldparam [Integer] offset The offset of this dynamic tag within +@buffer+.
130
136
  def each_dynamic_tags
131
- return unless block_given?
132
-
133
- sec = find_section '.dynamic'
134
- return unless sec
135
-
136
- return if sec.header.sh_type == ELFTools::Constants::SHT_NOBITS
137
+ sec = find_section('.dynamic')
138
+ return if sec.nil? || sec.header.sh_type == ELFTools::Constants::SHT_NOBITS
137
139
 
138
140
  shdr = sec.header
139
141
  with_buf_at(shdr.sh_offset) do |buf|
@@ -145,8 +147,8 @@ module PatchELF
145
147
  break if dyn.d_tag == ELFTools::Constants::DT_NULL
146
148
 
147
149
  yield dyn, buf_dyn_offset
148
- # there's a possibility for caller to modify @buffer.pos, seek to avoid such issues
149
- buf.seek buf_dyn_offset + dyn.num_bytes
150
+ # It's possible the caller may modify @buffer.pos, seek to ensure it points to the next tag.
151
+ buf.seek(buf_dyn_offset + dyn.num_bytes)
150
152
  end
151
153
  end
152
154
  end
@@ -575,7 +577,7 @@ module PatchELF
575
577
 
576
578
  cur_off = ehdr.num_bytes + (@segments.count * seg_num_bytes)
577
579
  Logger.debug "clearing first #{start_offset - cur_off} bytes"
578
- with_buf_at(cur_off) { |buf| buf.fill("\x00", (start_offset - cur_off)) }
580
+ with_buf_at(cur_off) { |buf| buf.fill("\x00", start_offset - cur_off) }
579
581
 
580
582
  cur_off = write_replaced_sections cur_off, first_page, 0
581
583
  raise PatchError, "cur_off(#{cur_off}) != needed_space" if cur_off != needed_space
@@ -671,18 +673,13 @@ module PatchELF
671
673
  new_phdrs = []
672
674
 
673
675
  while curr_off < end_off
674
- size = 0
675
- sections_at_aligned_offset(curr_off) do |sec|
676
- next if sec.type != ELFTools::Constants::SHT_NOTE
676
+ section = sections_at_aligned_offset(curr_off).find { |sec| sec.type == ELFTools::Constants::SHT_NOTE }
677
+ raise PatchError, 'cannot normalize PT_NOTE segment: non-contiguous SHT_NOTE sections' if section.nil?
677
678
 
678
- size = sec.header.sh_size.to_i
679
- curr_off = sec.header.sh_offset.to_i
680
- break
681
- end
679
+ size = section.header.sh_size.to_i
680
+ curr_off = section.header.sh_offset.to_i
682
681
 
683
- raise PatchError, 'cannot normalize PT_NOTE segment: non-contiguous SHT_NOTE sections' if size.zero?
684
-
685
- if curr_off + size > end_off
682
+ if size.zero? || curr_off + size > end_off
686
683
  raise PatchError, 'cannot normalize PT_NOTE segment: partially mapped SHT_NOTE section'
687
684
  end
688
685
 
@@ -706,6 +703,8 @@ module PatchELF
706
703
  end
707
704
 
708
705
  def sections_at_aligned_offset(offset)
706
+ return to_enum(__method__, offset) unless block_given?
707
+
709
708
  @sections.each do |sec|
710
709
  shdr = sec.header
711
710
 
@@ -739,7 +738,6 @@ module PatchELF
739
738
  phdr.p_vaddr -= shift if phdr.p_vaddr > shift
740
739
  end
741
740
 
742
- # rubocop:disable Metrics/PerceivedComplexity
743
741
  def shift_segments(shift, start_offset)
744
742
  split_index = -1
745
743
  split_shift = 0
@@ -748,9 +746,8 @@ module PatchELF
748
746
  phdr = seg.header
749
747
  p_start = phdr.p_offset
750
748
 
751
- if p_start <= start_offset && p_start + phdr.p_filesz > start_offset &&
752
- phdr.p_type == ELFTools::Constants::PT_LOAD
753
- raise PatchError, "split_index(#{split_index}) != -1" if split_index != -1
749
+ if (p_start...(p_start + phdr.p_filesz)).cover?(start_offset) && phdr.p_type == ELFTools::Constants::PT_LOAD
750
+ raise PatchError, 'PT_LOAD segments overlapped, unable to shift segments' if split_index != -1
754
751
 
755
752
  split_index = idx
756
753
  split_shift = start_offset - p_start
@@ -771,11 +768,10 @@ module PatchELF
771
768
  end
772
769
  end
773
770
 
774
- raise PatchError, "split_index(#{split_index}) == -1" if split_index == -1
771
+ raise PatchError, "No PT_LOAD found covers offset 0x#{start_offset.to_s(16)}" if split_index == -1
775
772
 
776
773
  [split_index, split_shift]
777
774
  end
778
- # rubocop:enable Metrics/PerceivedComplexity
779
775
 
780
776
  def shift_file(extra_pages, start_offset, extra_bytes)
781
777
  raise PatchError, "start_offset(#{start_offset}) < ehdr.num_bytes" if start_offset < ehdr.num_bytes
@@ -861,9 +857,9 @@ module PatchELF
861
857
  sec_name
862
858
  end
863
859
 
864
- # given a +dyn.d_tag+, returns the section name it must be synced to.
865
- # it may return nil, when given tag maps to no section,
866
- # or when its okay to skip if section is not found.
860
+ # Given a +dyn.d_tag+, returns the section name it must be synced to.
861
+ # Returns +nil+ when given tag maps to no section, or when its okay to skip if section is not found.
862
+ # @return [String?]
867
863
  def dyn_tag_to_section_name(d_tag)
868
864
  case d_tag
869
865
  when ELFTools::Constants::DT_STRTAB, ELFTools::Constants::DT_STRSZ
@@ -897,32 +893,37 @@ module PatchELF
897
893
  end
898
894
  end
899
895
 
896
+ # @return [ELFTools::Structs::ELF_Shdr?]
897
+ def dyn_tag_to_shdr(d_tag)
898
+ sec_name = dyn_tag_to_section_name(d_tag)
899
+ return if sec_name.nil?
900
+
901
+ find_section(sec_name)&.header
902
+ end
903
+
900
904
  # updates dyn tags by syncing it with @section values
901
905
  def sync_dyn_tags!
906
+ # Position of the fist dynamic tag.
902
907
  dyn_table_offset = nil
903
908
  each_dynamic_tags do |dyn, buf_off|
904
909
  dyn_table_offset ||= buf_off
905
910
 
906
- sec_name = dyn_tag_to_section_name(dyn.d_tag)
907
-
908
- unless sec_name
909
- if dyn.d_tag == ELFTools::Constants::DT_MIPS_RLD_MAP_REL && ehdr.e_machine == ELFTools::Constants::EM_MIPS
910
- rld_map = find_section('.rld_map')
911
- dyn.d_val = if rld_map
912
- rld_map.header.sh_addr.to_i - (buf_off - dyn_table_offset) -
913
- find_section('.dynamic').header.sh_addr.to_i
914
- else
915
- Logger.warn 'DT_MIPS_RLD_MAP_REL entry is present, but .rld_map section is not'
916
- 0
917
- end
918
- end
911
+ if dyn.d_tag == ELFTools::Constants::DT_MIPS_RLD_MAP_REL
912
+ rld_map = find_section('.rld_map')
913
+ dyn.d_val = if rld_map
914
+ rld_map.header.sh_addr.to_i - (buf_off - dyn_table_offset) -
915
+ find_section('.dynamic').header.sh_addr.to_i
916
+ else
917
+ Logger.warn 'DT_MIPS_RLD_MAP_REL entry is present, but .rld_map section is not'
918
+ 0
919
+ end
920
+ else
921
+ shdr = dyn_tag_to_shdr(dyn.d_tag)
922
+ next if shdr.nil?
919
923
 
920
- next
924
+ dyn.d_val = dyn.d_tag == ELFTools::Constants::DT_STRSZ ? shdr.sh_size.to_i : shdr.sh_addr.to_i
921
925
  end
922
926
 
923
- shdr = find_section(sec_name).header
924
- dyn.d_val = dyn.d_tag == ELFTools::Constants::DT_STRSZ ? shdr.sh_size.to_i : shdr.sh_addr.to_i
925
-
926
927
  with_buf_at(buf_off) { |wbuf| dyn.write(wbuf) }
927
928
  end
928
929
  end
@@ -941,6 +942,8 @@ module PatchELF
941
942
  nil
942
943
  end
943
944
 
945
+ # Some sections have their corresponding segment.
946
+ # Use this utility when a section is being patched so its segment should be updated with the same values.
944
947
  def sync_sec_to_seg(shdr, phdr)
945
948
  phdr.p_offset = shdr.sh_offset.to_i
946
949
  phdr.p_vaddr = phdr.p_paddr = shdr.sh_addr.to_i
@@ -958,6 +961,8 @@ module PatchELF
958
961
  end
959
962
 
960
963
  # Returns a blank shdr if the section doesn't exist.
964
+ #
965
+ # @return [ELFTools::Structs::ELF_Shdr]
961
966
  def find_or_create_section_header(rsec_name)
962
967
  shdr = find_section(rsec_name)&.header
963
968
  shdr ||= ELFTools::Structs::ELF_Shdr.new(endian: endian, elf_class: elf_class)
@@ -987,11 +992,54 @@ module PatchELF
987
992
  (s_start >= p_start && s_start < p_end) || (s_end > p_start && s_end <= p_end)
988
993
  end
989
994
 
995
+ # Used when patching sections.
996
+ # For sections with a correspondence segment, update the segment values.
997
+ # @return [void]
998
+ def section_sync_correspondence_segment(sec_name, shdr)
999
+ seg_type = {
1000
+ '.interp' => ELFTools::Constants::PT_INTERP,
1001
+ '.dynamic' => ELFTools::Constants::PT_DYNAMIC,
1002
+ '.MIPS.abiflags' => ELFTools::Constants::PT_MIPS_ABIFLAGS,
1003
+ '.note.gnu.property' => ELFTools::Constants::PT_GNU_PROPERTY
1004
+ }[sec_name]
1005
+ return if seg_type.nil?
1006
+
1007
+ phdrs_by_type(seg_type) { |phdr| sync_sec_to_seg(shdr, phdr) }
1008
+ end
1009
+
1010
+ # Similar to +section_sync_correspondence_segment+ but dedicate for note sections as it is allowed to have multiple
1011
+ # note segments.
1012
+ # This function searches all note segments and only sync the one matched with the section values before patching.
1013
+ #
1014
+ # NOTE: This function is no-op if +shdr+ does not have section type +ELFTools::Constants::SHT_NOTE+.
1015
+ #
1016
+ # @param [Integer] orig_sh_offset The original section offset value.
1017
+ # @param [Integer] orig_sh_size The original section size value.
1018
+ # @param [ELFTools::Structs::ELF_Shdr] shdr The section header with values after patched.
1019
+ #
1020
+ # @return [void]
1021
+ def sync_note_segment(orig_sh_offset, orig_sh_size, shdr)
1022
+ return if shdr.sh_type != ELFTools::Constants::SHT_NOTE
1023
+
1024
+ phdrs_by_type(ELFTools::Constants::PT_NOTE) do |phdr|
1025
+ s_start = orig_sh_offset
1026
+ s_end = s_start + orig_sh_size
1027
+ p_start = phdr.p_offset
1028
+ p_end = p_start + phdr.p_filesz
1029
+
1030
+ # Skip if no overlap.
1031
+ next unless section_bounds_within_segment?(s_start, s_end, p_start, p_end)
1032
+
1033
+ # Only support exact matches.
1034
+ raise PatchError, 'unsupported overlap of SHT_NOTE and PT_NOTE' unless [p_start, p_end] == [s_start, s_end]
1035
+
1036
+ sync_sec_to_seg(shdr, phdr)
1037
+ end
1038
+ end
1039
+
990
1040
  def write_replaced_sections(cur_off, start_addr, start_offset)
991
1041
  overwrite_replaced_sections
992
1042
 
993
- noted_phdrs = Set.new
994
-
995
1043
  # the sort is necessary, the strategy in ruby and Cpp to iterate map/hash
996
1044
  # is different, patchelf v0.10 iterates the replaced_sections sorted by
997
1045
  # keys.
@@ -1014,34 +1062,8 @@ module PatchELF
1014
1062
  shdr.sh_size = rsec_data.size
1015
1063
 
1016
1064
  write_section_alignment(shdr)
1017
-
1018
- seg_type = {
1019
- '.interp' => ELFTools::Constants::PT_INTERP,
1020
- '.dynamic' => ELFTools::Constants::PT_DYNAMIC,
1021
- '.MIPS.abiflags' => ELFTools::Constants::PT_MIPS_ABIFLAGS,
1022
- '.note.gnu.property' => ELFTools::Constants::PT_GNU_PROPERTY
1023
- }[rsec_name]
1024
-
1025
- phdrs_by_type(seg_type) { |phdr| sync_sec_to_seg(shdr, phdr) }
1026
-
1027
- if shdr.sh_type == ELFTools::Constants::SHT_NOTE
1028
- phdrs_by_type(ELFTools::Constants::PT_NOTE) do |phdr, idx|
1029
- next if noted_phdrs.include?(idx)
1030
-
1031
- s_start = orig_sh_offset
1032
- s_end = s_start + orig_sh_size
1033
- p_start = phdr.p_offset
1034
- p_end = p_start + phdr.p_filesz
1035
-
1036
- next unless section_bounds_within_segment?(s_start, s_end, p_start, p_end)
1037
-
1038
- raise PatchError, 'unsupported overlap of SHT_NOTE and PT_NOTE' if p_start != s_start || p_end != s_end
1039
-
1040
- sync_sec_to_seg(shdr, phdr)
1041
-
1042
- noted_phdrs << idx
1043
- end
1044
- end
1065
+ section_sync_correspondence_segment(rsec_name, shdr)
1066
+ sync_note_segment(orig_sh_offset, orig_sh_size, shdr)
1045
1067
 
1046
1068
  cur_off += Helper.alignup(rsec_data.size, @section_alignment)
1047
1069
  end
data/lib/patchelf/cli.rb CHANGED
@@ -9,7 +9,7 @@ module PatchELF
9
9
  # For command line interface to parsing arguments.
10
10
  module CLI
11
11
  # Name of binary.
12
- SCRIPT_NAME = 'patchelf.rb'.freeze
12
+ SCRIPT_NAME = 'patchelf.rb'
13
13
  # CLI usage string.
14
14
  USAGE = format('Usage: %s <commands> FILENAME [OUTPUT_FILE]', SCRIPT_NAME).freeze
15
15
 
@@ -31,7 +31,7 @@ module PatchELF
31
31
  needed: []
32
32
  }
33
33
  return $stdout.puts "PatchELF Version #{PatchELF::VERSION}" if argv.include?('--version')
34
- return $stdout.puts option_parser unless parse(argv)
34
+ return $stdout.puts option_parser unless parse?(argv)
35
35
 
36
36
  # Now the options are (hopefully) valid, let's process the ELF file.
37
37
  begin
@@ -71,7 +71,7 @@ module PatchELF
71
71
  end
72
72
  end
73
73
 
74
- def parse(argv)
74
+ def parse?(argv)
75
75
  remain = option_parser.permute(argv)
76
76
  return false if remain.first.nil?
77
77
 
@@ -78,7 +78,14 @@ module PatchELF
78
78
  # alignup(0x10, 0x8)
79
79
  # #=> 16
80
80
  def alignup(val, align = page_size)
81
- (val & (align - 1)).zero? ? val : (aligndown(val, align) + align)
81
+ val.nobits?(align - 1) ? val : (aligndown(val, align) + align)
82
+ end
83
+
84
+ # @param [File?] file
85
+ # @return [Proc]
86
+ # A proc that closes the file if it's open.
87
+ def close_file_proc(file)
88
+ proc { file.close if file && !file.closed? }
82
89
  end
83
90
  end
84
91
  end
data/lib/patchelf/mm.rb CHANGED
@@ -49,7 +49,7 @@ module PatchELF
49
49
  abnormal_elf('No LOAD segment found, not an executable.') if load_segments.empty?
50
50
  # TODO: Handle only one LOAD. (be careful if memsz > filesz)
51
51
 
52
- fgap_method || mgap_method || new_load_method
52
+ fgap_method? || mgap_method? || new_load_method
53
53
  end
54
54
 
55
55
  # Query if extended.
@@ -72,25 +72,25 @@ module PatchELF
72
72
 
73
73
  private
74
74
 
75
- def fgap_method
75
+ def fgap_method?
76
76
  idx = find_gap { |prv, nxt| nxt.file_head - prv.file_tail }
77
77
  return false if idx.nil?
78
78
 
79
79
  loads = load_segments
80
80
  # prefer extend backwardly
81
- return extend_backward(loads[idx - 1]) if writable?(loads[idx - 1])
81
+ return extend_backward?(loads[idx - 1]) if writable?(loads[idx - 1])
82
82
 
83
- extend_forward(loads[idx])
83
+ extend_forward?(loads[idx])
84
84
  end
85
85
 
86
- def extend_backward(seg, size = @request_size)
86
+ def extend_backward?(seg, size = @request_size)
87
87
  invoke_callbacks(seg, seg.file_tail)
88
88
  seg.header.p_filesz += size
89
89
  seg.header.p_memsz += size
90
90
  true
91
91
  end
92
92
 
93
- def extend_forward(seg, size = @request_size)
93
+ def extend_forward?(seg, size = @request_size)
94
94
  seg.header.p_offset -= size
95
95
  seg.header.p_vaddr -= size
96
96
  seg.header.p_filesz += size
@@ -99,7 +99,7 @@ module PatchELF
99
99
  true
100
100
  end
101
101
 
102
- def mgap_method
102
+ def mgap_method?
103
103
  # | 1 | | 2 |
104
104
  # | 1 | | 2 |
105
105
  #=>
@@ -113,10 +113,10 @@ module PatchELF
113
113
  @extend_size = PatchELF::Helper.alignup(@request_size)
114
114
  shift_attributes
115
115
  # prefer backward than forward
116
- return extend_backward(loads[idx - 1]) if writable?(loads[idx - 1])
116
+ return extend_backward?(loads[idx - 1]) if writable?(loads[idx - 1])
117
117
 
118
118
  # NOTE: loads[idx].file_head has been changed in shift_attributes
119
- extend_forward(loads[idx], @extend_size)
119
+ extend_forward?(loads[idx], @extend_size)
120
120
  end
121
121
 
122
122
  def find_gap(check_sz: true)
@@ -2,8 +2,10 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  require 'elftools/elf_file'
5
+ require 'objspace'
5
6
 
6
7
  require 'patchelf/exceptions'
8
+ require 'patchelf/helper'
7
9
  require 'patchelf/logger'
8
10
  require 'patchelf/saver'
9
11
 
@@ -27,13 +29,17 @@ module PatchELF
27
29
  # :silent = ignore the errors
28
30
  def initialize(filename, on_error: :log, logging: true)
29
31
  @in_file = filename
30
- @elf = ELFTools::ELFFile.new(File.open(filename))
32
+ f = File.open(filename) # rubocop:disable Style/FileOpen
33
+ @elf = ELFTools::ELFFile.new(f)
31
34
  @set = {}
32
35
  @rpath_sym = :runpath
33
36
  @on_error = logging ? on_error : :exception
34
37
 
35
38
  on_error_syms = %i[exception log silent]
36
39
  raise ArgumentError, "on_error must be one of #{on_error_syms}" unless on_error_syms.include?(@on_error)
40
+
41
+ # Ensure file is closed when the {Patcher} object is garbage collected.
42
+ ObjectSpace.define_finalizer(self, Helper.close_file_proc(f))
37
43
  end
38
44
 
39
45
  # @return [String?]
@@ -5,10 +5,15 @@ require 'elftools/elf_file'
5
5
  require 'elftools/structs'
6
6
  require 'elftools/util'
7
7
  require 'fileutils'
8
+ require 'objspace'
8
9
 
10
+ require 'patchelf/helper'
9
11
  require 'patchelf/mm'
10
12
 
11
13
  module PatchELF
14
+ # To mark a not-using tag
15
+ IGNORE = ELFTools::Constants::DT_LOOS
16
+
12
17
  # Internal use only.
13
18
  #
14
19
  # For {Patcher} to do patching things and save to file.
@@ -27,10 +32,14 @@ module PatchELF
27
32
  @set = set
28
33
  # [{Integer => String}]
29
34
  @inline_patch = {}
30
- @elf = ELFTools::ELFFile.new(File.open(in_file))
35
+ f = File.open(in_file) # rubocop:disable Style/FileOpen
36
+ @elf = ELFTools::ELFFile.new(f)
31
37
  @mm = PatchELF::MM.new(@elf)
32
38
  @strtab_extend_requests = []
33
39
  @append_dyn = []
40
+
41
+ # Ensure file is closed when the {Saver} object is garbage collected.
42
+ ObjectSpace.define_finalizer(self, Helper.close_file_proc(f))
34
43
  end
35
44
 
36
45
  # @return [void]
@@ -116,8 +125,6 @@ module PatchELF
116
125
  end
117
126
  end
118
127
 
119
- # To mark a not-using tag
120
- IGNORE = ELFTools::Constants::DT_LOOS
121
128
  def patch_needed
122
129
  original_needs = dynamic.tags_by_type(:needed)
123
130
  @set[:needed].uniq!
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PatchELF
4
4
  # Current gem version.
5
- VERSION = '1.5.1'.freeze
5
+ VERSION = '1.6.2'
6
6
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patchelf
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 1.6.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - david942j
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-04-22 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: elftools
@@ -24,6 +23,20 @@ dependencies:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
25
  version: '1.3'
26
+ - !ruby/object:Gem::Dependency
27
+ name: logger
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1'
27
40
  - !ruby/object:Gem::Dependency
28
41
  name: rake
29
42
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +93,20 @@ dependencies:
80
93
  - - ">="
81
94
  - !ruby/object:Gem::Version
82
95
  version: '0.22'
96
+ - !ruby/object:Gem::Dependency
97
+ name: simplecov-cobertura
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '4'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '4'
83
110
  - !ruby/object:Gem::Dependency
84
111
  name: tty-platform
85
112
  requirement: !ruby/object:Gem::Requirement
@@ -118,6 +145,7 @@ executables:
118
145
  extensions: []
119
146
  extra_rdoc_files: []
120
147
  files:
148
+ - LICENSE
121
149
  - README.md
122
150
  - bin/patchelf.rb
123
151
  - lib/patchelf.rb
@@ -130,12 +158,14 @@ files:
130
158
  - lib/patchelf/patcher.rb
131
159
  - lib/patchelf/saver.rb
132
160
  - lib/patchelf/version.rb
133
- homepage: https://github.com/david942j/patchelf.rb
161
+ homepage: https://github.com/Homebrew/patchelf.rb
134
162
  licenses:
135
163
  - MIT
136
164
  metadata:
137
165
  rubygems_mfa_required: 'true'
138
- post_install_message:
166
+ source_code_uri: https://github.com/Homebrew/patchelf.rb
167
+ bug_tracker_uri: https://github.com/Homebrew/patchelf.rb/issues
168
+ funding_uri: https://github.com/sponsors/Homebrew
139
169
  rdoc_options: []
140
170
  require_paths:
141
171
  - lib
@@ -143,15 +173,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
143
173
  requirements:
144
174
  - - ">="
145
175
  - !ruby/object:Gem::Version
146
- version: '3.1'
176
+ version: '3.3'
147
177
  required_rubygems_version: !ruby/object:Gem::Requirement
148
178
  requirements:
149
179
  - - ">="
150
180
  - !ruby/object:Gem::Version
151
181
  version: '0'
152
182
  requirements: []
153
- rubygems_version: 3.5.3
154
- signing_key:
183
+ rubygems_version: 4.0.16
155
184
  specification_version: 4
156
185
  summary: patchelf
157
186
  test_files: []