MIPS ELF imported symbols are not recognized correctly
Describe the bug I have the following file (same as in #795).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const int NUMBER = 42;
const char VERSION[] = "11.22";
const char *VERSION2 = "22.33";
void function1 (const char* str) {
char buffer[16];
strcpy(buffer, str);
}
int main(int argc, char *argv[]) {
printf("Hello %s %s\n", VERSION, VERSION2);
exit(0);
}
Which is compiled to MIPS platforms:
test-mips-64bit-le: test.c
mips64el-linux-gnuabi64-gcc-10 -o $@ $^
test-mips-64bit-le-lib: test.c
mips64el-linux-gnuabi64-gcc-10 -o $@ -shared $^
test-mips-64bit-be: test.c
mips64-linux-gnuabi64-gcc-10 -o $@ $^
Except the shared lib (test-mips-64bit-le-lib), these parse correctly, but selecting imported symbols/function is failing.
To Reproduce I have the following python code to show list imported symbols/functions:
import lief
import sys
lief.logging.set_level(lief.logging.LOGGING_LEVEL.DEBUG)
l = lief.parse(sys.argv[1])
print("SYMBOLS", [s.name for s in l.symbols])
print("FUNCTIONS", [s.name for s in l.functions])
print("IMPORTED SYMBOLS", [s.name for s in l.imported_symbols])
print("IMPORTED FUNCTIONS", [s.name for s in l.imported_functions])
Running it results in the following:
SYMBOLS ['', '_DYNAMIC_LINKING', 'VERSION', 'VERSION2', '_IO_stdin_used', '__RLD_MAP', 'main', '_ITM_registerTMCloneTable', 'exit', '__gmon_start__', 'strcpy', '__stack_chk_fail', '__stack_chk_guard', 'printf', '_ITM_deregisterTMCloneTable', '__libc_start_main', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'crt1.o', 'hlt', '__abi_tag', 'crtstuff.c', '__CTOR_LIST__', '__DTOR_LIST__', 'deregister_tm_clones', 'register_tm_clones', '__do_global_dtors_aux', 'completed.1', 'dtor_idx.0', 'frame_dummy', 'test.c', 'crtstuff.c', '__CTOR_END__', '__FRAME_END__', '__do_global_ctors_aux', '', '_MIPS_STUBS_', '__DTOR_END__', '_DYNAMIC', '__TMC_END__', '_gp', '__libc_start_main@GLIBC_2.34', '_DYNAMIC_LINKING', '_ITM_deregisterTMCloneTable', 'data_start', 'printf@GLIBC_2.0', 'function1', 'NUMBER', '__stack_chk_guard@GLIBC_2.4', '_edata', 'VERSION', '_fini', '__stack_chk_fail@GLIBC_2.4', 'VERSION2', 'strcpy@GLIBC_2.0', '__data_start', '__gmon_start__', 'exit@GLIBC_2.0', '__dso_handle', '_IO_stdin_used', '_fdata', '__RLD_MAP', '_end', '__bss_start', 'main', '__start', '_ftext', '_GLOBAL_OFFSET_TABLE_', '_fbss', '_ITM_registerTMCloneTable', '_init']
FUNCTIONS ['_init', '__start', 'deregister_tm_clones', 'register_tm_clones', '__do_global_dtors_aux', 'frame_dummy', 'function1', 'main', '__do_global_ctors_aux', '__libc_start_main', 'printf', '__stack_chk_fail', 'strcpy', 'exit', '_fini']
IMPORTED SYMBOLS ['__gmon_start__', '__stack_chk_guard', '__stack_chk_guard@GLIBC_2.4', '__gmon_start__']
IMPORTED FUNCTIONS ['__gmon_start__', '__gmon_start__']
Expected behavior
Neither printf nor strcpy are listed in imported, while they should be.
For each ELF Symbol is_imported status is defined here:
bool Symbol::is_imported() const {
// An import must not be defined in a section
bool is_imported = shndx() == static_cast<uint16_t>(SYMBOL_SECTION_INDEX::SHN_UNDEF);
// An import must not have an address
is_imported = is_imported && value() == 0;
// its name must not be empty
is_imported = is_imported && !name().empty();
// It must have a GLOBAL or WEAK bind
is_imported = is_imported && (binding() == SYMBOL_BINDINGS::STB_GLOBAL ||
binding() == SYMBOL_BINDINGS::STB_WEAK);
// It must be a FUNC or an OBJECT
is_imported = is_imported && (type() == ELF_SYMBOL_TYPES::STT_FUNC ||
type() == ELF_SYMBOL_TYPES::STT_GNU_IFUNC ||
type() == ELF_SYMBOL_TYPES::STT_OBJECT);
return is_imported;
}
However on MIPS symbols have value:
Num: Value Size Type Bind Vis Ndx Name
10: 0000000120000d70 0 FUNC GLOBAL DEFAULT UND strcpy@GLIBC_2.0 (3)
13: 0000000120000d50 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.0 (3)
While on other platforms (like arm/x86) they do not:
Num: Value Size Type Bind Vis Ndx Name
3: 00000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.4 (3)
8: 00000000 0 FUNC GLOBAL DEFAULT UND strcpy@GLIBC_2.4 (3)
Getting the value in LIEF for printf using print("PRINTF VALUE", [(s.name, s.value) for s in l.symbols if s.name == 'printf']) also shows this:
On MIPS: PRINTF VALUE [('printf', 4831841616)]
On ARM32: PRINTF VALUE [('printf', 0)]
On X86_64: PRINTF VALUE [('printf', 0)]
Not sure if not having a value is a mandatory requirement for imported functions, radare2 also has a bit different definition:
In _r_bin_elf_get_symbols_imports:
<snip>
bool is_imported = false;
if (type == R_BIN_ELF_IMPORT_SYMBOLS) {
if (sym[k].st_value) {
toffset = sym[k].st_value;
} else if ((toffset = get_import_addr (bin, k)) == -1) {
toffset = 0;
}
tsize = 16;
is_imported = sym[k].st_shndx == STN_UNDEF;
<snip>
Glad to see this issue open, I just noticed the same issue for an elf on powerpc and was just starting to investigate it Likewise, on powerpc symbols have value:
74: 10014c14 84 FUNC GLOBAL DEFAULT UND strstr
75: 10014c1c 72 FUNC GLOBAL DEFAULT UND setgid
83: 10014c24 76 FUNC GLOBAL DEFAULT UND sscanf
running print("PRINTF VALUE", [(s.name, s.value) for s in l.symbols if s.name == 'printf']) also shows this:
PRINTF VALUE [('printf', 268520356)]