diff --git a/lldb/lit/Modules/ELF/Inputs/load-from-dynsym-alone.c b/lldb/lit/Modules/ELF/Inputs/load-from-dynsym-alone.c new file mode 100644 --- /dev/null +++ b/lldb/lit/Modules/ELF/Inputs/load-from-dynsym-alone.c @@ -0,0 +1,7 @@ +// This function will be embedded within the .dynsym section of the main binary. +int functionInDynsym(int num) { return num * 3; } + +int main(int argc, char *argv[]) { + int y = functionInDynsym(argc); + return y; +} diff --git a/lldb/lit/Modules/ELF/Inputs/load-symtab-and-dynsym.c b/lldb/lit/Modules/ELF/Inputs/load-symtab-and-dynsym.c new file mode 100644 --- /dev/null +++ b/lldb/lit/Modules/ELF/Inputs/load-symtab-and-dynsym.c @@ -0,0 +1,12 @@ +// This function will be embedded within the .symtab section of the +// .gnu_debugdata section. +int functionInSymtab(int num) { return num * 4; } + +// This function will be embedded within the .dynsym section of the main binary. +int functionInDynsym(int num) { return num * 3; } + +int main(int argc, char *argv[]) { + int x = functionInSymtab(argc); + int y = functionInDynsym(x); + return y; +} diff --git a/lldb/lit/Modules/ELF/load-from-dynsym-alone.test b/lldb/lit/Modules/ELF/load-from-dynsym-alone.test new file mode 100644 --- /dev/null +++ b/lldb/lit/Modules/ELF/load-from-dynsym-alone.test @@ -0,0 +1,33 @@ +# REQUIRES: system-linux + +# This test ensures that we will load .dynsym even if there's no .symtab section. +# We do this by compiling a small C program with a function and we direct the +# linker where to put the symbols so that in the end the layout is as follows: +# +# Symbol table '.dynsym' contains 4 entries: +# Num: Value Size Type Bind Vis Ndx Name +# 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +# 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 +# 2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ +# 3: 0000000000401110 13 FUNC GLOBAL DEFAULT 10 functionInDynsym + +# We want to keep the symbol "functionInDynsym" in the .dynamic section and not +# have it put the default .symtab section. +# RUN: echo "{functionInDynsym;};" > %T/dynmic-symbols.txt +# RUN: %clang -Wl,--dynamic-list=%T/dynmic-symbols.txt -g -o %t.binary %p/Inputs/load-from-dynsym-alone.c + +# Remove not needed symbols +# RUN: echo "functionInDynsym" > %t.keep_symbols +# RUN: llvm-objcopy --strip-all --remove-section .gdb_index --remove-section .comment --keep-symbols=%t.keep_symbols %t.binary + +# Remove functionInDynsym symbol from .symtab (will leave symbol in .dynsym intact) +# RUN: llvm-strip --strip-symbol=functionInDynsym %t.binary + +# RUN: %lldb -b -o 'b functionInDynsym' -o 'run' -o 'continue' %t.binary | FileCheck %s + +# CHECK: (lldb) b functionInDynsym +# CHECK-NEXT: Breakpoint 1: where = {{.*}}.binary`functionInDynsym, address = 0x{{.*}} + +# CHECK: (lldb) run +# CHECK-NEXT: Process {{.*}} stopped +# CHECK-NEXT: * thread #1, name = 'load-from-dynsy', stop reason = breakpoint 1.1 diff --git a/lldb/lit/Modules/ELF/load-symtab-and-dynsym.test b/lldb/lit/Modules/ELF/load-symtab-and-dynsym.test new file mode 100644 --- /dev/null +++ b/lldb/lit/Modules/ELF/load-symtab-and-dynsym.test @@ -0,0 +1,48 @@ +# REQUIRES: system-linux + +# This test ensures that we will load .dynsym even if there's a .symtab section. +# We do this by compiling a small C program with two functions and we direct the +# linker where to put the symbols so that in the end the layout is as follows: +# +# Symbol table '.dynsym' contains 4 entries: +# Num: Value Size Type Bind Vis Ndx Name +# 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +# 1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND __libc_start_main@GLIBC_2.2.5 +# 2: 0000000000000000 0 NOTYPE WEAK DEFAULT UND __gmon_start__ +# 3: 0000000000401120 13 FUNC GLOBAL DEFAULT 10 functionInDynsym +# +# Symbol table '.symtab' contains 2 entries: +# Num: Value Size Type Bind Vis Ndx Name +# 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND +# 1: 0000000000401110 15 FUNC GLOBAL DEFAULT 10 functionInSymtab + +# We want to keep the symbol "functionInDynsym" in the .dynamic section and not +# have it put the default .symtab section. +# RUN: echo "{functionInDynsym;};" > %T/dynmic-symbols.txt +# RUN: %clang -Wl,--dynamic-list=%T/dynmic-symbols.txt -g -o %t.binary %p/Inputs/load-symtab-and-dynsym.c + +# Remove not needed symbols +# RUN: echo "functionInSymtab" > %t.keep_symbols +# RUN: echo "functionInDynsym" >> %t.keep_symbols +# RUN: llvm-objcopy --strip-all --remove-section .gdb_index --remove-section .comment --keep-symbols=%t.keep_symbols %t.binary + +# Remove functionInDynsym symbol from .symtab (will leave symbol in .dynsym intact) +# RUN: llvm-strip --strip-symbol=functionInDynsym %t.binary + +# RUN: %lldb -b -o 'b functionInSymtab' -o 'b functionInDynsym' -o 'run' -o 'continue' %t.binary | FileCheck %s + +# CHECK: (lldb) b functionInSymtab +# CHECK-NEXT: Breakpoint 1: where = {{.*}}.binary`functionInSymtab, address = 0x{{.*}} + +# CHECK: (lldb) b functionInDynsym +# CHECK-NEXT: Breakpoint 2: where = {{.*}}.binary`functionInDynsym, address = 0x{{.*}} + +# CHECK: (lldb) run +# CHECK-NEXT: Process {{.*}} stopped +# CHECK-NEXT: * thread #1, name = 'load-symtab-and', stop reason = breakpoint 1.1 + +# CHECK: (lldb) continue +# CHECK-NEXT: Process {{.*}} resuming +# CHECK-NEXT: Process {{.*}} stopped +# CHECK-NEXT: * thread #1, name = 'load-symtab-and', stop reason = breakpoint 2.1 + diff --git a/lldb/lit/helper/toolchain.py b/lldb/lit/helper/toolchain.py --- a/lldb/lit/helper/toolchain.py +++ b/lldb/lit/helper/toolchain.py @@ -126,6 +126,6 @@ support_tools = ['yaml2obj', 'obj2yaml', 'llvm-pdbutil', 'llvm-mc', 'llvm-readobj', 'llvm-objdump', - 'llvm-objcopy', 'lli'] + 'llvm-objcopy', 'lli', 'llvm-strip'] additional_tool_dirs += [config.lldb_tools_dir, config.llvm_tools_dir] llvm_config.add_tool_substitutions(support_tools, additional_tool_dirs) diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -2647,21 +2647,31 @@ // smaller version of the symtab that only contains global symbols. The // information found in the dynsym is therefore also found in the symtab, // while the reverse is not necessarily true. + // One exception to the above rule is when we have minidebuginfo embedded + // into a compressed .gnu_debugdata section. This section contains a .symtab + // from which all symbols already contained in the .dynsym are stripped. Section *symtab = section_list->FindSectionByType(eSectionTypeELFSymbolTable, true).get(); - if (!symtab) { - // The symtab section is non-allocable and can be stripped, so if it - // doesn't exist then use the dynsym section which should always be - // there. - symtab = - section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true) - .get(); - } if (symtab) { m_symtab_up.reset(new Symtab(symtab->GetObjectFile())); symbol_id += ParseSymbolTable(m_symtab_up.get(), symbol_id, symtab); } + // The symtab section is non-allocable and can be stripped, while the dynsym + // section which should always be always be there. If both exist we load + // both to support the minidebuginfo case. Otherwise we just load the dynsym + // section. + Section *dynsym = + section_list->FindSectionByType(eSectionTypeELFDynamicSymbols, true) + .get(); + if (dynsym) { + if (!m_symtab_up) { + auto sec = symtab ? symtab : dynsym; + m_symtab_up.reset(new Symtab(sec->GetObjectFile())); + } + symbol_id += ParseSymbolTable(m_symtab_up.get(), symbol_id, dynsym); + } + // DT_JMPREL // If present, this entry's d_ptr member holds the address of // relocation