diff --git a/src/ld_so/linker.rs b/src/ld_so/linker.rs
index fe98fae5f7ceaa18aec696b97be8ed51414ec025..848645146058b9695a7493b6902ce18656904226 100644
--- a/src/ld_so/linker.rs
+++ b/src/ld_so/linker.rs
@@ -39,6 +39,8 @@ pub struct Linker {
     // Used by link
     /// Global symbols
     globals: BTreeMap<String, usize>,
+    /// Weak symbols
+    weak_syms: BTreeMap<String, usize>,
     /// Loaded library in-memory data
     mmaps: BTreeMap<String, &'static mut [u8]>,
     verbose: bool,
@@ -51,6 +53,7 @@ impl Linker {
             library_path: library_path.to_string(),
             objects: BTreeMap::new(),
             globals: BTreeMap::new(),
+            weak_syms: BTreeMap::new(),
             mmaps: BTreeMap::new(),
             verbose,
             tls_index_offset: 0,
@@ -129,21 +132,43 @@ impl Linker {
         }
     }
 
-    fn collect_syms(elf: &Elf, mmap: &[u8], verbose: bool) -> Result<BTreeMap<String, usize>> {
+    fn collect_syms(
+        elf: &Elf,
+        mmap: &[u8],
+        verbose: bool,
+    ) -> Result<(BTreeMap<String, usize>, BTreeMap<String, usize>)> {
         let mut globals = BTreeMap::new();
+        let mut weak_syms = BTreeMap::new();
         for sym in elf.dynsyms.iter() {
-            if sym.st_bind() == sym::STB_GLOBAL && sym.st_value != 0 {
-                if let Some(name_res) = elf.dynstrtab.get(sym.st_name) {
-                    let name = name_res?;
-                    let value = mmap.as_ptr() as usize + sym.st_value as usize;
+            let bind = sym.st_bind();
+            if sym.st_value == 0 || ![sym::STB_GLOBAL, sym::STB_WEAK].contains(&bind) {
+                continue;
+            }
+            let name: String;
+            let value: usize;
+            if let Some(name_res) = elf.dynstrtab.get(sym.st_name) {
+                name = name_res?.to_string();
+                value = mmap.as_ptr() as usize + sym.st_value as usize;
+            } else {
+                continue;
+            }
+            match sym.st_bind() {
+                sym::STB_GLOBAL => {
                     if verbose {
                         println!("  global {}: {:x?} = {:#x}", &name, sym, value);
                     }
-                    globals.insert(name.to_string(), value);
+                    globals.insert(name, value);
+                }
+                sym::STB_WEAK => {
+                    if verbose {
+                        println!("  weak {}: {:x?} = {:#x}", &name, sym, value);
+                    }
+                    weak_syms.insert(name, value);
                 }
+                _ => unreachable!(),
             }
         }
-        return Ok(globals);
+        return Ok((globals, weak_syms));
     }
 
     pub fn get_sym(&self, name: &str) -> Option<usize> {
@@ -152,6 +177,11 @@ impl Linker {
                 println!("    sym {} = {:#x}", name, value);
             }
             Some(*value)
+        } else if let Some(value) = self.weak_syms.get(name) {
+            if self.verbose {
+                println!("    sym {} = {:#x}", name, value);
+            }
+            Some(*value)
         } else {
             if self.verbose {
                 println!("    sym {} = undefined", name);
@@ -252,8 +282,9 @@ impl Linker {
             if self.verbose {
                 println!("  mmap {:p}, {:#x}", mmap.as_mut_ptr(), mmap.len());
             }
-            let globals = Linker::collect_syms(&elf, &mmap, self.verbose)?;
+            let (globals, weak_syms) = Linker::collect_syms(&elf, &mmap, self.verbose)?;
             self.globals.extend(globals.into_iter());
+            self.weak_syms.extend(weak_syms.into_iter());
             self.mmaps.insert(elf_name.to_string(), mmap);
         }