From 7829a7ade9ded4a2eb981542d82a408b3ac1af82 Mon Sep 17 00:00:00 2001
From: Mateusz Tabaka <tab.debugteam@gmail.com>
Date: Tue, 29 Sep 2020 19:30:42 +0200
Subject: [PATCH] Add support for RUNPATH

---
 src/ld_so/library.rs |  1 +
 src/ld_so/linker.rs  | 40 +++++++++++++++++++++++++++++++++-------
 src/ld_so/start.rs   |  7 ++-----
 3 files changed, 36 insertions(+), 12 deletions(-)

diff --git a/src/ld_so/library.rs b/src/ld_so/library.rs
index 88958e01..19862f7e 100644
--- a/src/ld_so/library.rs
+++ b/src/ld_so/library.rs
@@ -36,6 +36,7 @@ pub struct Library {
     pub dep_tree: DepTree,
     /// A set used to detect circular dependencies in the Linker::load function
     pub cir_dep: BTreeSet<String>,
+    pub runpath: Option<String>,
 }
 impl Library {
     pub fn new() -> Library {
diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs
index a091e857..3f6b4d4c 100644
--- a/src/ld_so/linker.rs
+++ b/src/ld_so/linker.rs
@@ -14,7 +14,7 @@ use goblin::{
     elf::{
         header::ET_DYN,
         program_header,
-        r#dyn::{Dyn, DT_DEBUG},
+        r#dyn::{Dyn, DT_DEBUG, DT_RUNPATH},
         reloc, sym, Elf,
     },
     error::{Error, Result},
@@ -63,7 +63,8 @@ impl Symbol {
 pub struct Linker {
     // Used by load
     /// Library path to search when loading library by name
-    library_path: String,
+    default_library_path: String,
+    ld_library_path: Option<String>,
     root: Library,
     verbose: bool,
     tls_index_offset: usize,
@@ -73,9 +74,10 @@ pub struct Linker {
 }
 
 impl Linker {
-    pub fn new(library_path: &str, verbose: bool) -> Self {
+    pub fn new(ld_library_path: Option<String>, verbose: bool) -> Self {
         Self {
-            library_path: library_path.to_string(),
+            default_library_path: "/lib".to_string(),
+            ld_library_path: ld_library_path,
             root: Library::new(),
             verbose,
             tls_index_offset: 0,
@@ -139,13 +141,30 @@ impl Linker {
     ) -> Result<Vec<DepTree>> {
         let elf = Elf::parse(&data)?;
         //println!("{:#?}", elf);
+
+        // search for RUNPATH
+        lib.runpath = if let Some(dynamic) = elf.dynamic {
+            let entry = dynamic.dyns.iter().find(|d| d.d_tag == DT_RUNPATH);
+            match entry {
+                Some(entry) => {
+                    let path = elf
+                        .dynstrtab
+                        .get(entry.d_val as usize)
+                        .ok_or(Error::Malformed("Missing RUNPATH in dynstrtab".to_string()))??;
+                    Some(path.to_string())
+                }
+                _ => None,
+            }
+        } else {
+            None
+        };
+
         let mut deps = Vec::new();
         for library in elf.libraries.iter() {
             if let Some(dep) = self._load_library(library, lib)? {
                 deps.push(dep);
             }
         }
-        let elf = Elf::parse(&data)?;
         let key = match elf.soname {
             Some(soname) => soname,
             _ => name,
@@ -171,8 +190,15 @@ impl Linker {
         } else if name.contains('/') {
             Ok(Some(self.load_recursive(name, name, lib)?))
         } else {
-            let library_path = self.library_path.clone();
-            for part in library_path.split(PATH_SEP) {
+            let mut paths = Vec::new();
+            if let Some(ld_library_path) = &self.ld_library_path {
+                paths.push(ld_library_path);
+            }
+            if let Some(runpath) = &lib.runpath {
+                paths.push(runpath);
+            }
+            paths.push(&self.default_library_path);
+            for part in paths.iter() {
                 let path = if part.is_empty() {
                     format!("./{}", name)
                 } else {
diff --git a/src/ld_so/start.rs b/src/ld_so/start.rs
index 153b8992..6a9439d0 100644
--- a/src/ld_so/start.rs
+++ b/src/ld_so/start.rs
@@ -183,10 +183,7 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
     }
 
     // Some variables that will be overridden by environment and auxiliary vectors
-    let library_path = match envs.get("LD_LIBRARY_PATH") {
-        Some(lib_path) => lib_path.to_owned() + ":/lib",
-        None => "/lib".to_owned(),
-    };
+    let ld_library_path = envs.get("LD_LIBRARY_PATH").map(|s| s.to_owned());
 
     let name_or_path = if is_manual {
         // ld.so is run directly by user and not via execve() or similar systemcall
@@ -234,7 +231,7 @@ pub extern "C" fn relibc_ld_so_start(sp: &'static mut Stack, ld_entry: usize) ->
         }
         pr
     };
-    let mut linker = Linker::new(&library_path, false);
+    let mut linker = Linker::new(ld_library_path, false);
     match linker.load(&path, &path) {
         Ok(()) => (),
         Err(err) => {
-- 
GitLab