Skip to content

Commit 404f04b

Browse files
committed
Refactoring
Update README Update lib4bin (add deps check)
1 parent 426ee48 commit 404f04b

File tree

5 files changed

+79
-48
lines changed

5 files changed

+79
-48
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sharun"
3-
version = "0.0.3"
3+
version = "0.0.4"
44
readme = "README.md"
55
license = "MIT"
66
repository = "https://github.com/VHSgunzo/sharun"

README.md

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# sharun
2-
Run dynamically linked ELF binaries everywhere (musl and glibc are supported)
2+
Run dynamically linked ELF binaries everywhere (musl and glibc are supported).
3+
* This works with [userland-execve](https://github.com/io12/userland-execve-rust) by mapping the interpreter (such as ld-linux-x86-64.so.2) into memory, creating a stack for it (containing the auxiliary vector, arguments, and environment variables), and then jumping to the entry point with the new stack.
4+
* [lib4bin](https://github.com/VHSgunzo/sharun/blob/main/lib4bin) pulls out the binary file and all the libraries on which it depends, strip it (you can disable it with `STRIP=0` env var, see [lib4bin](https://github.com/VHSgunzo/sharun/blob/main/lib4bin#L15)) and forms the `bin`, `shared/{bin,lib,lib32}` directories (see [screenshots](https://github.com/VHSgunzo/sharun?tab=readme-ov-file#screenshots)) and generate a file `shared/{lib,lib32}/lib.path` with a list of all directories that contain libraries for pass it to interpreter `--library-path`. The paths in this file are specified on a new line with a `+` at the beginning and relative to the directory in which it is located.
35

46
## Supported architectures:
57
* aarch64
@@ -34,23 +36,34 @@ cargo build --release
3436
| -h, --help Print help
3537
|
3638
[ Environments ]:
37-
| SHARUN_LDNAME=ld.so Specifies the name of the linker
39+
| SHARUN_LDNAME=ld.so Specifies the name of the interpreter
3840
```
3941

4042
## Examples:
4143
```
4244
# create a directory and cd
4345
mkdir test && cd test
4446
47+
# and copy 'sharun' to this directory
48+
cp ../target/x86_64-unknown-linux-musl/release/sharun .
49+
4550
# run lib4bin with the paths to the binary files that you want to make portable
4651
../lib4bin /bin/{curl,bash,ls}
4752
48-
# and copy sharun to this directory
49-
cp ../target/x86_64-unknown-linux-musl/release/sharun .
53+
# or for correct /proc/self/exe you can use HARD_LINKS=1
54+
HARD_LINKS=1 ../lib4bin /bin/{curl,bash,ls}
55+
# this ^ will create hard links to 'sharun' in the 'bin' directory
5056
5157
# now you can move 'test' dir to other linux system and run binaries from the 'bin' dir
5258
./bin/ls -lha
5359
54-
# or specify them as an argument to sharun
60+
# or specify them as an argument to 'sharun'
5561
./sharun ls -lha
5662
```
63+
64+
# Screenshots:
65+
![alt text](img/image.png)
66+
67+
## References
68+
* [userland-execve](https://crates.io/crates/userland-execve)
69+
* https://brioche.dev/blog/portable-dynamically-linked-packages-on-linux

img/image.png

19.8 KB
Loading

lib4bin

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,20 @@ CREATE_LINKS=${CREATE_LINKS:=1}
2323
#PATCH_RPATH=1
2424
#PATCH_INTERPRETER=1
2525

26+
check_deps() {
27+
local ret=0
28+
for bin in file strip patchelf find
29+
do ! command -v $bin &>/dev/null && \
30+
echo -e "$RED[ ERROR ]: $BLUE[$bin]$YELLOW not found!$RESETCOLOR" && \
31+
ret=1
32+
done
33+
if [ "$ret" != 0 ]
34+
then
35+
echo -e "${GREEN}You need to install lib4bin dependencies: ${BLUE}file binutils patchelf findutils$RESETCOLOR"
36+
exit 1
37+
fi
38+
}
39+
2640
try_strip() {
2741
[ "$STRIP" == 1 ] && \
2842
strip -s -R .comment --strip-unneeded "$1"
@@ -64,6 +78,8 @@ repath_needed_libs() {
6478
done
6579
}
6680
81+
check_deps
82+
6783
#IFS=$'\n'
6884
if [ ! -n "$BINARY_LIST" ]
6985
then

src/main.rs

Lines changed: 44 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
use std::{
2-
fs::File,
3-
io::Read,
4-
{env, fs},
5-
path::Path,
6-
str::FromStr,
2+
env,
73
ffi::CString,
4+
str::FromStr,
85
process::exit,
9-
collections::HashSet
6+
collections::HashSet,
7+
path::{Path, PathBuf},
8+
io::{Read, Result, Error},
9+
fs::{File, write, read_to_string}
1010
};
1111

1212
use walkdir::WalkDir;
1313

1414

1515
const SHARUN_NAME: &str = env!("CARGO_PKG_NAME");
16-
const LINKER_NAME: &str = "ld-linux-x86-64.so.2";
1716

1817

19-
fn get_linker_name(library_path: &str) -> String {
20-
#[cfg(target_arch = "x86_64")] // target x86_64-unknown-linux-musl
21-
let linkers = vec![
22-
"ld-linux-x86-64.so.2",
23-
"ld-musl-x86_64.so.1",
24-
"ld-linux.so.2",
25-
];
26-
#[cfg(target_arch = "aarch64")] // target aarch64-unknown-linux-musl
27-
let linkers = vec![
28-
"ld-linux-aarch64.so.1",
29-
"ld-musl-aarch64.so.1",
30-
];
31-
for linker in linkers {
32-
let linker_path = Path::new(library_path).join(linker);
33-
if linker_path.exists() {
34-
return linker_path.file_name().unwrap().to_str().unwrap().to_string()
18+
fn get_interpreter(library_path: &str) -> Result<PathBuf> {
19+
let mut interpreters = Vec::new();
20+
if let Ok(ldname) = env::var("SHARUN_LDNAME") {
21+
if !ldname.is_empty() {
22+
interpreters.push(ldname)
23+
}
24+
} else {
25+
#[cfg(target_arch = "x86_64")] // target x86_64-unknown-linux-musl
26+
interpreters.append(&mut vec![
27+
"ld-linux-x86-64.so.2".into(),
28+
"ld-musl-x86_64.so.1".into(),
29+
"ld-linux.so.2".into()
30+
]);
31+
#[cfg(target_arch = "aarch64")] // target aarch64-unknown-linux-musl
32+
interpreters.append(&mut vec![
33+
"ld-linux-aarch64.so.1".into(),
34+
"ld-musl-aarch64.so.1".into()
35+
]);
36+
}
37+
for interpreter in interpreters {
38+
let interpreter_path = Path::new(library_path).join(interpreter);
39+
if interpreter_path.exists() {
40+
return Ok(interpreter_path)
3541
}
3642
}
37-
LINKER_NAME.to_string()
43+
Err(Error::last_os_error())
3844
}
3945

4046
fn realpath(path: &str) -> String {
@@ -51,7 +57,7 @@ fn is_file(path: &str) -> bool {
5157
path.is_file()
5258
}
5359

54-
fn is_elf32(file_path: &str) -> std::io::Result<bool> {
60+
fn is_elf32(file_path: &str) -> Result<bool> {
5561
let mut file = File::open(file_path)?;
5662
let mut buff = [0u8; 5];
5763
file.read_exact(&mut buff)?;
@@ -87,7 +93,7 @@ fn gen_library_path(library_path: &mut String) -> i32 {
8793
library_path.push(':');
8894
library_path.push_str(&new_paths.join(":"))
8995
}
90-
if let Err(err) = fs::write(&lib_path_file, &library_path
96+
if let Err(err) = write(&lib_path_file, &library_path
9197
.replace(":", "\n")
9298
.replace(&old_library_path, "+")
9399
) {
@@ -119,21 +125,21 @@ fn print_usage() {
119125
| -h, --help Print help
120126
|
121127
[ Environments ]:
122-
| SHARUN_LDNAME=ld.so Specifies the name of the linker",
128+
| SHARUN_LDNAME=ld.so Specifies the name of the interpreter",
123129
env!("CARGO_PKG_DESCRIPTION"))));
124130
}
125131

126132
fn main() {
127133
let sharun = env::current_exe().unwrap();
134+
let mut exec_args: Vec<String> = env::args().collect();
135+
128136
let mut sharun_dir = sharun.parent().unwrap().to_str().unwrap().to_string();
129137
let lower_dir = format!("{sharun_dir}/../");
130138
if basename(&sharun_dir) == "bin" &&
131139
is_file(&format!("{lower_dir}{SHARUN_NAME}")) {
132140
sharun_dir = realpath(&lower_dir)
133141
}
134142

135-
let mut exec_args: Vec<String> = env::args().collect();
136-
137143
let shared_dir = format!("{sharun_dir}/shared");
138144
let shared_bin = format!("{shared_dir}/bin");
139145
let shared_lib = format!("{shared_dir}/lib");
@@ -186,18 +192,14 @@ fn main() {
186192
library_path = shared_lib
187193
}
188194

189-
let linker_name = env::var("SHARUN_LDNAME")
190-
.unwrap_or(get_linker_name(&library_path));
191-
let linker = &format!("{library_path}/{linker_name}");
192-
193-
if !Path::new(linker).exists() {
194-
eprintln!("Linker not found: {linker}");
195+
let interpreter = get_interpreter(&library_path).unwrap_or_else(|_|{
196+
eprintln!("The interpreter was not found!");
195197
exit(1)
196-
}
198+
});
197199

198200
let lib_path_file = format!("{library_path}/lib.path");
199201
if Path::new(&lib_path_file).exists() {
200-
library_path = fs::read_to_string(lib_path_file).unwrap().trim()
202+
library_path = read_to_string(lib_path_file).unwrap().trim()
201203
.replace("\n", ":")
202204
.replace("+", &library_path)
203205
} else {
@@ -209,19 +211,19 @@ fn main() {
209211
format!("{}={}", key, value)
210212
).unwrap()).collect();
211213

212-
let mut linker_args = vec![
213-
CString::from_str(linker).unwrap(),
214+
let mut interpreter_args = vec![
215+
CString::from_str(&interpreter.to_string_lossy()).unwrap(),
214216
CString::new("--library-path").unwrap(),
215217
CString::new(library_path).unwrap(),
216218
CString::new(bin).unwrap()
217219
];
218220
for arg in exec_args {
219-
linker_args.push(CString::from_str(&arg).unwrap())
221+
interpreter_args.push(CString::from_str(&arg).unwrap())
220222
}
221223

222224
userland_execve::exec(
223-
Path::new(linker),
224-
&linker_args,
225+
interpreter.as_path(),
226+
&interpreter_args,
225227
&envs,
226228
)
227229
}

0 commit comments

Comments
 (0)