Error Handling

Introduction

LIEF manages the errors using

  1. The exceptions (removed since LIEF 0.13.0)

  2. std::expected (tl::expected)

It turns out that using the C++ exceptions (and the RTTI) were not the better design choice as LIEF (as a library) can be used in -fno-exceptions context. This is why we are slowly moving to the second mechanism which is based on the ResultOrError idiom. We can find this kind idiom in LLVM with llvm::ErrorOr, in Rust with std::result. LIEF is using a std::expected-like to handle errors. Since this interface is only available in C++23, we rely on TartanLlama/expected which provides this interface for C++11/C++17.

Basically, LIEF functions that use this idiom return a LIEF::result which wraps the effective result or an error.

The user can process this result as follows:

result<PE_TYPE> pe_type = PE::get_type("/tmp/NotPE.elf")
if (pe_type) {
  PE_TYPE effective_type = pe_type.value();
} else {
  lief_errors err = as_lief_err(pe_type);
}

In the case of Python, we leverage the dynamic features of the language to return either: the expected value or an error if the function failed. For instance, if we take the lief.PE.get_type() function, the former implementation of this function raised an exception to inform the user:

try:
  pe_type = lief.PE.get_type("/tmp/NotPE.elf")
  # If it does not fail, pe_type handles a lief.PE.PE_TYPE object
except Exception as e:
  print(f"Error: {e}")

With the new implementation that relies on the ResultOrError idiom, the function returns the lief.PE.PE_TYPE value is everything is ok and in the case of a processing error, it returns a lief.lief_errors.

The user can handle this new interface by using the isinstance() function or by comparing the value with a lief.lief_errors attribute:

pe_type = lief.PE.get_type("/tmp/NotPE.elf")

if pe_type == lief.lief_errors.file_error:
  print("File error")
elif isinstance(pe_type, lief.lief_errors):
  print("Another kind of error")
else:
  print("No error, type is: {}".format(pe_type))

API

C++

template<typename T>
using LIEF::result = tl::expected<T, lief_errors>

Wrapper that contains an Object (T) or an error.

The tl/expected implementation exposes the method value() to access the underlying object (if no error)

Typical usage is:

result<int> intval = my_function();
if (intval) {
 int val = intval.value();
} else { // There is an error
 std::cout << get_error(intval).message() << "\n";
}

See https://tl.tartanllama.xyz/en/latest/api/expected.html for more details

template<class T>
lief_errors LIEF::as_lief_err(result<T> &err)

Return the lief_errors when the provided result<T> is an error.

enum class lief_errors : uint32_t

LIEF error codes definition.

Values:

enumerator read_error = 1
enumerator not_found
enumerator not_implemented
enumerator not_supported
enumerator corrupted
enumerator conversion_error
enumerator read_out_of_bound
enumerator asn1_bad_tag
enumerator file_error
enumerator file_format_error
enumerator parsing_error
enumerator build_error
enumerator data_too_large
enumerator require_extended_version
using LIEF::ok_error_t = result<ok_t>

Opaque structure that is used by LIEF to avoid writing result<void> f(...). Instead, it makes the output explicit such as:

ok_error_t process() {
  if (fail) {
    return make_error_code(...);
  }
  return ok();
}
inline ok_t LIEF::ok()

Return success for function with return type ok_error_t.

struct ok_t

Opaque structure used by ok_error_t.

Python

class lief.lief_errors

Bases: object

asn1_bad_tag = lief._lief.lief_errors.asn1_bad_tag
build_error = lief._lief.lief_errors.build_error
conversion_error = lief._lief.lief_errors.conversion_error
corrupted = lief._lief.lief_errors.corrupted
data_too_large = lief._lief.lief_errors.data_too_large
file_error = lief._lief.lief_errors.file_error
file_format_error = lief._lief.lief_errors.file_format_error
not_found = lief._lief.lief_errors.not_found
not_implemented = lief._lief.lief_errors.not_implemented
not_supported = lief._lief.lief_errors.not_supported
parsing_error = lief._lief.lief_errors.parsing_error
read_error = lief._lief.lief_errors.read_error
read_out_of_bound = lief._lief.lief_errors.read_out_of_bound
require_extended_version = lief._lief.lief_errors.require_extended_version
class lief.ok_t

Bases: object

Opaque value returned when a void function is executed successfully.

class lief.ok_error_t

Bases: object

Return either: ok_t (success) or lief_errors (error)

property error lief.lief_errors
property is_error bool
property is_value bool
property value lief.ok_t