Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort dynamic symbols #507

Merged

Conversation

Clcanny
Copy link
Contributor

@Clcanny Clcanny commented Dec 6, 2020

// foo.cpp
#include <iostream>
namespace {
int var = 1;

void bar() {
  std::cout << "bar" << std::endl;
}
}  // namespace
void foo() {
}
// merge.cpp
#include <LIEF/ELF.hpp>
#include <memory>
#include <vector>
int main() {
  std::unique_ptr<LIEF::ELF::Binary> binary_(
      LIEF::ELF::Parser::parse("libfoo.so"));
  std::vector<LIEF::ELF::Symbol> dynamic_symbols{
      binary_->dynamic_symbols().begin(), binary_->dynamic_symbols().end()};
  uint32_t added_symbol_num = 0;
  for (const auto& symbol : dynamic_symbols) {
    binary_->add_dynamic_symbol(symbol);
    added_symbol_num += 1;
  }
  uint64_t entry_size = binary_->get_section(".dynsym").entry_size();
  assert(entry_size == 24);
  binary_->extend(binary_->get_section(".dynsym"),
                  dynamic_symbols.size() * entry_size);
  binary_->write("libfoo-modified.so");
}
all : clean compile run

clean :
        rm -f libfoo.so merge libfoo-modified.so

compile :
        g++ -std=c++11 foo.cpp -O0 -ggdb -shared -fPIC -o libfoo.so
        g++ -std=c++11 -O0 -ggdb merge.cpp -I/usr/include/LIEF-0.11.0-Linux/include -L/usr/lib/LIEF-0.11.0-Linux/lib -lLIEF -o merge

run :
        ./merge
        chmod u+x libfoo-modified.so
        readelf --dyn-syms libfoo-modified.so

Dynamic symbol table of libfoo.so is:

# readelf --dyn-syms libfoo.so
Symbol table '.dynsym' contains 19 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@GLIBC_2.2.5 (3)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4 (2)
     6: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZStlsISt11char_traitsIcE@GLIBCXX_3.4 (2)
     8: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     9: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (3)
    10: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND _ZSt4cout@GLIBCXX_3.4 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSolsEPFRSoS_E@GLIBCXX_3.4 (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZSt4endlIcSt11char_trait@GLIBCXX_3.4 (2)
    13: 0000000000201048     0 NOTYPE  GLOBAL DEFAULT   24 _end
    14: 0000000000201044     0 NOTYPE  GLOBAL DEFAULT   23 _edata
    15: 0000000000000932     7 FUNC    GLOBAL DEFAULT   12 _Z3foov
    16: 0000000000201044     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    17: 0000000000000788     0 FUNC    GLOBAL DEFAULT    9 _init
    18: 0000000000000998     0 FUNC    GLOBAL DEFAULT   13 _fini

Without this change:

# make
readelf --dyn-syms libfoo-modified.so

Symbol table '.dynsym' contains 38 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     3: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4 (2)
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@GLIBC_2.2.5 (3)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4 (2)
     6: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     7: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZStlsISt11char_traitsIcE@GLIBCXX_3.4 (2)
     8: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
     9: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (3)
    10: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND _ZSt4cout@GLIBCXX_3.4 (2)
    11: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSolsEPFRSoS_E@GLIBCXX_3.4 (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZSt4endlIcSt11char_trait@GLIBCXX_3.4 (2)
    13: 0000000000204210     0 NOTYPE  GLOBAL DEFAULT   24 _end
    14: 000000000020420c     0 NOTYPE  GLOBAL DEFAULT   23 _edata
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZStlsISt11char_traitsIcE
    16: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    17: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize
    18: 0000000000204210     0 NOTYPE  GLOBAL DEFAULT   24 _end
    19: 000000000020420c     0 NOTYPE  GLOBAL DEFAULT   23 _edata
    20: 0000000000003afa     7 FUNC    GLOBAL DEFAULT   12 _Z3foov
    21: 000000000020420c     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    22: 0000000000003950     0 FUNC    GLOBAL DEFAULT    9 _init
    23: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZSt4endlIcSt11char_trait
    25: 0000000000003afa     7 FUNC    GLOBAL DEFAULT   12 _Z3foov
    26: 000000000020420c     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    27: 0000000000003950     0 FUNC    GLOBAL DEFAULT    9 _init
    28: 0000000000003b60     0 FUNC    GLOBAL DEFAULT   13 _fini
    29: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
readelf: Warning: local symbol 29 found at index >= .dynsym's sh_info value of 1
    30: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    31: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    32: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev
    33: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev
    34: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    35: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND _ZSt4cout
    36: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSolsEPFRSoS_E
    37: 0000000000003b60     0 FUNC    GLOBAL DEFAULT   13 _fini

With this change:

# make
information of .dynsym section changes from 1 to 2
symndx of .gnu.hash section changes from 13 to 26
symndx of .gnu.hash section changes from 13 to 26
symndx of .gnu.hash section changes from 13 to 26
chmod u+x libfoo-modified.so
readelf --dyn-syms libfoo-modified.so

Symbol table '.dynsym' contains 38 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     2: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     3: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
     4: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev@GLIBCXX_3.4 (2)
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit@GLIBC_2.2.5 (3)
     6: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev@GLIBCXX_3.4 (2)
     7: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZStlsISt11char_traitsIcE@GLIBCXX_3.4 (2)
     9: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    10: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize@GLIBC_2.2.5 (3)
    11: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND _ZSt4cout@GLIBCXX_3.4 (2)
    12: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSolsEPFRSoS_E@GLIBCXX_3.4 (2)
    13: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZSt4endlIcSt11char_trait@GLIBCXX_3.4 (2)
    14: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    15: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    16: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitC1Ev
    17: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __cxa_atexit
    18: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSt8ios_base4InitD1Ev
    19: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_deregisterTMCloneTab
    20: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZStlsISt11char_traitsIcE
    21: 0000000000000000     0 NOTYPE  WEAK   DEFAULT  UND _ITM_registerTMCloneTable
    22: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_finalize
    23: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND _ZSt4cout
    24: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZNSolsEPFRSoS_E
    25: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND _ZSt4endlIcSt11char_trait
    26: 0000000000204210     0 NOTYPE  GLOBAL DEFAULT   24 _end
    27: 000000000020420c     0 NOTYPE  GLOBAL DEFAULT   23 _edata
    28: 0000000000204210     0 NOTYPE  GLOBAL DEFAULT   24 _end
    29: 000000000020420c     0 NOTYPE  GLOBAL DEFAULT   23 _edata
    30: 0000000000003afa     7 FUNC    GLOBAL DEFAULT   12 _Z3foov
    31: 000000000020420c     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    32: 0000000000003950     0 FUNC    GLOBAL DEFAULT    9 _init
    33: 0000000000003afa     7 FUNC    GLOBAL DEFAULT   12 _Z3foov
    34: 000000000020420c     0 NOTYPE  GLOBAL DEFAULT   24 __bss_start
    35: 0000000000003950     0 FUNC    GLOBAL DEFAULT    9 _init
    36: 0000000000003b60     0 FUNC    GLOBAL DEFAULT   13 _fini
    37: 0000000000003b60     0 FUNC    GLOBAL DEFAULT   13 _fini

@Clcanny
Copy link
Contributor Author

Clcanny commented Dec 6, 2020

@romainthomas Hello, please review this change. Thank you!

@romainthomas
Copy link
Member

Hi @Clcanny
Thanks for the PR. Is there any reference of such sorting in LLVM linker / binutils or did you implement the sort with your own method ?

@Clcanny
Copy link
Contributor Author

Clcanny commented Dec 7, 2020

Hi @romainthomas ,

  1. Imported symbols should be sorted before exported symbols: I have investigated .gnu.hash section and read do_lookup_x function in ld-linux-x86-64.so.2.

  2. Local symbols should be sorted before global symbols: I just want to fix warning "local symbol 29 found at index >= .dynsym's sh_info value of 1" of readelf, I am not so confident of this sorting.

@Clcanny
Copy link
Contributor Author

Clcanny commented Dec 7, 2020

2. Local symbols should be sorted before global symbols

FYI. https://sourceware.org/bugzilla/show_bug.cgi?id=24857

@romainthomas
Copy link
Member

@Clcanny
Copy link
Contributor Author

Clcanny commented Dec 7, 2020

According to llvm, we need to sort dynamic symbol table 2 times:

  1. Let undefined symbols come before defined symbols: https://github.com/llvm/llvm-project/blob/2fc704a0a529dd7eba7566a293f981a86bfa5c3e/lld/ELF/SyntheticSections.cpp#L2415
std::vector<SymbolTableEntry>::iterator mid =
    std::stable_partition(v.begin(), v.end(), [&](const SymbolTableEntry &s) {
      return !s.sym->isDefined() || s.sym->partition != partition;
    });
  1. Sort by bucket ids: https://github.com/llvm/llvm-project/blob/2fc704a0a529dd7eba7566a293f981a86bfa5c3e/lld/ELF/SyntheticSections.cpp#L2441
llvm::stable_sort(symbols, [](const Entry &l, const Entry &r) {
  return l.bucketIdx < r.bucketIdx;
});

Besides the above two sorting, we need another sort (3) to find local symbols (null entries).

I am going to combine the first sort and the third sort, and do other things to improve my code.

Thanks for your guide!

@Clcanny
Copy link
Contributor Author

Clcanny commented Dec 10, 2020

@romainthomas Hi, I have changed my code. I think calling stable_partition twice is needed, it makes code easy to understand.

And I add a unit test for this change. There is one thing worth noting: size of libadd.dynamic_symbols is bigger than the real size.

@romainthomas romainthomas merged commit 50a348e into lief-project:master Dec 11, 2020
@romainthomas
Copy link
Member

Perfect @Clcanny 👍
I merged the pr through the commit 3613f47 and I updated the changelog as well

@Clcanny
Copy link
Contributor Author

Clcanny commented Dec 11, 2020

Perfect @Clcanny 👍
I merged the pr through the commit 3613f47 and I updated the changelog as well

Thanks! But the ut still has some problems when running in Travis CI while it is okay when running on my local machine. I will fix it and commit it.

@romainthomas
Copy link
Member

Oh yes I mess that. Open a PR with your fixe when it's done

Clcanny added a commit to Clcanny/LIEF that referenced this pull request Dec 12, 2020
Clcanny added a commit to Clcanny/LIEF that referenced this pull request Dec 16, 2020
Clcanny added a commit to Clcanny/LIEF that referenced this pull request Dec 16, 2020
Clcanny added a commit to Clcanny/LIEF that referenced this pull request Dec 17, 2020
Clcanny added a commit to Clcanny/LIEF that referenced this pull request Dec 17, 2020
Clcanny added a commit to Clcanny/LIEF that referenced this pull request Dec 21, 2020
@romainthomas
Copy link
Member

To be reopened

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants