Error Handling

Introduction

LIEF manages errors using:

  1. Exceptions (deprecated and removed since LIEF 0.13.0)

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

It turns out that using C++ exceptions (and RTTI) was not the best design choice, as LIEF, as a library, can be used in a -fno-exceptions context. Consequently, we moved to a mechanism based on the ResultOrError idiom. This idiom is similar to those found in LLVM with llvm::ErrorOr and in Rust with std::result. LIEF uses a std::expected-like interface 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.

Functions using this idiom return a LIEF::result, which wraps either the successful 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 fails. For instance, in previous versions of lief.PE.get_type(), the implementation 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 if everything is correct, and returns a lief.lief_errors in case of a processing error.

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>
class result : public 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

Subclassed by LIEF::optional< T >

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
class ok_error_t : public LIEF::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(*values)

Bases: Enum

Enum class which represents an error generated by LIEF’s functions

asn1_bad_tag = 8
build_error = 12
conversion_error = 6
corrupted = 5
data_too_large = 13
file_error = 9
file_format_error = 10
not_found = 2
not_implemented = 3
not_supported = 4
parsing_error = 11
read_error = 1
read_out_of_bound = 7
require_extended_version = 14
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