patchelf 1.3.0 → 1.4.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/README.md +1 -1
- data/lib/patchelf/alt_saver.rb +280 -59
- data/lib/patchelf/cli.rb +1 -1
- data/lib/patchelf/exceptions.rb +2 -0
- data/lib/patchelf/helper.rb +19 -5
- data/lib/patchelf/mm.rb +2 -2
- data/lib/patchelf/patcher.rb +1 -1
- data/lib/patchelf/saver.rb +4 -4
- data/lib/patchelf/version.rb +1 -1
- metadata +18 -11
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 275f2e51c3ce1332b2069f3d9bb058351c1b0953df6bb5aad9d34f1af3adb9a4
|
|
4
|
+
data.tar.gz: a4f2c422bac7e669601a4c29e30046cdaa2076f9937d767152bd5f08920abdf4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 97c66ab530c8f00f95c67dbeb07dfcc426c786c0d90b4a85cede69dc205283beb3d9d1c393b910b1ff88312bf80d040e6a2dccd5ed35300665015fcc3e3ce7e7
|
|
7
|
+
data.tar.gz: 9d09373c7ef6d6aa3f7a3541afefd3486b9e9906091a98277141fa25decbd32f0eee3e214ebd97e7f8e56c89cd8646a7c4b3ccde3a48151b345c9ebc170c5173
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[](https://github.com/david942j/patchelf.rb/actions)
|
|
2
2
|
[](https://dependabot.com)
|
|
3
3
|
[](https://codeclimate.com/github/david942j/patchelf.rb)
|
|
4
4
|
[](https://codeclimate.com/github/david942j/patchelf.rb)
|
data/lib/patchelf/alt_saver.rb
CHANGED
|
@@ -8,10 +8,10 @@ require 'fileutils'
|
|
|
8
8
|
|
|
9
9
|
require 'patchelf/helper'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
# :nodoc:
|
|
12
12
|
module PatchELF
|
|
13
13
|
# TODO: refactor buf_* methods here
|
|
14
|
-
# TODO: move all refinements into a
|
|
14
|
+
# TODO: move all refinements into a separate file / helper file.
|
|
15
15
|
# refinements for cleaner syntax / speed / memory optimizations
|
|
16
16
|
module Refinements
|
|
17
17
|
refine StringIO do
|
|
@@ -21,7 +21,7 @@ module PatchELF
|
|
|
21
21
|
# @param [Integer] nbytes
|
|
22
22
|
# @return[void]
|
|
23
23
|
def fill(char, nbytes)
|
|
24
|
-
at_once = Helper
|
|
24
|
+
at_once = Helper.page_size
|
|
25
25
|
pending = nbytes
|
|
26
26
|
|
|
27
27
|
if pending > at_once
|
|
@@ -133,6 +133,8 @@ module PatchELF
|
|
|
133
133
|
sec = find_section '.dynamic'
|
|
134
134
|
return unless sec
|
|
135
135
|
|
|
136
|
+
return if sec.header.sh_type == ELFTools::Constants::SHT_NOBITS
|
|
137
|
+
|
|
136
138
|
shdr = sec.header
|
|
137
139
|
with_buf_at(shdr.sh_offset) do |buf|
|
|
138
140
|
dyn = ELFTools::Structs::ELF_Dyn.new(elf_class: elf_class, endian: endian)
|
|
@@ -171,7 +173,7 @@ module PatchELF
|
|
|
171
173
|
end
|
|
172
174
|
|
|
173
175
|
def modify_interpreter
|
|
174
|
-
@replaced_sections['.interp'] = @set[:interpreter]
|
|
176
|
+
@replaced_sections['.interp'] = "#{@set[:interpreter]}\x00"
|
|
175
177
|
end
|
|
176
178
|
|
|
177
179
|
def modify_needed
|
|
@@ -234,14 +236,13 @@ module PatchELF
|
|
|
234
236
|
dyn_tags = collect_runpath_tags
|
|
235
237
|
resolve_rpath_tag_conflict(dyn_tags, force_rpath: force_rpath)
|
|
236
238
|
# (:runpath, :rpath) order_matters.
|
|
237
|
-
|
|
239
|
+
resolved_rpath_dyn = dyn_tags.values_at(:runpath, :rpath).compact.first
|
|
238
240
|
|
|
239
241
|
old_rpath = ''
|
|
240
242
|
rpath_off = nil
|
|
241
|
-
|
|
242
|
-
rpath_off = shdr_dynstr.sh_offset +
|
|
243
|
+
if resolved_rpath_dyn
|
|
244
|
+
rpath_off = shdr_dynstr.sh_offset + resolved_rpath_dyn[:header].d_val
|
|
243
245
|
old_rpath = buf_cstr(rpath_off)
|
|
244
|
-
break
|
|
245
246
|
end
|
|
246
247
|
return if old_rpath == new_rpath
|
|
247
248
|
|
|
@@ -293,6 +294,11 @@ module PatchELF
|
|
|
293
294
|
dt_null_idx += 1
|
|
294
295
|
end
|
|
295
296
|
|
|
297
|
+
if dyn_num_bytes.nil?
|
|
298
|
+
Logger.error 'no dynamic tags'
|
|
299
|
+
return
|
|
300
|
+
end
|
|
301
|
+
|
|
296
302
|
# allot for new dt_runpath
|
|
297
303
|
shdr_dynamic = find_section('.dynamic').header
|
|
298
304
|
new_dynamic_data = replace_section '.dynamic', shdr_dynamic.sh_size + dyn_num_bytes
|
|
@@ -331,7 +337,7 @@ module PatchELF
|
|
|
331
337
|
end
|
|
332
338
|
|
|
333
339
|
def page_size
|
|
334
|
-
Helper
|
|
340
|
+
Helper.page_size(ehdr.e_machine)
|
|
335
341
|
end
|
|
336
342
|
|
|
337
343
|
def patch_out
|
|
@@ -359,7 +365,7 @@ module PatchELF
|
|
|
359
365
|
elsif data.size < size
|
|
360
366
|
data.ljust(size, "\x00")
|
|
361
367
|
else
|
|
362
|
-
data[0...size]
|
|
368
|
+
"#{data[0...size]}\x00"
|
|
363
369
|
end
|
|
364
370
|
@replaced_sections[section_name] = rep_data
|
|
365
371
|
end
|
|
@@ -512,7 +518,7 @@ module PatchELF
|
|
|
512
518
|
def copy_shdrs_to_eof
|
|
513
519
|
shoff_new = @buffer.size
|
|
514
520
|
# honestly idk why `ehdr.e_shoff` is considered when we are only moving shdrs.
|
|
515
|
-
sh_size = ehdr.e_shoff + ehdr.e_shnum * ehdr.e_shentsize
|
|
521
|
+
sh_size = ehdr.e_shoff + (ehdr.e_shnum * ehdr.e_shentsize)
|
|
516
522
|
buf_grow! @buffer.size + sh_size
|
|
517
523
|
ehdr.e_shoff = shoff_new
|
|
518
524
|
raise PatchError, 'ehdr.e_shnum != @sections.size' if ehdr.e_shnum != @sections.size
|
|
@@ -529,11 +535,11 @@ module PatchELF
|
|
|
529
535
|
def rewrite_sections_executable
|
|
530
536
|
sort_shdrs!
|
|
531
537
|
shdr = start_replacement_shdr
|
|
532
|
-
start_offset = shdr.sh_offset
|
|
533
|
-
start_addr = shdr.sh_addr
|
|
538
|
+
start_offset = shdr.sh_offset.to_i
|
|
539
|
+
start_addr = shdr.sh_addr.to_i
|
|
534
540
|
first_page = start_addr - start_offset
|
|
535
541
|
|
|
536
|
-
Logger.debug "first reserved offset/addr is 0x#{start_offset.
|
|
542
|
+
Logger.debug "first reserved offset/addr is 0x#{start_offset.to_s 16}/0x#{start_addr.to_s 16}"
|
|
537
543
|
|
|
538
544
|
unless start_addr % page_size == start_offset % page_size
|
|
539
545
|
raise PatchError, 'start_addr != start_offset (mod PAGE_SIZE)'
|
|
@@ -543,6 +549,8 @@ module PatchELF
|
|
|
543
549
|
|
|
544
550
|
copy_shdrs_to_eof if ehdr.e_shoff < start_offset
|
|
545
551
|
|
|
552
|
+
normalize_note_segments
|
|
553
|
+
|
|
546
554
|
seg_num_bytes = @segments.first.header.num_bytes
|
|
547
555
|
needed_space = (
|
|
548
556
|
ehdr.num_bytes +
|
|
@@ -557,10 +565,10 @@ module PatchELF
|
|
|
557
565
|
Logger.debug "needed pages is #{needed_pages}"
|
|
558
566
|
raise PatchError, 'virtual address space underrun' if needed_pages * page_size > first_page
|
|
559
567
|
|
|
568
|
+
shift_file(needed_pages, start_offset)
|
|
569
|
+
|
|
560
570
|
first_page -= needed_pages * page_size
|
|
561
571
|
start_offset += needed_pages * page_size
|
|
562
|
-
|
|
563
|
-
shift_file(needed_pages, first_page)
|
|
564
572
|
end
|
|
565
573
|
Logger.debug "needed space is #{needed_space}"
|
|
566
574
|
|
|
@@ -575,27 +583,31 @@ module PatchELF
|
|
|
575
583
|
end
|
|
576
584
|
|
|
577
585
|
def replace_sections_in_the_way_of_phdr!
|
|
578
|
-
|
|
586
|
+
num_notes = @sections.count { |sec| sec.type == ELFTools::Constants::SHT_NOTE }
|
|
587
|
+
pht_size = ehdr.num_bytes + ((@segments.count + num_notes + 1) * @segments.first.header.num_bytes)
|
|
579
588
|
|
|
580
589
|
# replace sections that may overlap with expanded program header table
|
|
581
590
|
@sections.each_with_index do |sec, idx|
|
|
582
591
|
shdr = sec.header
|
|
583
592
|
next if idx.zero? || @replaced_sections[sec.name]
|
|
584
|
-
break if shdr.
|
|
593
|
+
break if shdr.sh_offset > pht_size
|
|
585
594
|
|
|
586
595
|
replace_section sec.name, shdr.sh_size
|
|
587
596
|
end
|
|
588
597
|
end
|
|
589
598
|
|
|
590
|
-
def seg_end_addr(seg)
|
|
591
|
-
phdr = seg.header
|
|
592
|
-
Helper.alignup(phdr.p_vaddr + phdr.p_memsz, page_size)
|
|
593
|
-
end
|
|
594
|
-
|
|
595
599
|
def rewrite_sections_library
|
|
596
|
-
start_page =
|
|
600
|
+
start_page = 0
|
|
601
|
+
first_page = 0
|
|
602
|
+
@segments.each do |seg|
|
|
603
|
+
phdr = seg.header
|
|
604
|
+
this_page = Helper.alignup(phdr.p_vaddr + phdr.p_memsz, page_size)
|
|
605
|
+
start_page = [start_page, this_page].max
|
|
606
|
+
first_page = phdr.p_vaddr - phdr.p_offset if phdr.p_type == ELFTools::Constants::PT_PHDR
|
|
607
|
+
end
|
|
597
608
|
|
|
598
609
|
Logger.debug "Last page is 0x#{start_page.to_s 16}"
|
|
610
|
+
Logger.debug "First page is 0x#{first_page.to_s 16}"
|
|
599
611
|
replace_sections_in_the_way_of_phdr!
|
|
600
612
|
needed_space = @replaced_sections.sum { |_, str| Helper.alignup(str.size, @section_alignment) }
|
|
601
613
|
Logger.debug "needed space = #{needed_space}"
|
|
@@ -623,42 +635,172 @@ module PatchELF
|
|
|
623
635
|
p_align: page_size
|
|
624
636
|
)
|
|
625
637
|
|
|
638
|
+
normalize_note_segments
|
|
639
|
+
|
|
626
640
|
cur_off = write_replaced_sections start_offset, start_page, start_offset
|
|
627
641
|
raise PatchError, 'cur_off != start_offset + needed_space' if cur_off != start_offset + needed_space
|
|
628
642
|
|
|
629
|
-
rewrite_headers ehdr.e_phoff
|
|
643
|
+
rewrite_headers(first_page + ehdr.e_phoff)
|
|
630
644
|
end
|
|
631
645
|
|
|
632
|
-
def
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
buf_move! shift, 0, oldsz
|
|
637
|
-
with_buf_at(ehdr.num_bytes) { |buf| buf.write "\x00" * (shift - ehdr.num_bytes) }
|
|
646
|
+
def normalize_note_segments
|
|
647
|
+
return if @replaced_sections.none? do |rsec_name, _|
|
|
648
|
+
find_section(rsec_name)&.type == ELFTools::Constants::SHT_NOTE
|
|
649
|
+
end
|
|
638
650
|
|
|
639
|
-
|
|
640
|
-
|
|
651
|
+
new_phdrs = []
|
|
652
|
+
|
|
653
|
+
phdrs_by_type(ELFTools::Constants::PT_NOTE) do |phdr|
|
|
654
|
+
# Binaries produced by older patchelf versions may contain empty PT_NOTE segments.
|
|
655
|
+
next if @sections.none? do |sec|
|
|
656
|
+
sec.header.sh_offset >= phdr.p_offset && sec.header.sh_offset < phdr.p_offset + phdr.p_filesz
|
|
657
|
+
end
|
|
658
|
+
|
|
659
|
+
new_phdrs += normalize_note_segment(phdr)
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
new_phdrs.each { |phdr| add_segment!(**phdr.snapshot) }
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
def normalize_note_segment(phdr)
|
|
666
|
+
start_off = phdr.p_offset.to_i
|
|
667
|
+
curr_off = start_off
|
|
668
|
+
end_off = start_off + phdr.p_filesz
|
|
669
|
+
|
|
670
|
+
new_phdrs = []
|
|
671
|
+
|
|
672
|
+
while curr_off < end_off
|
|
673
|
+
size = 0
|
|
674
|
+
sections_at_aligned_offset(curr_off) do |sec|
|
|
675
|
+
next if sec.type != ELFTools::Constants::SHT_NOTE
|
|
676
|
+
|
|
677
|
+
size = sec.header.sh_size.to_i
|
|
678
|
+
curr_off = sec.header.sh_offset.to_i
|
|
679
|
+
break
|
|
680
|
+
end
|
|
681
|
+
|
|
682
|
+
raise PatchError, 'cannot normalize PT_NOTE segment: non-contiguous SHT_NOTE sections' if size.zero?
|
|
683
|
+
|
|
684
|
+
if curr_off + size > end_off
|
|
685
|
+
raise PatchError, 'cannot normalize PT_NOTE segment: partially mapped SHT_NOTE section'
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
new_phdr = ELFTools::Structs::ELF_Phdr[elf_class].new(endian: endian, **phdr.snapshot)
|
|
689
|
+
new_phdr.p_offset = curr_off
|
|
690
|
+
new_phdr.p_vaddr = phdr.p_vaddr + (curr_off - start_off)
|
|
691
|
+
new_phdr.p_paddr = phdr.p_paddr + (curr_off - start_off)
|
|
692
|
+
new_phdr.p_filesz = size
|
|
693
|
+
new_phdr.p_memsz = size
|
|
694
|
+
|
|
695
|
+
if curr_off == start_off
|
|
696
|
+
phdr.assign(new_phdr)
|
|
697
|
+
else
|
|
698
|
+
new_phdrs << new_phdr
|
|
699
|
+
end
|
|
700
|
+
|
|
701
|
+
curr_off += size
|
|
702
|
+
end
|
|
703
|
+
|
|
704
|
+
new_phdrs
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def sections_at_aligned_offset(offset)
|
|
708
|
+
@sections.each do |sec|
|
|
709
|
+
shdr = sec.header
|
|
710
|
+
|
|
711
|
+
aligned_offset = Helper.alignup(offset, shdr.sh_addralign)
|
|
712
|
+
next if shdr.sh_offset != aligned_offset
|
|
713
|
+
|
|
714
|
+
yield sec
|
|
715
|
+
end
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
def shift_sections(shift, start_offset)
|
|
719
|
+
ehdr.e_shoff += shift if ehdr.e_shoff >= start_offset
|
|
641
720
|
|
|
642
721
|
@sections.each_with_index do |sec, i|
|
|
643
722
|
next if i.zero? # dont touch NULL section
|
|
644
723
|
|
|
645
724
|
shdr = sec.header
|
|
725
|
+
next if shdr.sh_offset < start_offset
|
|
726
|
+
|
|
646
727
|
shdr.sh_offset += shift
|
|
647
728
|
end
|
|
729
|
+
end
|
|
648
730
|
|
|
649
|
-
|
|
731
|
+
def shift_segment_offset(phdr, shift)
|
|
732
|
+
phdr.p_offset += shift
|
|
733
|
+
phdr.p_align = page_size if phdr.p_align != 0 && (phdr.p_vaddr - phdr.p_offset) % phdr.p_align != 0
|
|
734
|
+
end
|
|
735
|
+
|
|
736
|
+
def shift_segment_virtual_address(phdr, shift)
|
|
737
|
+
phdr.p_paddr -= shift if phdr.p_paddr > shift
|
|
738
|
+
phdr.p_vaddr -= shift if phdr.p_vaddr > shift
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
742
|
+
def shift_segments(shift, start_offset)
|
|
743
|
+
split_index = -1
|
|
744
|
+
split_shift = 0
|
|
745
|
+
|
|
746
|
+
@segments.each_with_index do |seg, idx|
|
|
650
747
|
phdr = seg.header
|
|
651
|
-
phdr.p_offset
|
|
652
|
-
|
|
748
|
+
p_start = phdr.p_offset
|
|
749
|
+
|
|
750
|
+
if p_start <= start_offset && p_start + phdr.p_filesz > start_offset &&
|
|
751
|
+
phdr.p_type == ELFTools::Constants::PT_LOAD
|
|
752
|
+
raise PatchError, "split_index(#{split_index}) != -1" if split_index != -1
|
|
753
|
+
|
|
754
|
+
split_index = idx
|
|
755
|
+
split_shift = start_offset - p_start
|
|
756
|
+
|
|
757
|
+
phdr.p_offset = start_offset
|
|
758
|
+
phdr.p_memsz -= split_shift
|
|
759
|
+
phdr.p_filesz -= split_shift
|
|
760
|
+
phdr.p_paddr += split_shift
|
|
761
|
+
phdr.p_vaddr += split_shift
|
|
762
|
+
|
|
763
|
+
p_start = start_offset
|
|
764
|
+
end
|
|
765
|
+
|
|
766
|
+
if p_start >= start_offset
|
|
767
|
+
shift_segment_offset(phdr, shift)
|
|
768
|
+
else
|
|
769
|
+
shift_segment_virtual_address(phdr, shift)
|
|
770
|
+
end
|
|
653
771
|
end
|
|
654
772
|
|
|
773
|
+
raise PatchError, "split_index(#{split_index}) == -1" if split_index == -1
|
|
774
|
+
|
|
775
|
+
[split_index, split_shift]
|
|
776
|
+
end
|
|
777
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
778
|
+
|
|
779
|
+
def shift_file(extra_pages, start_offset)
|
|
780
|
+
raise PatchError, "start_offset(#{start_offset}) < ehdr.num_bytes" if start_offset < ehdr.num_bytes
|
|
781
|
+
|
|
782
|
+
oldsz = @buffer.size
|
|
783
|
+
raise PatchError, "oldsz <= start_offset(#{start_offset})" if oldsz <= start_offset
|
|
784
|
+
|
|
785
|
+
shift = extra_pages * page_size
|
|
786
|
+
buf_grow!(oldsz + shift)
|
|
787
|
+
buf_move!(start_offset + shift, start_offset, oldsz - start_offset)
|
|
788
|
+
with_buf_at(start_offset) { |buf| buf.write "\x00" * shift }
|
|
789
|
+
|
|
790
|
+
ehdr.e_phoff = ehdr.num_bytes
|
|
791
|
+
|
|
792
|
+
shift_sections(shift, start_offset)
|
|
793
|
+
|
|
794
|
+
split_index, split_shift = shift_segments(shift, start_offset)
|
|
795
|
+
|
|
796
|
+
split_phdr = @segments[split_index].header
|
|
655
797
|
add_segment!(
|
|
656
798
|
p_type: ELFTools::Constants::PT_LOAD,
|
|
657
|
-
p_offset:
|
|
658
|
-
p_vaddr:
|
|
659
|
-
p_paddr:
|
|
660
|
-
p_filesz: shift,
|
|
661
|
-
p_memsz: shift,
|
|
799
|
+
p_offset: split_phdr.p_offset - split_shift - shift,
|
|
800
|
+
p_vaddr: split_phdr.p_vaddr - split_shift - shift,
|
|
801
|
+
p_paddr: split_phdr.p_paddr - split_shift - shift,
|
|
802
|
+
p_filesz: split_shift + shift,
|
|
803
|
+
p_memsz: split_shift + shift,
|
|
662
804
|
p_flags: ELFTools::Constants::PF_R | ELFTools::Constants::PF_W,
|
|
663
805
|
p_align: page_size
|
|
664
806
|
)
|
|
@@ -699,12 +841,23 @@ module PatchELF
|
|
|
699
841
|
end
|
|
700
842
|
|
|
701
843
|
def sort_shdrs!
|
|
844
|
+
return if @sections.empty?
|
|
845
|
+
|
|
702
846
|
section_dep_values = collect_section_to_section_refs
|
|
703
|
-
|
|
847
|
+
shstrtab = @sections[ehdr.e_shstrndx].header
|
|
704
848
|
@sections.sort! { |me, you| me.header.sh_offset.to_i <=> you.header.sh_offset.to_i }
|
|
705
849
|
update_section_idx!
|
|
706
850
|
restore_section_to_section_refs!(section_dep_values)
|
|
707
|
-
|
|
851
|
+
@sections.each_with_index do |sec, idx|
|
|
852
|
+
ehdr.e_shstrndx = idx if sec.header.sh_offset == shstrtab.sh_offset
|
|
853
|
+
end
|
|
854
|
+
end
|
|
855
|
+
|
|
856
|
+
def jmprel_section_name
|
|
857
|
+
sec_name = %w[.rel.plt .rela.plt .rela.IA_64.pltoff].find { |s| find_section(s) }
|
|
858
|
+
raise PatchError, 'cannot find section corresponding to DT_JMPREL' unless sec_name
|
|
859
|
+
|
|
860
|
+
sec_name
|
|
708
861
|
end
|
|
709
862
|
|
|
710
863
|
# given a +dyn.d_tag+, returns the section name it must be synced to.
|
|
@@ -719,12 +872,14 @@ module PatchELF
|
|
|
719
872
|
when ELFTools::Constants::DT_HASH
|
|
720
873
|
'.hash'
|
|
721
874
|
when ELFTools::Constants::DT_GNU_HASH
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
875
|
+
# return nil if not found, patchelf claims no problem in skipping
|
|
876
|
+
find_section('.gnu.hash')&.name
|
|
877
|
+
when ELFTools::Constants::DT_MIPS_XHASH
|
|
878
|
+
return if ehdr.e_machine != ELFTools::Constants::EM_MIPS
|
|
726
879
|
|
|
727
|
-
|
|
880
|
+
'.MIPS.xhash'
|
|
881
|
+
when ELFTools::Constants::DT_JMPREL
|
|
882
|
+
jmprel_section_name
|
|
728
883
|
when ELFTools::Constants::DT_REL
|
|
729
884
|
# regarding .rel.got, NixOS/patchelf says
|
|
730
885
|
# "no idea if this makes sense, but it was needed for some program"
|
|
@@ -743,9 +898,26 @@ module PatchELF
|
|
|
743
898
|
|
|
744
899
|
# updates dyn tags by syncing it with @section values
|
|
745
900
|
def sync_dyn_tags!
|
|
901
|
+
dyn_table_offset = nil
|
|
746
902
|
each_dynamic_tags do |dyn, buf_off|
|
|
903
|
+
dyn_table_offset ||= buf_off
|
|
904
|
+
|
|
747
905
|
sec_name = dyn_tag_to_section_name(dyn.d_tag)
|
|
748
|
-
|
|
906
|
+
|
|
907
|
+
unless sec_name
|
|
908
|
+
if dyn.d_tag == ELFTools::Constants::DT_MIPS_RLD_MAP_REL && ehdr.e_machine == ELFTools::Constants::EM_MIPS
|
|
909
|
+
rld_map = find_section('.rld_map')
|
|
910
|
+
dyn.d_val = if rld_map
|
|
911
|
+
rld_map.header.sh_addr.to_i - (buf_off - dyn_table_offset) -
|
|
912
|
+
find_section('.dynamic').header.sh_addr.to_i
|
|
913
|
+
else
|
|
914
|
+
Logger.warn 'DT_MIPS_RLD_MAP_REL entry is present, but .rld_map section is not'
|
|
915
|
+
0
|
|
916
|
+
end
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
next
|
|
920
|
+
end
|
|
749
921
|
|
|
750
922
|
shdr = find_section(sec_name).header
|
|
751
923
|
dyn.d_val = dyn.d_tag == ELFTools::Constants::DT_STRSZ ? shdr.sh_size.to_i : shdr.sh_addr.to_i
|
|
@@ -784,22 +956,46 @@ module PatchELF
|
|
|
784
956
|
end
|
|
785
957
|
end
|
|
786
958
|
|
|
787
|
-
|
|
788
|
-
|
|
959
|
+
# Returns a blank shdr if the section doesn't exist.
|
|
960
|
+
def find_or_create_section_header(rsec_name)
|
|
961
|
+
shdr = find_section(rsec_name)&.header
|
|
962
|
+
shdr ||= ELFTools::Structs::ELF_Shdr.new(endian: endian, elf_class: elf_class)
|
|
963
|
+
shdr
|
|
964
|
+
end
|
|
789
965
|
|
|
790
|
-
|
|
966
|
+
def overwrite_replaced_sections
|
|
967
|
+
# the original source says this has to be done separately to
|
|
791
968
|
# prevent clobbering the previously written section contents.
|
|
792
969
|
@replaced_sections.each do |rsec_name, _|
|
|
793
|
-
shdr = find_section(rsec_name)
|
|
794
|
-
|
|
970
|
+
shdr = find_section(rsec_name)&.header
|
|
971
|
+
next unless shdr
|
|
972
|
+
|
|
973
|
+
next if shdr.sh_type == ELFTools::Constants::SHT_NOBITS
|
|
974
|
+
|
|
975
|
+
with_buf_at(shdr.sh_offset) { |b| b.fill('X', shdr.sh_size) }
|
|
795
976
|
end
|
|
977
|
+
end
|
|
978
|
+
|
|
979
|
+
def write_section_aligment(shdr)
|
|
980
|
+
return if shdr.sh_type == ELFTools::Constants::SHT_NOTE && shdr.sh_addralign <= @section_alignment
|
|
981
|
+
|
|
982
|
+
shdr.sh_addralign = @section_alignment
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
def section_bounds_within_segment?(s_start, s_end, p_start, p_end)
|
|
986
|
+
(s_start >= p_start && s_start < p_end) || (s_end > p_start && s_end <= p_end)
|
|
987
|
+
end
|
|
988
|
+
|
|
989
|
+
def write_replaced_sections(cur_off, start_addr, start_offset)
|
|
990
|
+
overwrite_replaced_sections
|
|
991
|
+
|
|
992
|
+
noted_phdrs = Set.new
|
|
796
993
|
|
|
797
994
|
# the sort is necessary, the strategy in ruby and Cpp to iterate map/hash
|
|
798
995
|
# is different, patchelf v0.10 iterates the replaced_sections sorted by
|
|
799
996
|
# keys.
|
|
800
997
|
@replaced_sections.sort.each do |rsec_name, rsec_data|
|
|
801
|
-
|
|
802
|
-
shdr = section.header
|
|
998
|
+
shdr = find_or_create_section_header(rsec_name)
|
|
803
999
|
|
|
804
1000
|
Logger.debug <<~DEBUG
|
|
805
1001
|
rewriting section '#{rsec_name}'
|
|
@@ -809,18 +1005,43 @@ module PatchELF
|
|
|
809
1005
|
|
|
810
1006
|
with_buf_at(cur_off) { |b| b.write rsec_data }
|
|
811
1007
|
|
|
1008
|
+
orig_sh_offset = shdr.sh_offset.to_i
|
|
1009
|
+
orig_sh_size = shdr.sh_size.to_i
|
|
1010
|
+
|
|
812
1011
|
shdr.sh_offset = cur_off
|
|
813
1012
|
shdr.sh_addr = start_addr + (cur_off - start_offset)
|
|
814
1013
|
shdr.sh_size = rsec_data.size
|
|
815
|
-
|
|
1014
|
+
|
|
1015
|
+
write_section_aligment(shdr)
|
|
816
1016
|
|
|
817
1017
|
seg_type = {
|
|
818
1018
|
'.interp' => ELFTools::Constants::PT_INTERP,
|
|
819
|
-
'.dynamic' => ELFTools::Constants::PT_DYNAMIC
|
|
820
|
-
|
|
1019
|
+
'.dynamic' => ELFTools::Constants::PT_DYNAMIC,
|
|
1020
|
+
'.MIPS.abiflags' => ELFTools::Constants::PT_MIPS_ABIFLAGS,
|
|
1021
|
+
'.note.gnu.property' => ELFTools::Constants::PT_GNU_PROPERTY
|
|
1022
|
+
}[rsec_name]
|
|
821
1023
|
|
|
822
1024
|
phdrs_by_type(seg_type) { |phdr| sync_sec_to_seg(shdr, phdr) }
|
|
823
1025
|
|
|
1026
|
+
if shdr.sh_type == ELFTools::Constants::SHT_NOTE
|
|
1027
|
+
phdrs_by_type(ELFTools::Constants::PT_NOTE) do |phdr, idx|
|
|
1028
|
+
next if noted_phdrs.include?(idx)
|
|
1029
|
+
|
|
1030
|
+
s_start = orig_sh_offset
|
|
1031
|
+
s_end = s_start + orig_sh_size
|
|
1032
|
+
p_start = phdr.p_offset
|
|
1033
|
+
p_end = p_start + phdr.p_filesz
|
|
1034
|
+
|
|
1035
|
+
next unless section_bounds_within_segment?(s_start, s_end, p_start, p_end)
|
|
1036
|
+
|
|
1037
|
+
raise PatchError, 'unsupported overlap of SHT_NOTE and PT_NOTE' if p_start != s_start || p_end != s_end
|
|
1038
|
+
|
|
1039
|
+
sync_sec_to_seg(shdr, phdr)
|
|
1040
|
+
|
|
1041
|
+
noted_phdrs << idx
|
|
1042
|
+
end
|
|
1043
|
+
end
|
|
1044
|
+
|
|
824
1045
|
cur_off += Helper.alignup(rsec_data.size, @section_alignment)
|
|
825
1046
|
end
|
|
826
1047
|
@replaced_sections.clear
|
data/lib/patchelf/cli.rb
CHANGED
data/lib/patchelf/exceptions.rb
CHANGED
|
@@ -6,8 +6,10 @@ require 'elftools/exceptions'
|
|
|
6
6
|
module PatchELF
|
|
7
7
|
# Raised on an error during ELF modification.
|
|
8
8
|
class PatchError < ELFTools::ELFError; end
|
|
9
|
+
|
|
9
10
|
# Raised when Dynamic Tag is missing
|
|
10
11
|
class MissingTagError < PatchError; end
|
|
12
|
+
|
|
11
13
|
# Raised on missing Program Header(segment)
|
|
12
14
|
class MissingSegmentError < PatchError; end
|
|
13
15
|
end
|
data/lib/patchelf/helper.rb
CHANGED
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
module PatchELF
|
|
4
4
|
# Helper methods for internal usage.
|
|
5
5
|
module Helper
|
|
6
|
-
# The size of one page.
|
|
7
|
-
PAGE_SIZE = 0x1000
|
|
8
|
-
|
|
9
6
|
module_function
|
|
10
7
|
|
|
11
8
|
# Color codes for pretty print.
|
|
@@ -16,6 +13,23 @@ module PatchELF
|
|
|
16
13
|
error: "\e[38;5;196m" # heavy red
|
|
17
14
|
}.freeze
|
|
18
15
|
|
|
16
|
+
# The size of one page.
|
|
17
|
+
def page_size(e_machine = nil)
|
|
18
|
+
# Different architectures have different minimum section alignments.
|
|
19
|
+
case e_machine
|
|
20
|
+
when ELFTools::Constants::EM_SPARC,
|
|
21
|
+
ELFTools::Constants::EM_MIPS,
|
|
22
|
+
ELFTools::Constants::EM_PPC,
|
|
23
|
+
ELFTools::Constants::EM_PPC64,
|
|
24
|
+
ELFTools::Constants::EM_AARCH64,
|
|
25
|
+
ELFTools::Constants::EM_TILEGX,
|
|
26
|
+
ELFTools::Constants::EM_LOONGARCH
|
|
27
|
+
0x10000
|
|
28
|
+
else
|
|
29
|
+
0x1000
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
19
33
|
# For wrapping string with color codes for prettier inspect.
|
|
20
34
|
# @param [String] str
|
|
21
35
|
# Content to colorize.
|
|
@@ -48,7 +62,7 @@ module PatchELF
|
|
|
48
62
|
# #=> 32
|
|
49
63
|
# aligndown(0x10, 0x8)
|
|
50
64
|
# #=> 16
|
|
51
|
-
def aligndown(val, align =
|
|
65
|
+
def aligndown(val, align = page_size)
|
|
52
66
|
val - (val & (align - 1))
|
|
53
67
|
end
|
|
54
68
|
|
|
@@ -63,7 +77,7 @@ module PatchELF
|
|
|
63
77
|
# #=> 64
|
|
64
78
|
# alignup(0x10, 0x8)
|
|
65
79
|
# #=> 16
|
|
66
|
-
def alignup(val, align =
|
|
80
|
+
def alignup(val, align = page_size)
|
|
67
81
|
(val & (align - 1)).zero? ? val : (aligndown(val, align) + align)
|
|
68
82
|
end
|
|
69
83
|
end
|
data/lib/patchelf/mm.rb
CHANGED
|
@@ -115,7 +115,7 @@ module PatchELF
|
|
|
115
115
|
# prefer backward than forward
|
|
116
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
119
|
extend_forward(loads[idx], @extend_size)
|
|
120
120
|
end
|
|
121
121
|
|
|
@@ -161,7 +161,7 @@ module PatchELF
|
|
|
161
161
|
|
|
162
162
|
seg.header.p_offset += extend_size
|
|
163
163
|
# We have to change align of LOAD segment since ld.so checks it.
|
|
164
|
-
seg.header.p_align = Helper
|
|
164
|
+
seg.header.p_align = Helper.page_size if seg.is_a?(ELFTools::Segments::LoadSegment)
|
|
165
165
|
end
|
|
166
166
|
|
|
167
167
|
@elf.header.e_shoff += extend_size if @elf.header.e_shoff >= threshold
|
data/lib/patchelf/patcher.rb
CHANGED
|
@@ -30,7 +30,7 @@ module PatchELF
|
|
|
30
30
|
@elf = ELFTools::ELFFile.new(File.open(filename))
|
|
31
31
|
@set = {}
|
|
32
32
|
@rpath_sym = :runpath
|
|
33
|
-
@on_error =
|
|
33
|
+
@on_error = logging ? on_error : :exception
|
|
34
34
|
|
|
35
35
|
on_error_syms = %i[exception log silent]
|
|
36
36
|
raise ArgumentError, "on_error must be one of #{on_error_syms}" unless on_error_syms.include?(@on_error)
|
data/lib/patchelf/saver.rb
CHANGED
|
@@ -53,8 +53,8 @@ module PatchELF
|
|
|
53
53
|
def patch_interpreter
|
|
54
54
|
return if @set[:interpreter].nil?
|
|
55
55
|
|
|
56
|
-
new_interp = @set[:interpreter]
|
|
57
|
-
old_interp = @elf.segment_by_type(:interp).interp_name
|
|
56
|
+
new_interp = "#{@set[:interpreter]}\x00"
|
|
57
|
+
old_interp = "#{@elf.segment_by_type(:interp).interp_name}\x00"
|
|
58
58
|
return if old_interp == new_interp
|
|
59
59
|
|
|
60
60
|
# These headers must be found here but not in the proc.
|
|
@@ -184,7 +184,7 @@ module PatchELF
|
|
|
184
184
|
need_size = strtab_string.size + @strtab_extend_requests.reduce(0) { |sum, (str, _)| sum + str.size + 1 }
|
|
185
185
|
dynstr = section_header('.dynstr')
|
|
186
186
|
@mm.malloc(need_size) do |off, vaddr|
|
|
187
|
-
new_str = strtab_string
|
|
187
|
+
new_str = "#{strtab_string}#{@strtab_extend_requests.map(&:first).join("\x00")}\x00"
|
|
188
188
|
inline_patch(off, new_str)
|
|
189
189
|
cur = strtab_string.size
|
|
190
190
|
@strtab_extend_requests.each do |str, block|
|
|
@@ -206,7 +206,7 @@ module PatchELF
|
|
|
206
206
|
# @yieldparam [Integer] idx
|
|
207
207
|
# @yieldreturn [void]
|
|
208
208
|
def reg_str_table(str, &block)
|
|
209
|
-
idx = strtab_string.index(str
|
|
209
|
+
idx = strtab_string.index("#{str}\x00")
|
|
210
210
|
# Request string is already exist
|
|
211
211
|
return yield idx if idx
|
|
212
212
|
|
data/lib/patchelf/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: patchelf
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- david942j
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-11-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: elftools
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 1.
|
|
19
|
+
version: '1.2'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 1.
|
|
26
|
+
version: '1.2'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: rake
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -44,28 +44,28 @@ dependencies:
|
|
|
44
44
|
requirements:
|
|
45
45
|
- - "~>"
|
|
46
46
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '3
|
|
47
|
+
version: '3'
|
|
48
48
|
type: :development
|
|
49
49
|
prerelease: false
|
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
51
|
requirements:
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '3
|
|
54
|
+
version: '3'
|
|
55
55
|
- !ruby/object:Gem::Dependency
|
|
56
56
|
name: rubocop
|
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
|
58
58
|
requirements:
|
|
59
59
|
- - "~>"
|
|
60
60
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
61
|
+
version: '1'
|
|
62
62
|
type: :development
|
|
63
63
|
prerelease: false
|
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
65
|
requirements:
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
68
|
+
version: '1'
|
|
69
69
|
- !ruby/object:Gem::Dependency
|
|
70
70
|
name: simplecov
|
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -73,6 +73,9 @@ dependencies:
|
|
|
73
73
|
- - "~>"
|
|
74
74
|
- !ruby/object:Gem::Version
|
|
75
75
|
version: '0.17'
|
|
76
|
+
- - "<"
|
|
77
|
+
- !ruby/object:Gem::Version
|
|
78
|
+
version: '0.18'
|
|
76
79
|
type: :development
|
|
77
80
|
prerelease: false
|
|
78
81
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -80,6 +83,9 @@ dependencies:
|
|
|
80
83
|
- - "~>"
|
|
81
84
|
- !ruby/object:Gem::Version
|
|
82
85
|
version: '0.17'
|
|
86
|
+
- - "<"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0.18'
|
|
83
89
|
- !ruby/object:Gem::Dependency
|
|
84
90
|
name: tty-platform
|
|
85
91
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -133,7 +139,8 @@ files:
|
|
|
133
139
|
homepage: https://github.com/david942j/patchelf.rb
|
|
134
140
|
licenses:
|
|
135
141
|
- MIT
|
|
136
|
-
metadata:
|
|
142
|
+
metadata:
|
|
143
|
+
rubygems_mfa_required: 'true'
|
|
137
144
|
post_install_message:
|
|
138
145
|
rdoc_options: []
|
|
139
146
|
require_paths:
|
|
@@ -142,14 +149,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
142
149
|
requirements:
|
|
143
150
|
- - ">="
|
|
144
151
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '2.
|
|
152
|
+
version: '2.6'
|
|
146
153
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
147
154
|
requirements:
|
|
148
155
|
- - ">="
|
|
149
156
|
- !ruby/object:Gem::Version
|
|
150
157
|
version: '0'
|
|
151
158
|
requirements: []
|
|
152
|
-
rubygems_version: 3.1.
|
|
159
|
+
rubygems_version: 3.1.6
|
|
153
160
|
signing_key:
|
|
154
161
|
specification_version: 4
|
|
155
162
|
summary: patchelf
|