Skip to content

Commit

Permalink
Only iterate static or dynamic symbols (#9)
Browse files Browse the repository at this point in the history
As a response to lief-project/LIEF#962 only
iterate static or dynamic symbols.

Added a simple Makefile to demonstrate shadowing variables.
  • Loading branch information
fzakaria authored Sep 11, 2023
1 parent c5532dd commit e5ae524
Show file tree
Hide file tree
Showing 5 changed files with 51 additions and 3 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ sqlelf.egg-info/
.vscode/
dist/
result
examples/shadowed-symbols/exe
examples/**/*.so
25 changes: 25 additions & 0 deletions examples/shadowed-symbols/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
all: exe

# This library depends libx2.so soname and calls h() from it
y/liby.so: x/libx2.so
@mkdir -p $(dir $@)
echo 'extern int foo(); int g() { return foo(); }' | $(CC) -o $@ -shared -x c - -Lx -l:libx2.so '-Wl,--no-as-needed,--enable-new-dtags,-rpath,$$ORIGIN/../x'

# This library has both file and soname libx.so
x/libx.so:
@mkdir -p $(dir $@)
echo 'int foo(){return 12;}' | $(CC) -o $@ -shared -x c -

# This library has both file and soname libx.so
x/libx2.so:
@mkdir -p $(dir $@)
echo 'int foo(){return 1000;}' | $(CC) -o $@ -shared -x c -

# This links to b/liby.so and c/libx.so, and gets libx.so and liby.so in DT_NEEDED, no paths.
exe: y/liby.so x/libx.so
echo 'extern int g(); extern int foo(); int main(){ printf("\%d\n", g() + foo()); }' | \
$(CC) -o $@ -include stdio.h -x c - -Ly -Lx -l:liby.so '-Wl,--no-as-needed,--enable-new-dtags,-rpath,$$ORIGIN/y' \
-l:libx.so '-Wl,--no-as-needed,--enable-new-dtags,-rpath,$$ORIGIN/x'

clean:
rm -rf -- x y exe
2 changes: 1 addition & 1 deletion sqlelf/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def start(args=sys.argv[1:], stdin=sys.stdin):

# If none of the inputs are valid files, simply return
if len(filenames) == 0:
return
sys.exit("No valid ELF files were provided")

binaries: list[Binary] = [Binary(filename) for filename in filenames]

Expand Down
22 changes: 21 additions & 1 deletion sqlelf/elf/symbol.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import apsw
import apsw.ext
import lief

from sqlelf.elf.binary import Binary
from sqlelf.elf.section import section_name as elf_section_name
Expand All @@ -16,7 +17,7 @@ def generator() -> Iterator[dict[str, Any]]:
# super important that these accessors are pulled out of the tight loop
# as they can be costly
binary_path = binary.path
for symbol in binary.symbols:
for symbol in symbols(binary):
# The section index can be special numbers like 65521 or 65522
# that refer to special sections so they can't be indexed
section_name: str | None = next(
Expand Down Expand Up @@ -56,6 +57,25 @@ def generator() -> Iterator[dict[str, Any]]:
return generator


def symbols(binary: Binary) -> Iterator[lief.ELF.Symbol]:
"""Use heuristic to either get static symbols or dynamic symbol table
The static symbol table is a superset of the dynamic symbol table.
However it is often stripped from binaries as it's not needed beyond
debugging.
This method uses the simplest heuristic of checking for it's existence
to return the static symbol table.
A bad actor is free to strip arbitrarily from the static symbol table
and it would affect this method.
"""
static_symbols = binary.static_symbols
if len(static_symbols) > 0:
return static_symbols
return binary.dynamic_symbols


def register(connection: apsw.Connection, binaries: list[Binary]):
generator = elf_symbols(binaries)
# setup columns and access by providing an example of the first entry returned
Expand Down
3 changes: 2 additions & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ def test_cli_single_file_arguments():
cli.start(["/bin/ls"], stdin)

def test_cli_single_non_existent_file_arguments():
cli.start(["does_not_exist"])
with pytest.raises(SystemExit) as err:
cli.start(["does_not_exist"])

def test_cli_prompt_single_file_arguments():
stdin = StringIO(".exit 56\n")
Expand Down

0 comments on commit e5ae524

Please sign in to comment.