Skip to content

Commit

Permalink
Refactoring
Browse files Browse the repository at this point in the history
Update README
Update lib4bin (add deps check)
  • Loading branch information
VHSgunzo committed Nov 5, 2024
1 parent 426ee48 commit 404f04b
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 48 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "sharun"
version = "0.0.3"
version = "0.0.4"
readme = "README.md"
license = "MIT"
repository = "https://github.com/VHSgunzo/sharun"
Expand Down
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# sharun
Run dynamically linked ELF binaries everywhere (musl and glibc are supported)
Run dynamically linked ELF binaries everywhere (musl and glibc are supported).
* 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.
* [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.

## Supported architectures:
* aarch64
Expand Down Expand Up @@ -34,23 +36,34 @@ cargo build --release
| -h, --help Print help
|
[ Environments ]:
| SHARUN_LDNAME=ld.so Specifies the name of the linker
| SHARUN_LDNAME=ld.so Specifies the name of the interpreter
```

## Examples:
```
# create a directory and cd
mkdir test && cd test
# and copy 'sharun' to this directory
cp ../target/x86_64-unknown-linux-musl/release/sharun .
# run lib4bin with the paths to the binary files that you want to make portable
../lib4bin /bin/{curl,bash,ls}
# and copy sharun to this directory
cp ../target/x86_64-unknown-linux-musl/release/sharun .
# or for correct /proc/self/exe you can use HARD_LINKS=1
HARD_LINKS=1 ../lib4bin /bin/{curl,bash,ls}
# this ^ will create hard links to 'sharun' in the 'bin' directory
# now you can move 'test' dir to other linux system and run binaries from the 'bin' dir
./bin/ls -lha
# or specify them as an argument to sharun
# or specify them as an argument to 'sharun'
./sharun ls -lha
```

# Screenshots:
![alt text](img/image.png)

## References
* [userland-execve](https://crates.io/crates/userland-execve)
* https://brioche.dev/blog/portable-dynamically-linked-packages-on-linux
Binary file added img/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions lib4bin
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ CREATE_LINKS=${CREATE_LINKS:=1}
#PATCH_RPATH=1
#PATCH_INTERPRETER=1

check_deps() {
local ret=0
for bin in file strip patchelf find
do ! command -v $bin &>/dev/null && \
echo -e "$RED[ ERROR ]: $BLUE[$bin]$YELLOW not found!$RESETCOLOR" && \
ret=1
done
if [ "$ret" != 0 ]
then
echo -e "${GREEN}You need to install lib4bin dependencies: ${BLUE}file binutils patchelf findutils$RESETCOLOR"
exit 1
fi
}

try_strip() {
[ "$STRIP" == 1 ] && \
strip -s -R .comment --strip-unneeded "$1"
Expand Down Expand Up @@ -64,6 +78,8 @@ repath_needed_libs() {
done
}
check_deps
#IFS=$'\n'
if [ ! -n "$BINARY_LIST" ]
then
Expand Down
86 changes: 44 additions & 42 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,46 @@
use std::{
fs::File,
io::Read,
{env, fs},
path::Path,
str::FromStr,
env,
ffi::CString,
str::FromStr,
process::exit,
collections::HashSet
collections::HashSet,
path::{Path, PathBuf},
io::{Read, Result, Error},
fs::{File, write, read_to_string}
};

use walkdir::WalkDir;


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


fn get_linker_name(library_path: &str) -> String {
#[cfg(target_arch = "x86_64")] // target x86_64-unknown-linux-musl
let linkers = vec![
"ld-linux-x86-64.so.2",
"ld-musl-x86_64.so.1",
"ld-linux.so.2",
];
#[cfg(target_arch = "aarch64")] // target aarch64-unknown-linux-musl
let linkers = vec![
"ld-linux-aarch64.so.1",
"ld-musl-aarch64.so.1",
];
for linker in linkers {
let linker_path = Path::new(library_path).join(linker);
if linker_path.exists() {
return linker_path.file_name().unwrap().to_str().unwrap().to_string()
fn get_interpreter(library_path: &str) -> Result<PathBuf> {
let mut interpreters = Vec::new();
if let Ok(ldname) = env::var("SHARUN_LDNAME") {
if !ldname.is_empty() {
interpreters.push(ldname)
}
} else {
#[cfg(target_arch = "x86_64")] // target x86_64-unknown-linux-musl
interpreters.append(&mut vec![
"ld-linux-x86-64.so.2".into(),
"ld-musl-x86_64.so.1".into(),
"ld-linux.so.2".into()
]);
#[cfg(target_arch = "aarch64")] // target aarch64-unknown-linux-musl
interpreters.append(&mut vec![
"ld-linux-aarch64.so.1".into(),
"ld-musl-aarch64.so.1".into()
]);
}
for interpreter in interpreters {
let interpreter_path = Path::new(library_path).join(interpreter);
if interpreter_path.exists() {
return Ok(interpreter_path)
}
}
LINKER_NAME.to_string()
Err(Error::last_os_error())
}

fn realpath(path: &str) -> String {
Expand All @@ -51,7 +57,7 @@ fn is_file(path: &str) -> bool {
path.is_file()
}

fn is_elf32(file_path: &str) -> std::io::Result<bool> {
fn is_elf32(file_path: &str) -> Result<bool> {
let mut file = File::open(file_path)?;
let mut buff = [0u8; 5];
file.read_exact(&mut buff)?;
Expand Down Expand Up @@ -87,7 +93,7 @@ fn gen_library_path(library_path: &mut String) -> i32 {
library_path.push(':');
library_path.push_str(&new_paths.join(":"))
}
if let Err(err) = fs::write(&lib_path_file, &library_path
if let Err(err) = write(&lib_path_file, &library_path
.replace(":", "\n")
.replace(&old_library_path, "+")
) {
Expand Down Expand Up @@ -119,21 +125,21 @@ fn print_usage() {
| -h, --help Print help
|
[ Environments ]:
| SHARUN_LDNAME=ld.so Specifies the name of the linker",
| SHARUN_LDNAME=ld.so Specifies the name of the interpreter",
env!("CARGO_PKG_DESCRIPTION"))));
}

fn main() {
let sharun = env::current_exe().unwrap();
let mut exec_args: Vec<String> = env::args().collect();

let mut sharun_dir = sharun.parent().unwrap().to_str().unwrap().to_string();
let lower_dir = format!("{sharun_dir}/../");
if basename(&sharun_dir) == "bin" &&
is_file(&format!("{lower_dir}{SHARUN_NAME}")) {
sharun_dir = realpath(&lower_dir)
}

let mut exec_args: Vec<String> = env::args().collect();

let shared_dir = format!("{sharun_dir}/shared");
let shared_bin = format!("{shared_dir}/bin");
let shared_lib = format!("{shared_dir}/lib");
Expand Down Expand Up @@ -186,18 +192,14 @@ fn main() {
library_path = shared_lib
}

let linker_name = env::var("SHARUN_LDNAME")
.unwrap_or(get_linker_name(&library_path));
let linker = &format!("{library_path}/{linker_name}");

if !Path::new(linker).exists() {
eprintln!("Linker not found: {linker}");
let interpreter = get_interpreter(&library_path).unwrap_or_else(|_|{
eprintln!("The interpreter was not found!");
exit(1)
}
});

let lib_path_file = format!("{library_path}/lib.path");
if Path::new(&lib_path_file).exists() {
library_path = fs::read_to_string(lib_path_file).unwrap().trim()
library_path = read_to_string(lib_path_file).unwrap().trim()
.replace("\n", ":")
.replace("+", &library_path)
} else {
Expand All @@ -209,19 +211,19 @@ fn main() {
format!("{}={}", key, value)
).unwrap()).collect();

let mut linker_args = vec![
CString::from_str(linker).unwrap(),
let mut interpreter_args = vec![
CString::from_str(&interpreter.to_string_lossy()).unwrap(),
CString::new("--library-path").unwrap(),
CString::new(library_path).unwrap(),
CString::new(bin).unwrap()
];
for arg in exec_args {
linker_args.push(CString::from_str(&arg).unwrap())
interpreter_args.push(CString::from_str(&arg).unwrap())
}

userland_execve::exec(
Path::new(linker),
&linker_args,
interpreter.as_path(),
&interpreter_args,
&envs,
)
}

0 comments on commit 404f04b

Please sign in to comment.