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 +4 -4
- data/LICENSE +21 -0
- data/README.md +20 -7
- data/lib/patchelf/alt_saver.rb +99 -77
- data/lib/patchelf/cli.rb +3 -3
- data/lib/patchelf/helper.rb +8 -1
- data/lib/patchelf/mm.rb +9 -9
- data/lib/patchelf/patcher.rb +7 -1
- data/lib/patchelf/saver.rb +10 -3
- data/lib/patchelf/version.rb +1 -1
- metadata +37 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 64cae3b6070bf26b6716a82886fcb183f20213bf0c396501141e93c1e7951ffa
|
|
4
|
+
data.tar.gz: 49793e2b2bf154c0d4ff70f1533130495a0161db418248fc39105997159c59c8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
[](
|
|
1
|
+
[](https://rubygems.org/gems/patchelf)
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/rb/patchelf)
|
|
4
|
+
[](https://github.com/Homebrew/patchelf.rb/actions/workflows/tests.yml)
|
|
5
|
+
[](https://codecov.io/gh/Homebrew/patchelf.rb)
|
|
6
|
+
[](https://www.rubydoc.info/gems/patchelf)
|
|
7
|
+
[](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)
|
data/lib/patchelf/alt_saver.rb
CHANGED
|
@@ -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
|
-
#
|
|
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
|
-
|
|
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
|
-
#
|
|
149
|
-
buf.seek
|
|
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",
|
|
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
|
-
|
|
675
|
-
|
|
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
|
-
|
|
679
|
-
|
|
680
|
-
break
|
|
681
|
-
end
|
|
679
|
+
size = section.header.sh_size.to_i
|
|
680
|
+
curr_off = section.header.sh_offset.to_i
|
|
682
681
|
|
|
683
|
-
|
|
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
|
|
752
|
-
|
|
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, "
|
|
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
|
-
#
|
|
865
|
-
#
|
|
866
|
-
#
|
|
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
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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'
|
|
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
|
|
data/lib/patchelf/helper.rb
CHANGED
|
@@ -78,7 +78,14 @@ module PatchELF
|
|
|
78
78
|
# alignup(0x10, 0x8)
|
|
79
79
|
# #=> 16
|
|
80
80
|
def alignup(val, align = page_size)
|
|
81
|
-
|
|
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)
|
data/lib/patchelf/patcher.rb
CHANGED
|
@@ -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
|
-
|
|
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?]
|
data/lib/patchelf/saver.rb
CHANGED
|
@@ -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
|
-
|
|
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!
|
data/lib/patchelf/version.rb
CHANGED
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.
|
|
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:
|
|
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/
|
|
161
|
+
homepage: https://github.com/Homebrew/patchelf.rb
|
|
134
162
|
licenses:
|
|
135
163
|
- MIT
|
|
136
164
|
metadata:
|
|
137
165
|
rubygems_mfa_required: 'true'
|
|
138
|
-
|
|
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.
|
|
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:
|
|
154
|
-
signing_key:
|
|
183
|
+
rubygems_version: 4.0.16
|
|
155
184
|
specification_version: 4
|
|
156
185
|
summary: patchelf
|
|
157
186
|
test_files: []
|