LIEF icon indicating copy to clipboard operation
LIEF copied to clipboard

MIPS ELF imported symbols are not recognized correctly

Open martonilles opened this issue 3 years ago • 1 comments

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 $@ $^

test-mips.zip

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>

martonilles avatar Sep 17 '22 21:09 martonilles

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)]

nikaiw avatar Sep 18 '22 11:09 nikaiw