@@ -12,56 +12,88 @@ import (
1212 "strings"
1313)
1414
15- // glibcPatcher patches ELF binaries to use an alternative version of glibc.
16- type glibcPatcher struct {
15+ // libPatcher patches ELF binaries to use an alternative version of glibc.
16+ type libPatcher struct {
1717 // ld is the absolute path to the new dynamic linker (ld.so).
1818 ld string
1919
2020 // rpath is the new RPATH with the directories containing the new libc
2121 // shared objects (libc.so) and other libraries.
2222 rpath []string
23- }
2423
25- // newGlibcPatcher creates a new glibcPatcher and verifies that it can find the
26- // shared object files in glibc.
27- func newGlibcPatcher (glibc * packageFS ) (* glibcPatcher , error ) {
28- patcher := & glibcPatcher {}
24+ // needed are shared libraries to add as dependencies (DT_NEEDED).
25+ needed []string
26+ }
2927
28+ // setGlibc configures the patcher to use the dynamic linker and libc libraries
29+ // in pkg.
30+ func (p * libPatcher ) setGlibc (pkg * packageFS ) error {
3031 // Verify that we can find a directory with libc in it.
3132 glob := "lib*/libc.so*"
32- matches , _ := fs .Glob (glibc , glob )
33+ matches , _ := fs .Glob (pkg , glob )
3334 if len (matches ) == 0 {
34- return nil , fmt .Errorf ("cannot find libc.so file matching %q" , glob )
35+ return fmt .Errorf ("cannot find libc.so file matching %q" , glob )
3536 }
3637 for i := range matches {
3738 matches [i ] = path .Dir (matches [i ])
3839 }
39- slices .Sort (matches ) // pick the shortest name: lib < lib32 < lib64 < libx32
40+ // Pick the shortest name: lib < lib32 < lib64 < libx32
41+ //
42+ // - lib is usually a symlink to the correct arch (e.g., lib -> lib64)
43+ // - *.so is usually a symlink to the correct version (e.g., foo.so -> foo.so.2)
44+ slices .Sort (matches )
4045
41- lib , err := glibc .OSPath (matches [0 ])
46+ lib , err := pkg .OSPath (matches [0 ])
4247 if err != nil {
43- return nil , err
48+ return err
4449 }
45- patcher .rpath = append (patcher .rpath , lib )
50+ p .rpath = append (p .rpath , lib )
4651 slog .Debug ("found new libc directory" , "path" , lib )
4752
4853 // Verify that we can find the new dynamic linker.
4954 glob = "lib*/ld-linux*.so*"
50- matches , _ = fs .Glob (glibc , glob )
55+ matches , _ = fs .Glob (pkg , glob )
5156 if len (matches ) == 0 {
52- return nil , fmt .Errorf ("cannot find ld.so file matching %q" , glob )
57+ return fmt .Errorf ("cannot find ld.so file matching %q" , glob )
5358 }
5459 slices .Sort (matches )
55- patcher .ld , err = glibc .OSPath (matches [0 ])
60+ p .ld , err = pkg .OSPath (matches [0 ])
5661 if err != nil {
57- return nil , err
62+ return err
63+ }
64+ slog .Debug ("found new dynamic linker" , "path" , p .ld )
65+ return nil
66+ }
67+
68+ // setGlibc configures the patcher to use the standard C++ and gcc libraries in
69+ // pkg.
70+ func (p * libPatcher ) setGcc (pkg * packageFS ) error {
71+ // Verify that we can find a directory with libstdc++.so in it.
72+ glob := "lib*/libstdc++.so*"
73+ matches , _ := fs .Glob (pkg , glob )
74+ if len (matches ) == 0 {
75+ return fmt .Errorf ("cannot find libstdc++.so file matching %q" , glob )
5876 }
59- slog .Debug ("found new dynamic linker" , "path" , patcher .ld )
77+ for i := range matches {
78+ matches [i ] = path .Dir (matches [i ])
79+ }
80+ // Pick the shortest name: lib < lib32 < lib64 < libx32
81+ //
82+ // - lib is usually a symlink to the correct arch (e.g., lib -> lib64)
83+ // - *.so is usually a symlink to the correct version (e.g., foo.so -> foo.so.2)
84+ slices .Sort (matches )
6085
61- return patcher , nil
86+ lib , err := pkg .OSPath (matches [0 ])
87+ if err != nil {
88+ return err
89+ }
90+ p .rpath = append (p .rpath , lib )
91+ p .needed = append (p .needed , "libstdc++.so" )
92+ slog .Debug ("found new libstdc++ directory" , "path" , lib )
93+ return nil
6294}
6395
64- func (g * glibcPatcher ) prependRPATH (libPkg * packageFS ) {
96+ func (p * libPatcher ) prependRPATH (libPkg * packageFS ) {
6597 glob := "lib*/*.so*"
6698 matches , _ := fs .Glob (libPkg , glob )
6799 if len (matches ) == 0 {
@@ -80,13 +112,13 @@ func (g *glibcPatcher) prependRPATH(libPkg *packageFS) {
80112 continue
81113 }
82114 }
83- g .rpath = append (matches , g .rpath ... )
115+ p .rpath = append (p .rpath , matches ... )
84116 slog .Debug ("prepended package lib dirs to RPATH" , "pkg" , libPkg .storePath , "dirs" , matches )
85117}
86118
87119// patch applies glibc patches to a binary and writes the patched result to
88120// outPath. It does not modify the original binary in-place.
89- func (g * glibcPatcher ) patch (ctx context.Context , path , outPath string ) error {
121+ func (p * libPatcher ) patch (ctx context.Context , path , outPath string ) error {
90122 cmd := & patchelf {PrintInterpreter : true }
91123 out , err := cmd .run (ctx , path )
92124 if err != nil {
@@ -102,8 +134,9 @@ func (g *glibcPatcher) patch(ctx context.Context, path, outPath string) error {
102134 oldRpath := strings .Split (string (out ), ":" )
103135
104136 cmd = & patchelf {
105- SetInterpreter : g .ld ,
106- SetRPATH : append (g .rpath , oldRpath ... ),
137+ SetInterpreter : p .ld ,
138+ SetRPATH : append (p .rpath , oldRpath ... ),
139+ AddNeeded : p .needed ,
107140 Output : outPath ,
108141 }
109142 slog .Debug ("patching glibc on binary" ,
@@ -123,6 +156,8 @@ type patchelf struct {
123156 SetInterpreter string
124157 PrintInterpreter bool
125158
159+ AddNeeded []string
160+
126161 Output string
127162}
128163
@@ -141,6 +176,9 @@ func (p *patchelf) run(ctx context.Context, elf string) ([]byte, error) {
141176 if p .PrintInterpreter {
142177 cmd .Args = append (cmd .Args , "--print-interpreter" )
143178 }
179+ for _ , needed := range p .AddNeeded {
180+ cmd .Args = append (cmd .Args , "--add-needed" , needed )
181+ }
144182 if p .Output != "" {
145183 cmd .Args = append (cmd .Args , "--output" , p .Output )
146184 }
0 commit comments