diff --git a/include/LIEF/ELF/Builder.hpp b/include/LIEF/ELF/Builder.hpp
index f63ab99..2197e68 100644
--- a/include/LIEF/ELF/Builder.hpp
+++ b/include/LIEF/ELF/Builder.hpp
@@ -93,6 +93,8 @@ class LIEF_API Builder {
   template<typename ELF_T>
   void build_symbol_gnuhash(void);
 
+  uint32_t sort_dynamic_symbols(void);
+
   void build_empty_symbol_gnuhash(void);
 
   template<typename ELF_T>
diff --git a/src/ELF/Builder.cpp b/src/ELF/Builder.cpp
index d51cea9..5727a81 100644
--- a/src/ELF/Builder.cpp
+++ b/src/ELF/Builder.cpp
@@ -111,6 +111,46 @@ void Builder::write(const std::string& filename) const {
 }
 
 
+uint32_t Builder::sort_dynamic_symbols(void) {
+  auto it_begin = std::begin(this->binary_->dynamic_symbols_);
+  auto it_end = std::end(this->binary_->dynamic_symbols_);
+
+  auto it_first_non_local_symbol =
+      std::stable_partition(it_begin, it_end, [](const Symbol* sym) {
+        return sym->binding() == SYMBOL_BINDINGS::STB_LOCAL;
+      });
+  uint32_t first_non_local_symbol_index =
+      std::distance(it_begin, it_first_non_local_symbol);
+  std::string section_name = ".dynsym";
+  if (this->binary_->has_section(section_name)) {
+    Section& section = this->binary_->get_section(section_name);
+    if (section.information() != first_non_local_symbol_index) {
+      // TODO: Erase null entries of dynamic symbol table and symbol version table
+      // if information of .dynsym section is smaller than null entries num.
+      LIEF_WARN("information of {} section changes from {:d} to {:d}",
+                section_name,
+                section.information(),
+                first_non_local_symbol_index);
+      section.information(first_non_local_symbol_index);
+    }
+  }
+
+  auto it_first_exported_symbol = std::stable_partition(
+      it_first_non_local_symbol, it_end, [](const Symbol* sym) {
+        return sym->shndx() ==
+               static_cast<uint16_t>(SYMBOL_SECTION_INDEX::SHN_UNDEF);
+      });
+  uint32_t first_exported_symbol_index =
+      std::distance(it_begin, it_first_exported_symbol);
+  if (this->binary_->gnu_hash().symbol_index() != first_exported_symbol_index) {
+    LIEF_WARN("symndx of .gnu.hash section changes from {:d} to {:d}",
+              this->binary_->gnu_hash().symbol_index(),
+              first_exported_symbol_index);
+  }
+  return first_exported_symbol_index;
+}
+
+
 void Builder::build_empty_symbol_gnuhash(void) {
   LIEF_DEBUG("Build empty GNU Hash");
   auto&& it_gnuhash = std::find_if(
diff --git a/src/ELF/Builder.tcc b/src/ELF/Builder.tcc
index 657dcb9..4b4077d 100644
--- a/src/ELF/Builder.tcc
+++ b/src/ELF/Builder.tcc
@@ -892,7 +892,7 @@ void Builder::build_symbol_gnuhash(void) {
   const GnuHash& gnu_hash   = this->binary_->gnu_hash();
 
   const uint32_t nb_buckets = gnu_hash.nb_buckets();
-  const uint32_t symndx     = gnu_hash.symbol_index();
+  const uint32_t symndx     = sort_dynamic_symbols();
   const uint32_t maskwords  = gnu_hash.maskwords();
   const uint32_t shift2     = gnu_hash.shift2();
 
diff --git a/tests/elf/test_dynamic.py b/tests/elf/test_dynamic.py
index 40c3b7b..1d1849d 100644
--- a/tests/elf/test_dynamic.py
+++ b/tests/elf/test_dynamic.py
@@ -126,6 +126,46 @@ class TestDynamic(TestCase):
     def setUp(self):
         self.logger = logging.getLogger(__name__)
 
+    @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
+    def test_add_dynamic_symbols(self):
+        sample = LibAddSample()
+        libadd = lief.parse(sample.libadd)
+        binadd = lief.parse(sample.binadd)
+        dynamic_symbols = list(libadd.dynamic_symbols)
+        for sym in dynamic_symbols:
+            libadd.add_dynamic_symbol(sym)
+        dynamic_section = libadd.get_section(".dynsym")
+        libadd.extend(dynamic_section, dynamic_section.entry_size * (len(dynamic_symbols) * 2))
+        libadd.write(sample.libadd)
+
+        p = Popen([sample.binadd_bin, '1', '2'],
+                  stdout=subprocess.PIPE,
+                  stderr=subprocess.STDOUT,
+                  env={"LD_LIBRARY_PATH": sample.directory})
+        stdout, _ = p.communicate()
+        if p.returncode > 0:
+            self.logger.fatal(stdout.decode("utf8"))
+            self.assertEqual(p.returncode, 0)
+        self.logger.debug(stdout.decode("utf8"))
+        self.assertIsNotNone(re.search(r'From myLIb, a \+ b = 3', stdout.decode("utf8")))
+
+        libadd = lief.parse(sample.libadd)
+        dynamic_section = libadd.get_section(".dynsym")
+        # TODO: Size of libadd.dynamic_symbols is larger than  dynamic_symbols_size.
+        dynamic_symbols_size = int(dynamic_section.size / dynamic_section.entry_size)
+        dynamic_symbols = list(libadd.dynamic_symbols)[:dynamic_symbols_size]
+        first_not_null_symbol_index = dynamic_section.information
+        first_exported_symbol_index = next(
+            i for i, sym in enumerate(dynamic_symbols) if sym.shndx != 0)
+        self.assertTrue(all(map(
+            lambda sym: sym.shndx == 0 and sym.binding == lief.ELF.SYMBOL_BINDINGS.LOCAL,
+                    dynamic_symbols[:first_not_null_symbol_index])))
+        self.assertTrue(all(map(
+            lambda sym: sym.shndx == 0 and sym.binding != lief.ELF.SYMBOL_BINDINGS.LOCAL,
+            dynamic_symbols[first_not_null_symbol_index:first_exported_symbol_index])))
+        self.assertTrue(all(map(
+            lambda sym: sym.shndx != 0,
+            dynamic_symbols[first_exported_symbol_index:])))
 
     @unittest.skipUnless(sys.platform.startswith("linux"), "requires Linux")
     def test_remove_library(self):