diff --git a/api/python/CMakeLists.txt b/api/python/CMakeLists.txt
index 97286cac22..fc2eb093b3 100644
--- a/api/python/CMakeLists.txt
+++ b/api/python/CMakeLists.txt
@@ -39,11 +39,10 @@ message(STATUS "Python interpreter: ${Python_EXECUTABLE}")
 nanobind_add_module(
   pyLIEF
   NB_STATIC
-
-  ${CMAKE_CURRENT_SOURCE_DIR}/src/pyLIEF.cpp
-
   # Do not default to -Os
   NOMINSIZE
+
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/pyLIEF.cpp
 )
 
 add_subdirectory(src)
diff --git a/api/python/lief/ELF.pyi b/api/python/lief/ELF.pyi
index 9abbb609b1..5be1b6f6dc 100644
--- a/api/python/lief/ELF.pyi
+++ b/api/python/lief/ELF.pyi
@@ -8,7 +8,11 @@ import lief.ELF.Binary # type: ignore
 import lief.ELF.Builder # type: ignore
 import lief.ELF.CoreAuxv # type: ignore
 import lief.ELF.CoreFile # type: ignore
+import lief.ELF.CorePrPsInfo # type: ignore
 import lief.ELF.CorePrStatus # type: ignore
+import lief.ELF.CorePrStatus.Registers # type: ignore
+import lief.ELF.Note # type: ignore
+import lief.ELF.NoteAbi # type: ignore
 import lief.ELF.Section # type: ignore
 import lief.ELF.Segment # type: ignore
 import lief.ELF.SymbolVersionDefinition # type: ignore
@@ -231,7 +235,7 @@ class ARM_EFLAGS:
     @property
     def value(self) -> int: ...
 
-class AndroidNote(NoteDetails):
+class AndroidIdent(Note):
     ndk_build_number: str
     ndk_version: str
     sdk_version: int
@@ -361,7 +365,7 @@ class Binary(lief.Binary):
     @overload
     def get(self, type: lief.ELF.SEGMENT_TYPES) -> lief.ELF.Segment: ...
     @overload
-    def get(self, type: lief.ELF.NOTE_TYPES) -> lief.ELF.Note: ...
+    def get(self, type: lief.ELF.Note.TYPE) -> lief.ELF.Note: ...
     @overload
     def get(self, type: lief.ELF.SECTION_TYPES) -> lief.ELF.Section: ...
     def get_dynamic_symbol(self, symbol_name: str) -> lief.ELF.Symbol: ...
@@ -380,7 +384,7 @@ class Binary(lief.Binary):
     @overload
     def has(self, type: lief.ELF.SEGMENT_TYPES) -> bool: ...
     @overload
-    def has(self, type: lief.ELF.NOTE_TYPES) -> bool: ...
+    def has(self, type: lief.ELF.Note.TYPE) -> bool: ...
     @overload
     def has(self, type: lief.ELF.SECTION_TYPES) -> bool: ...
     def has_dynamic_symbol(self, symbol_name: str) -> bool: ...
@@ -404,7 +408,7 @@ class Binary(lief.Binary):
     @overload
     def remove(self, note: lief.ELF.Note) -> None: ...
     @overload
-    def remove(self, type: lief.ELF.NOTE_TYPES) -> None: ...
+    def remove(self, type: lief.ELF.Note.TYPE) -> None: ...
     @overload
     def remove_dynamic_symbol(self, arg: lief.ELF.Symbol, /) -> None: ...
     @overload
@@ -427,13 +431,13 @@ class Binary(lief.Binary):
     @overload
     def __contains__(self, arg: lief.ELF.DYNAMIC_TAGS, /) -> bool: ...
     @overload
-    def __contains__(self, arg: lief.ELF.NOTE_TYPES, /) -> bool: ...
+    def __contains__(self, arg: lief.ELF.Note.TYPE, /) -> bool: ...
     @overload
     def __contains__(self, arg: lief.ELF.SECTION_TYPES, /) -> bool: ...
     @overload
     def __getitem__(self, arg: lief.ELF.SEGMENT_TYPES, /) -> lief.ELF.Segment: ...
     @overload
-    def __getitem__(self, arg: lief.ELF.NOTE_TYPES, /) -> lief.ELF.Note: ...
+    def __getitem__(self, arg: lief.ELF.Note.TYPE, /) -> lief.ELF.Note: ...
     @overload
     def __getitem__(self, arg: lief.ELF.DYNAMIC_TAGS, /) -> lief.ELF.DynamicEntry: ...
     @overload
@@ -453,7 +457,7 @@ class Binary(lief.Binary):
     @overload
     def __isub__(self, arg: lief.ELF.Note, /) -> lief.ELF.Binary: ...
     @overload
-    def __isub__(self, arg: lief.ELF.NOTE_TYPES, /) -> lief.ELF.Binary: ...
+    def __isub__(self, arg: lief.ELF.Note.TYPE, /) -> lief.ELF.Binary: ...
     @property
     def dtor_functions(self) -> list[lief.Function]: ...
     @property
@@ -550,81 +554,91 @@ class Builder:
     def get_build(self) -> list[int]: ...
     def write(self, output: str) -> None: ...
 
-class CoreAuxv(NoteDetails):
-    class TYPES:
-        BASE: ClassVar[CoreAuxv.TYPES] = ...
-        BASE_PLATFORM: ClassVar[CoreAuxv.TYPES] = ...
-        CKLTCK: ClassVar[CoreAuxv.TYPES] = ...
-        DCACHEBSIZE: ClassVar[CoreAuxv.TYPES] = ...
-        EGID: ClassVar[CoreAuxv.TYPES] = ...
-        ENTRY: ClassVar[CoreAuxv.TYPES] = ...
-        EUID: ClassVar[CoreAuxv.TYPES] = ...
-        EXECFD: ClassVar[CoreAuxv.TYPES] = ...
-        EXECFN: ClassVar[CoreAuxv.TYPES] = ...
-        FLAGS: ClassVar[CoreAuxv.TYPES] = ...
-        FPUCW: ClassVar[CoreAuxv.TYPES] = ...
-        GID: ClassVar[CoreAuxv.TYPES] = ...
-        HWCAP: ClassVar[CoreAuxv.TYPES] = ...
-        HWCAP2: ClassVar[CoreAuxv.TYPES] = ...
-        ICACHEBSIZE: ClassVar[CoreAuxv.TYPES] = ...
-        IGNORE: ClassVar[CoreAuxv.TYPES] = ...
-        IGNOREPPC: ClassVar[CoreAuxv.TYPES] = ...
-        L1D_CACHESHAPE: ClassVar[CoreAuxv.TYPES] = ...
-        L1I_CACHESHAPE: ClassVar[CoreAuxv.TYPES] = ...
-        NOTELF: ClassVar[CoreAuxv.TYPES] = ...
-        NULL: ClassVar[CoreAuxv.TYPES] = ...
-        PAGESZ: ClassVar[CoreAuxv.TYPES] = ...
-        PHDR: ClassVar[CoreAuxv.TYPES] = ...
-        PHENT: ClassVar[CoreAuxv.TYPES] = ...
-        PHNUM: ClassVar[CoreAuxv.TYPES] = ...
-        PLATFORM: ClassVar[CoreAuxv.TYPES] = ...
-        RANDOM: ClassVar[CoreAuxv.TYPES] = ...
-        SECURE: ClassVar[CoreAuxv.TYPES] = ...
-        SYSINFO: ClassVar[CoreAuxv.TYPES] = ...
-        SYSINFO_EHDR: ClassVar[CoreAuxv.TYPES] = ...
-        UCACHEBSIZE: ClassVar[CoreAuxv.TYPES] = ...
-        UID: ClassVar[CoreAuxv.TYPES] = ...
+class CoreAuxv(Note):
+    class TYPE:
+        BASE: ClassVar[CoreAuxv.TYPE] = ...
+        BASE_PLATFORM: ClassVar[CoreAuxv.TYPE] = ...
+        CLKTCK: ClassVar[CoreAuxv.TYPE] = ...
+        DCACHEBSIZE: ClassVar[CoreAuxv.TYPE] = ...
+        EGID: ClassVar[CoreAuxv.TYPE] = ...
+        END: ClassVar[CoreAuxv.TYPE] = ...
+        ENTRY: ClassVar[CoreAuxv.TYPE] = ...
+        EUID: ClassVar[CoreAuxv.TYPE] = ...
+        EXECFD: ClassVar[CoreAuxv.TYPE] = ...
+        EXECFN: ClassVar[CoreAuxv.TYPE] = ...
+        FLAGS: ClassVar[CoreAuxv.TYPE] = ...
+        FPUCW: ClassVar[CoreAuxv.TYPE] = ...
+        GID: ClassVar[CoreAuxv.TYPE] = ...
+        HWCAP: ClassVar[CoreAuxv.TYPE] = ...
+        HWCAP2: ClassVar[CoreAuxv.TYPE] = ...
+        ICACHEBSIZE: ClassVar[CoreAuxv.TYPE] = ...
+        IGNORE: ClassVar[CoreAuxv.TYPE] = ...
+        IGNOREPPC: ClassVar[CoreAuxv.TYPE] = ...
+        NOTELF: ClassVar[CoreAuxv.TYPE] = ...
+        PAGESZ: ClassVar[CoreAuxv.TYPE] = ...
+        PHDR: ClassVar[CoreAuxv.TYPE] = ...
+        PHENT: ClassVar[CoreAuxv.TYPE] = ...
+        PHNUM: ClassVar[CoreAuxv.TYPE] = ...
+        RANDOM: ClassVar[CoreAuxv.TYPE] = ...
+        SECURE: ClassVar[CoreAuxv.TYPE] = ...
+        SYSINFO: ClassVar[CoreAuxv.TYPE] = ...
+        SYSINFO_EHDR: ClassVar[CoreAuxv.TYPE] = ...
+        TGT_PLATFORM: ClassVar[CoreAuxv.TYPE] = ...
+        UCACHEBSIZE: ClassVar[CoreAuxv.TYPE] = ...
+        UID: ClassVar[CoreAuxv.TYPE] = ...
         __name__: Any
         def __init__(self, *args, **kwargs) -> None: ...
         @staticmethod
-        def from_value(arg: int, /) -> lief.ELF.CoreAuxv.TYPES: ...
+        def from_value(arg: int, /) -> lief.ELF.CoreAuxv.TYPE: ...
         @property
         def value(self) -> int: ...
-    values: dict[lief.ELF.CoreAuxv.TYPES,int]
     def __init__(self, *args, **kwargs) -> None: ...
-    def get(self, type: lief.ELF.CoreAuxv.TYPES) -> object: ...
-    def has(self, type: lief.ELF.CoreAuxv.TYPES) -> bool: ...
-    def set(self, type: lief.ELF.CoreAuxv.TYPES, value: int) -> bool: ...
-    def __contains__(self, arg: lief.ELF.CoreAuxv.TYPES, /) -> bool: ...
-    def __getitem__(self, arg: lief.ELF.CoreAuxv.TYPES, /) -> int: ...
-    def __setitem__(self, arg0: lief.ELF.CoreAuxv.TYPES, arg1: int, /) -> None: ...
-
-class CoreFile(NoteDetails):
+    def get(self, type: lief.ELF.CoreAuxv.TYPE) -> Optional[int]: ...
+    @overload
+    def set(self, type: lief.ELF.CoreAuxv.TYPE, value: int) -> bool: ...
+    @overload
+    def set(self, arg: dict[lief.ELF.CoreAuxv.TYPE,int], /) -> bool: ...
+    def __getitem__(self, arg: lief.ELF.CoreAuxv.TYPE, /) -> Optional[int]: ...
+    @overload
+    def __setitem__(self, arg0: lief.ELF.CoreAuxv.TYPE, arg1: int, /) -> bool: ...
+    @overload
+    def __setitem__(self, arg: dict[lief.ELF.CoreAuxv.TYPE,int], /) -> bool: ...
+    @property
+    def values(self) -> dict[lief.ELF.CoreAuxv.TYPE,int]: ...
+
+class CoreFile(Note):
+    class entry_t:
+        end: int
+        file_ofs: int
+        path: str
+        start: int
+        def __init__(self, *args, **kwargs) -> None: ...
+
     class files_t:
         @overload
         def __init__(self) -> None: ...
         @overload
         def __init__(self, arg: lief.ELF.CoreFile.files_t) -> None: ...
         @overload
-        def __init__(self, arg: Iterable[lief.ELF.CoreFileEntry], /) -> None: ...
-        def append(self, arg: lief.ELF.CoreFileEntry, /) -> None: ...
+        def __init__(self, arg: Iterable[lief.ELF.CoreFile.entry_t], /) -> None: ...
+        def append(self, arg: lief.ELF.CoreFile.entry_t, /) -> None: ...
         def clear(self) -> None: ...
         def extend(self, arg: lief.ELF.CoreFile.files_t, /) -> None: ...
-        def insert(self, arg0: int, arg1: lief.ELF.CoreFileEntry, /) -> None: ...
-        def pop(self, index: int = ...) -> lief.ELF.CoreFileEntry: ...
+        def insert(self, arg0: int, arg1: lief.ELF.CoreFile.entry_t, /) -> None: ...
+        def pop(self, index: int = ...) -> lief.ELF.CoreFile.entry_t: ...
         def __bool__(self) -> bool: ...
         @overload
         def __delitem__(self, arg: int, /) -> None: ...
         @overload
         def __delitem__(self, arg: slice, /) -> None: ...
         @overload
-        def __getitem__(self, arg: int, /) -> lief.ELF.CoreFileEntry: ...
+        def __getitem__(self, arg: int, /) -> lief.ELF.CoreFile.entry_t: ...
         @overload
         def __getitem__(self, arg: slice, /) -> lief.ELF.CoreFile.files_t: ...
         def __iter__(self) -> Iterator: ...
         def __len__(self) -> int: ...
         @overload
-        def __setitem__(self, arg0: int, arg1: lief.ELF.CoreFileEntry, /) -> None: ...
+        def __setitem__(self, arg0: int, arg1: lief.ELF.CoreFile.entry_t, /) -> None: ...
         @overload
         def __setitem__(self, arg0: slice, arg1: lief.ELF.CoreFile.files_t, /) -> None: ...
     files: lief.ELF.CoreFile.files_t
@@ -632,122 +646,168 @@ class CoreFile(NoteDetails):
     def __iter__(self) -> Iterator: ...
     def __len__(self) -> int: ...
 
-class CoreFileEntry:
-    end: int
-    file_ofs: int
-    path: str
-    start: int
+class CorePrPsInfo(Note):
+    class info_t:
+        args: str
+        filename: str
+        flag: int
+        gid: int
+        nice: int
+        pgrp: int
+        pid: int
+        ppid: int
+        sid: int
+        sname: str
+        state: int
+        uid: int
+        zombie: bool
+        def __init__(self, *args, **kwargs) -> None: ...
+        @property
+        def args_stripped(self) -> str: ...
+        @property
+        def filename_stripped(self) -> str: ...
+    info: Optional[lief.ELF.CorePrPsInfo.info_t]
     def __init__(self, *args, **kwargs) -> None: ...
 
-class CorePrPsInfo(NoteDetails):
-    file_name: str
-    flags: int
-    gid: int
-    pgrp: int
-    pid: int
-    ppid: int
-    sid: int
-    uid: int
-    def __init__(self, *args, **kwargs) -> None: ...
+class CorePrStatus(Note):
+    class Registers:
+        class AARCH64:
+            PC: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            PSTATE: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X0: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X1: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X10: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X11: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X12: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X13: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X14: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X15: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X16: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X17: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X18: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X19: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X2: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X20: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X21: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X22: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X23: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X24: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X25: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X26: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X27: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X28: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X29: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X3: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X30: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X31: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X4: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X5: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X6: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X7: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X8: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            X9: ClassVar[CorePrStatus.Registers.AARCH64] = ...
+            __name__: Any
+            def __init__(self, *args, **kwargs) -> None: ...
+            @staticmethod
+            def from_value(arg: int, /) -> lief.ELF.CorePrStatus.Registers.AARCH64: ...
+            @property
+            def value(self) -> int: ...
+    
+        class ARM:
+            CPSR: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R0: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R1: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R10: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R11: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R12: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R13: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R14: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R15: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R2: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R3: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R4: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R5: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R6: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R7: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R8: ClassVar[CorePrStatus.Registers.ARM] = ...
+            R9: ClassVar[CorePrStatus.Registers.ARM] = ...
+            __name__: Any
+            def __init__(self, *args, **kwargs) -> None: ...
+            @staticmethod
+            def from_value(arg: int, /) -> lief.ELF.CorePrStatus.Registers.ARM: ...
+            @property
+            def value(self) -> int: ...
+    
+        class X86:
+            CS: ClassVar[CorePrStatus.Registers.X86] = ...
+            DS: ClassVar[CorePrStatus.Registers.X86] = ...
+            EAX: ClassVar[CorePrStatus.Registers.X86] = ...
+            EBP: ClassVar[CorePrStatus.Registers.X86] = ...
+            EBX: ClassVar[CorePrStatus.Registers.X86] = ...
+            ECX: ClassVar[CorePrStatus.Registers.X86] = ...
+            EDI: ClassVar[CorePrStatus.Registers.X86] = ...
+            EDX: ClassVar[CorePrStatus.Registers.X86] = ...
+            EFLAGS: ClassVar[CorePrStatus.Registers.X86] = ...
+            EIP: ClassVar[CorePrStatus.Registers.X86] = ...
+            ES: ClassVar[CorePrStatus.Registers.X86] = ...
+            ESI: ClassVar[CorePrStatus.Registers.X86] = ...
+            ESP: ClassVar[CorePrStatus.Registers.X86] = ...
+            FS: ClassVar[CorePrStatus.Registers.X86] = ...
+            GS: ClassVar[CorePrStatus.Registers.X86] = ...
+            ORIG_EAX: ClassVar[CorePrStatus.Registers.X86] = ...
+            SS: ClassVar[CorePrStatus.Registers.X86] = ...
+            __name__: Any
+            def __init__(self, *args, **kwargs) -> None: ...
+            @staticmethod
+            def from_value(arg: int, /) -> lief.ELF.CorePrStatus.Registers.X86: ...
+            @property
+            def value(self) -> int: ...
+    
+        class X86_64:
+            CS: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            EFLAGS: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            ORIG_RAX: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            R10: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            R11: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            R12: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            R13: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            R14: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            R15: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            R8: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            R9: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RAX: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RBP: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RBX: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RCX: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RDI: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RDX: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RIP: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RSI: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            RSP: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            SS: ClassVar[CorePrStatus.Registers.X86_64] = ...
+            __name__: Any
+            def __init__(self, *args, **kwargs) -> None: ...
+            @staticmethod
+            def from_value(arg: int, /) -> lief.ELF.CorePrStatus.Registers.X86_64: ...
+            @property
+            def value(self) -> int: ...
+        def __init__(self, *args, **kwargs) -> None: ...
 
-class CorePrStatus(NoteDetails):
-    class REGISTERS:
-        AARCH64_PC: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X0: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X1: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X10: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X11: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X12: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X13: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X14: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X15: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X16: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X17: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X18: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X19: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X2: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X20: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X21: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X22: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X23: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X24: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X25: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X26: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X27: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X28: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X29: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X3: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X30: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X31: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X4: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X5: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X6: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X7: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X8: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64_X9: ClassVar[CorePrStatus.REGISTERS] = ...
-        AARCH64__: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_CPSR: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R0: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R1: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R10: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R11: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R12: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R13: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R14: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R15: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R2: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R3: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R4: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R5: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R6: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R7: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R8: ClassVar[CorePrStatus.REGISTERS] = ...
-        ARM_R9: ClassVar[CorePrStatus.REGISTERS] = ...
-        UNKNOWN: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_CS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_EFLAGS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_R10: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_R11: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_R12: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_R13: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_R14: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_R15: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_R8: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_R9: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RAX: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RBP: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RBX: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RCX: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RDI: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RDX: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RIP: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RSI: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_RSP: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64_SS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_64__: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_CS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_DS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_EAX: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_EBP: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_EBX: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_ECX: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_EDI: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_EDX: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_EFLAGS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_EIP: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_ES: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_ESI: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_ESP: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_FS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_GS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86_SS: ClassVar[CorePrStatus.REGISTERS] = ...
-        X86__: ClassVar[CorePrStatus.REGISTERS] = ...
-        __name__: Any
+    class pr_status_t:
+        cstime: lief.ELF.CorePrStatus.timeval_t  # type: ignore
+        cursig: int
+        cutime: lief.ELF.CorePrStatus.timeval_t  # type: ignore
+        info: lief.ELF.CorePrStatus.siginfo_t  # type: ignore
+        pgrp: int
+        pid: int
+        ppid: int
+        reserved: int
+        sid: int
+        sighold: int
+        sigpend: int
+        stime: lief.ELF.CorePrStatus.timeval_t  # type: ignore
+        utime: lief.ELF.CorePrStatus.timeval_t  # type: ignore
         def __init__(self, *args, **kwargs) -> None: ...
-        @staticmethod
-        def from_value(arg: int, /) -> lief.ELF.CorePrStatus.REGISTERS: ...
-        @property
-        def value(self) -> int: ...
 
     class siginfo_t:
         errno: int
@@ -755,35 +815,59 @@ class CorePrStatus(NoteDetails):
         signo: int
         def __init__(self, *args, **kwargs) -> None: ...
 
-    class timeval:
+    class timeval_t:
         sec: int
         usec: int
         def __init__(self, *args, **kwargs) -> None: ...
-    cstime: lief.ELF.CorePrStatus.timeval
-    current_sig: int
-    cutime: lief.ELF.CorePrStatus.timeval
-    pgrp: int
-    pid: int
-    ppid: int
-    register_context: dict[lief.ELF.CorePrStatus.REGISTERS,int]
-    sid: int
-    sighold: int
-    siginfo: lief.ELF.CorePrStatus.siginfo_t
-    sigpend: int
-    stime: lief.ELF.CorePrStatus.timeval
-    utime: lief.ELF.CorePrStatus.timeval
+    status: lief.ELF.CorePrStatus.pr_status_t
     def __init__(self, *args, **kwargs) -> None: ...
-    def get(self, register: lief.ELF.CorePrStatus.REGISTERS) -> object: ...
-    def has(self, register: lief.ELF.CorePrStatus.REGISTERS) -> bool: ...
-    def set(self, register: lief.ELF.CorePrStatus.REGISTERS, value: int) -> bool: ...
-    def __contains__(self, arg: lief.ELF.CorePrStatus.REGISTERS, /) -> bool: ...
-    def __getitem__(self, arg: lief.ELF.CorePrStatus.REGISTERS, /) -> int: ...
-    def __setitem__(self, arg0: lief.ELF.CorePrStatus.REGISTERS, arg1: int, /) -> None: ...
-
-class CoreSigInfo(NoteDetails):
-    sigcode: int
-    sigerrno: int
-    signo: int
+    @overload
+    def get(self, reg: lief.ELF.CorePrStatus.Registers.X86) -> Optional[int]: ...
+    @overload
+    def get(self, reg: lief.ELF.CorePrStatus.Registers.X86_64) -> Optional[int]: ...
+    @overload
+    def get(self, reg: lief.ELF.CorePrStatus.Registers.ARM) -> Optional[int]: ...
+    @overload
+    def get(self, reg: lief.ELF.CorePrStatus.Registers.AARCH64) -> Optional[int]: ...
+    @overload
+    def set(self, reg: lief.ELF.CorePrStatus.Registers.X86, value: int) -> lief.ok_error_t: ...
+    @overload
+    def set(self, reg: lief.ELF.CorePrStatus.Registers.X86_64, value: int) -> lief.ok_error_t: ...
+    @overload
+    def set(self, reg: lief.ELF.CorePrStatus.Registers.ARM, value: int) -> lief.ok_error_t: ...
+    @overload
+    def set(self, reg: lief.ELF.CorePrStatus.Registers.AARCH64, value: int) -> lief.ok_error_t: ...
+    @overload
+    def __getitem__(self, arg: lief.ELF.CorePrStatus.Registers.X86, /) -> Optional[int]: ...
+    @overload
+    def __getitem__(self, arg: lief.ELF.CorePrStatus.Registers.X86_64, /) -> Optional[int]: ...
+    @overload
+    def __getitem__(self, arg: lief.ELF.CorePrStatus.Registers.ARM, /) -> Optional[int]: ...
+    @overload
+    def __getitem__(self, arg: lief.ELF.CorePrStatus.Registers.AARCH64, /) -> Optional[int]: ...
+    @overload
+    def __setitem__(self, arg0: lief.ELF.CorePrStatus.Registers.X86, arg1: int, /) -> lief.ok_error_t: ...
+    @overload
+    def __setitem__(self, arg0: lief.ELF.CorePrStatus.Registers.X86_64, arg1: int, /) -> lief.ok_error_t: ...
+    @overload
+    def __setitem__(self, arg0: lief.ELF.CorePrStatus.Registers.ARM, arg1: int, /) -> lief.ok_error_t: ...
+    @overload
+    def __setitem__(self, arg0: lief.ELF.CorePrStatus.Registers.AARCH64, arg1: int, /) -> lief.ok_error_t: ...
+    @property
+    def architecture(self) -> lief.ELF.ARCH: ...
+    @property
+    def pc(self) -> Optional[int]: ...
+    @property
+    def register_values(self) -> list[int]: ...
+    @property
+    def return_value(self) -> Optional[int]: ...
+    @property
+    def sp(self) -> Optional[int]: ...
+
+class CoreSigInfo(Note):
+    sigcode: Optional[int]
+    sigerrno: Optional[int]
+    signo: Optional[int]
     def __init__(self, *args, **kwargs) -> None: ...
 
 class DYNAMIC_FLAGS:
@@ -1313,90 +1397,97 @@ class MIPS_EFLAGS:
     @property
     def value(self) -> int: ...
 
-class NOTE_ABIS:
-    FREEBSD: ClassVar[NOTE_ABIS] = ...
-    GNU: ClassVar[NOTE_ABIS] = ...
-    LINUX: ClassVar[NOTE_ABIS] = ...
-    NETBSD: ClassVar[NOTE_ABIS] = ...
-    SOLARIS2: ClassVar[NOTE_ABIS] = ...
-    SYLLABLE: ClassVar[NOTE_ABIS] = ...
-    UNKNOWN: ClassVar[NOTE_ABIS] = ...
-    __name__: Any
-    def __init__(self, *args, **kwargs) -> None: ...
-    @staticmethod
-    def from_value(arg: int, /) -> lief.ELF.NOTE_ABIS: ...
-    @property
-    def value(self) -> int: ...
-
-class NOTE_TYPES:
-    ABI_TAG: ClassVar[NOTE_TYPES] = ...
-    BUILD_ID: ClassVar[NOTE_TYPES] = ...
-    CRASHPAD: ClassVar[NOTE_TYPES] = ...
-    GNU_BUILD_ATTRIBUTE_FUNC: ClassVar[NOTE_TYPES] = ...
-    GNU_BUILD_ATTRIBUTE_OPEN: ClassVar[NOTE_TYPES] = ...
-    GOLD_VERSION: ClassVar[NOTE_TYPES] = ...
-    HWCAP: ClassVar[NOTE_TYPES] = ...
-    PROPERTY_TYPE_0: ClassVar[NOTE_TYPES] = ...
-    UNKNOWN: ClassVar[NOTE_TYPES] = ...
-    __name__: Any
-    def __init__(self, *args, **kwargs) -> None: ...
-    @staticmethod
-    def from_value(arg: int, /) -> lief.ELF.NOTE_TYPES: ...
-    @property
-    def value(self) -> int: ...
-
-class NOTE_TYPES_CORE:
-    ARM_HW_BREAK: ClassVar[NOTE_TYPES_CORE] = ...
-    ARM_HW_WATCH: ClassVar[NOTE_TYPES_CORE] = ...
-    ARM_SVE: ClassVar[NOTE_TYPES_CORE] = ...
-    ARM_SYSTEM_CALL: ClassVar[NOTE_TYPES_CORE] = ...
-    ARM_TLS: ClassVar[NOTE_TYPES_CORE] = ...
-    ARM_VFP: ClassVar[NOTE_TYPES_CORE] = ...
-    AUXV: ClassVar[NOTE_TYPES_CORE] = ...
-    FILE: ClassVar[NOTE_TYPES_CORE] = ...
-    I386_IOPERM: ClassVar[NOTE_TYPES_CORE] = ...
-    I386_TLS: ClassVar[NOTE_TYPES_CORE] = ...
-    I386_XSTATE: ClassVar[NOTE_TYPES_CORE] = ...
-    PRFPREG: ClassVar[NOTE_TYPES_CORE] = ...
-    PRPSINFO: ClassVar[NOTE_TYPES_CORE] = ...
-    PRSTATUS: ClassVar[NOTE_TYPES_CORE] = ...
-    SIGINFO: ClassVar[NOTE_TYPES_CORE] = ...
-    TASKSTRUCT: ClassVar[NOTE_TYPES_CORE] = ...
-    UNKNOWN: ClassVar[NOTE_TYPES_CORE] = ...
-    __name__: Any
-    def __init__(self, *args, **kwargs) -> None: ...
-    @staticmethod
-    def from_value(arg: int, /) -> lief.ELF.NOTE_TYPES_CORE: ...
-    @property
-    def value(self) -> int: ...
-
 class Note(lief.Object):
-    description: list[int]
+    class TYPE:
+        ANDROID_IDENT: ClassVar[Note.TYPE] = ...
+        ANDROID_KUSER: ClassVar[Note.TYPE] = ...
+        ANDROID_MEMTAG: ClassVar[Note.TYPE] = ...
+        CORE_ARM_HW_BREAK: ClassVar[Note.TYPE] = ...
+        CORE_ARM_HW_WATCH: ClassVar[Note.TYPE] = ...
+        CORE_ARM_PACA_KEYS: ClassVar[Note.TYPE] = ...
+        CORE_ARM_PACG_KEYS: ClassVar[Note.TYPE] = ...
+        CORE_ARM_PAC_MASK: ClassVar[Note.TYPE] = ...
+        CORE_ARM_SVE: ClassVar[Note.TYPE] = ...
+        CORE_ARM_SYSTEM_CALL: ClassVar[Note.TYPE] = ...
+        CORE_ARM_TLS: ClassVar[Note.TYPE] = ...
+        CORE_ARM_VFP: ClassVar[Note.TYPE] = ...
+        CORE_AUXV: ClassVar[Note.TYPE] = ...
+        CORE_FILE: ClassVar[Note.TYPE] = ...
+        CORE_FPREGS: ClassVar[Note.TYPE] = ...
+        CORE_FPREGSET: ClassVar[Note.TYPE] = ...
+        CORE_LWPSINFO: ClassVar[Note.TYPE] = ...
+        CORE_LWPSTATUS: ClassVar[Note.TYPE] = ...
+        CORE_PAC_ENABLED_KEYS: ClassVar[Note.TYPE] = ...
+        CORE_PRPSINFO: ClassVar[Note.TYPE] = ...
+        CORE_PRSTATUS: ClassVar[Note.TYPE] = ...
+        CORE_PRXFPREG: ClassVar[Note.TYPE] = ...
+        CORE_PSINFO: ClassVar[Note.TYPE] = ...
+        CORE_PSTATUS: ClassVar[Note.TYPE] = ...
+        CORE_SIGINFO: ClassVar[Note.TYPE] = ...
+        CORE_TAGGED_ADDR_CTRL: ClassVar[Note.TYPE] = ...
+        CORE_TASKSTRUCT: ClassVar[Note.TYPE] = ...
+        CORE_WIN32PSTATUS: ClassVar[Note.TYPE] = ...
+        CORE_X86_CET: ClassVar[Note.TYPE] = ...
+        CORE_X86_IOPERM: ClassVar[Note.TYPE] = ...
+        CORE_X86_TLS: ClassVar[Note.TYPE] = ...
+        CORE_X86_XSTATE: ClassVar[Note.TYPE] = ...
+        CRASHPAD: ClassVar[Note.TYPE] = ...
+        GNU_ABI_TAG: ClassVar[Note.TYPE] = ...
+        GNU_BUILD_ATTRIBUTE_FUNC: ClassVar[Note.TYPE] = ...
+        GNU_BUILD_ATTRIBUTE_OPEN: ClassVar[Note.TYPE] = ...
+        GNU_BUILD_ID: ClassVar[Note.TYPE] = ...
+        GNU_GOLD_VERSION: ClassVar[Note.TYPE] = ...
+        GNU_HWCAP: ClassVar[Note.TYPE] = ...
+        GNU_PROPERTY_TYPE_0: ClassVar[Note.TYPE] = ...
+        GO_BUILDID: ClassVar[Note.TYPE] = ...
+        STAPSDT: ClassVar[Note.TYPE] = ...
+        UNKNOWN: ClassVar[Note.TYPE] = ...
+        __name__: Any
+        def __init__(self, *args, **kwargs) -> None: ...
+        @staticmethod
+        def from_value(arg: int, /) -> lief.ELF.Note.TYPE: ...
+        @property
+        def value(self) -> int: ...
+    description: memoryview
     name: str
-    type: lief.ELF.NOTE_TYPES
-    type_core: lief.ELF.NOTE_TYPES_CORE
+    def __init__(self, *args, **kwargs) -> None: ...
+    def copy(self) -> Optional[lief.ELF.Note]: ...
     @overload
-    def __init__(self) -> None: ...
+    @staticmethod
+    def create(name: str, original_type: int, description: list[int], file_type: lief.ELF.E_TYPE = ..., arch: lief.ELF.ARCH = ..., cls: lief.ELF.ELF_CLASS = ...) -> Optional[lief.ELF.Note]: ...
     @overload
-    def __init__(self, name: str, type: lief.ELF.NOTE_TYPES, description: list[int]) -> None: ...
-    @property
-    def details(self) -> lief.ELF.NoteDetails: ...
-    @property
-    def is_android(self) -> bool: ...
+    @staticmethod
+    def create(raw: bytes, file_type: lief.ELF.E_TYPE = ..., arch: lief.ELF.ARCH = ..., cls: lief.ELF.ELF_CLASS = ...) -> Optional[lief.ELF.Note]: ...
+    @overload
+    @staticmethod
+    def create(name: str, type: lief.ELF.Note.TYPE, description: list[int], arch: lief.ELF.ARCH = ..., cls: lief.ELF.ELF_CLASS = ...) -> Optional[lief.ELF.Note]: ...
     @property
-    def is_core(self) -> bool: ...
+    def original_type(self) -> int: ...
     @property
     def size(self) -> int: ...
-
-class NoteAbi(NoteDetails):
+    @property
+    def type(self) -> lief.ELF.Note.TYPE: ...
+
+class NoteAbi(Note):
+    class ABI:
+        FREEBSD: ClassVar[NoteAbi.ABI] = ...
+        GNU: ClassVar[NoteAbi.ABI] = ...
+        LINUX: ClassVar[NoteAbi.ABI] = ...
+        NACL: ClassVar[NoteAbi.ABI] = ...
+        NETBSD: ClassVar[NoteAbi.ABI] = ...
+        SOLARIS2: ClassVar[NoteAbi.ABI] = ...
+        SYLLABLE: ClassVar[NoteAbi.ABI] = ...
+        __name__: Any
+        def __init__(self, *args, **kwargs) -> None: ...
+        @staticmethod
+        def from_value(arg: int, /) -> lief.ELF.NoteAbi.ABI: ...
+        @property
+        def value(self) -> int: ...
     def __init__(self, *args, **kwargs) -> None: ...
     @property
-    def abi(self) -> lief.ELF.NOTE_ABIS: ...
+    def abi(self) -> Optional[lief.ELF.NoteAbi.ABI]: ...
     @property
-    def version(self) -> list[int]: ...
-
-class NoteDetails(lief.Object):
-    def __init__(self, *args, **kwargs) -> None: ...
+    def version(self) -> Optional[list[int]]: ...
 
 class OS_ABI:
     AIX: ClassVar[OS_ABI] = ...
diff --git a/api/python/lief/__init__.pyi b/api/python/lief/__init__.pyi
index 3c33001b74..53ae988a42 100644
--- a/api/python/lief/__init__.pyi
+++ b/api/python/lief/__init__.pyi
@@ -261,6 +261,18 @@ class lief_errors:
     __name__: Any
     def __init__(self, *args, **kwargs) -> None: ...
 
+class ok_error_t:
+    def __init__(self, *args, **kwargs) -> None: ...
+    def __bool__(self) -> bool: ...
+    @property
+    def error(self) -> lief_errors: ...
+    @property
+    def is_error(self) -> bool: ...
+    @property
+    def is_value(self) -> bool: ...
+    @property
+    def value(self) -> lief.ok_t: ...
+
 class ok_t:
     def __init__(self, *args, **kwargs) -> None: ...
     def __bool__(self) -> bool: ...
diff --git a/api/python/pyproject.toml b/api/python/pyproject.toml
index 4292dc3e4a..3028fa2740 100644
--- a/api/python/pyproject.toml
+++ b/api/python/pyproject.toml
@@ -30,5 +30,5 @@ homepage = "https://lief-project.github.io/"
 documentation = "https://lief-project.github.io/doc/latest/"
 repository = "https://github.com/lief-project/LIEF"
 changelog = "https://lief-project.github.io/doc/latest/changelog.html"
-Funding = "https://lief-project.github.io/about"
+Funding = "https://github.com/sponsors/lief-project"
 Tracker = "https://github.com/lief-project/LIEF/issues"
diff --git a/api/python/src/ELF/enums.cpp b/api/python/src/ELF/enums.cpp
index edbccdd875..c9a953dc54 100644
--- a/api/python/src/ELF/enums.cpp
+++ b/api/python/src/ELF/enums.cpp
@@ -1177,50 +1177,6 @@ void init_enums(nb::module_& m) {
     .value(PY_ENUM(DYNSYM_COUNT_METHODS::COUNT_RELOCATIONS));
 
 
-  enum_<NOTE_TYPES>(m, "NOTE_TYPES")
-    .value(PY_ENUM(NOTE_TYPES::NT_UNKNOWN))
-    .value(PY_ENUM(NOTE_TYPES::NT_GNU_ABI_TAG))
-    .value(PY_ENUM(NOTE_TYPES::NT_GNU_HWCAP))
-    .value(PY_ENUM(NOTE_TYPES::NT_GNU_BUILD_ID))
-    .value(PY_ENUM(NOTE_TYPES::NT_GNU_GOLD_VERSION))
-    .value(PY_ENUM(NOTE_TYPES::NT_GNU_PROPERTY_TYPE_0))
-    .value(PY_ENUM(NOTE_TYPES::NT_GNU_BUILD_ATTRIBUTE_OPEN))
-    .value(PY_ENUM(NOTE_TYPES::NT_GNU_BUILD_ATTRIBUTE_FUNC))
-    .value(PY_ENUM(NOTE_TYPES::NT_CRASHPAD));
-
-
-  enum_<NOTE_TYPES_CORE>(m, "NOTE_TYPES_CORE")
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_CORE_UNKNOWN))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_PRSTATUS))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_PRFPREG))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_PRPSINFO))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_TASKSTRUCT))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_AUXV))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_SIGINFO))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_FILE))
-
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_ARM_VFP))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_ARM_TLS))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_ARM_HW_BREAK))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_ARM_HW_WATCH))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_ARM_SYSTEM_CALL))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_ARM_SVE))
-
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_386_TLS))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_386_IOPERM))
-    .value(PY_ENUM(NOTE_TYPES_CORE::NT_386_XSTATE));
-
-
-  enum_<NOTE_ABIS>(m, "NOTE_ABIS")
-    .value(PY_ENUM(NOTE_ABIS::ELF_NOTE_UNKNOWN))
-    .value(PY_ENUM(NOTE_ABIS::ELF_NOTE_OS_LINUX))
-    .value(PY_ENUM(NOTE_ABIS::ELF_NOTE_OS_GNU))
-    .value(PY_ENUM(NOTE_ABIS::ELF_NOTE_OS_SOLARIS2))
-    .value(PY_ENUM(NOTE_ABIS::ELF_NOTE_OS_FREEBSD))
-    .value(PY_ENUM(NOTE_ABIS::ELF_NOTE_OS_NETBSD))
-    .value(PY_ENUM(NOTE_ABIS::ELF_NOTE_OS_SYLLABLE));
-
-
   enum_<RELOCATION_PURPOSES>(m, "RELOCATION_PURPOSES")
     .value(PY_ENUM(RELOCATION_PURPOSES::RELOC_PURPOSE_NONE))
     .value(PY_ENUM(RELOCATION_PURPOSES::RELOC_PURPOSE_PLTGOT))
diff --git a/api/python/src/ELF/init.cpp b/api/python/src/ELF/init.cpp
index 40f8cc041e..f4f7ec7f68 100644
--- a/api/python/src/ELF/init.cpp
+++ b/api/python/src/ELF/init.cpp
@@ -30,15 +30,6 @@
 #include "LIEF/ELF/DynamicSharedObject.hpp"
 #include "LIEF/ELF/GnuHash.hpp"
 #include "LIEF/ELF/Header.hpp"
-#include "LIEF/ELF/Note.hpp"
-#include "LIEF/ELF/NoteDetails.hpp"
-#include "LIEF/ELF/NoteDetails/AndroidNote.hpp"
-#include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
-#include "LIEF/ELF/NoteDetails/core/CoreAuxv.hpp"
-#include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
-#include "LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp"
-#include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
-#include "LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp"
 #include "LIEF/ELF/Parser.hpp"
 #include "LIEF/ELF/Relocation.hpp"
 #include "LIEF/ELF/Section.hpp"
@@ -51,10 +42,30 @@
 #include "LIEF/ELF/SysvHash.hpp"
 #include "LIEF/ELF/utils.hpp"
 
+#include "LIEF/ELF/Note.hpp"
+#include "LIEF/ELF/NoteDetails/AndroidIdent.hpp"
+#include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
+#include "LIEF/ELF/NoteDetails/core/CoreAuxv.hpp"
+#include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
+#include "LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp"
+#include "LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp"
+#include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
+
 #define CREATE(X,Y) create<X>(Y)
 
 namespace LIEF::ELF::py {
 
+void init_notes(nb::module_& m) {
+  CREATE(Note, m);
+  CREATE(AndroidIdent, m);
+  CREATE(NoteAbi, m);
+  CREATE(CoreAuxv, m);
+  CREATE(CoreFile, m);
+  CREATE(CorePrPsInfo, m);
+  CREATE(CoreSigInfo, m);
+  CREATE(CorePrStatus, m);
+}
+
 void init_objects(nb::module_& m) {
   CREATE(ParserConfig, m);
   CREATE(Parser, m);
@@ -79,16 +90,8 @@ void init_objects(nb::module_& m) {
   CREATE(GnuHash, m);
   CREATE(SysvHash, m);
   CREATE(Builder, m);
-  CREATE(Note, m);
-  CREATE(NoteDetails, m);
-  CREATE(AndroidNote, m);
-  CREATE(NoteAbi, m);
-  CREATE(CorePrPsInfo, m);
-  CREATE(CoreFile, m);
-  CREATE(CoreFileEntry, m);
-  CREATE(CorePrStatus, m);
-  CREATE(CoreAuxv, m);
-  CREATE(CoreSigInfo, m);
+
+  init_notes(m);
 }
 
 inline void init_utils(nb::module_&) {
diff --git a/api/python/src/ELF/objects/CMakeLists.txt b/api/python/src/ELF/objects/CMakeLists.txt
index 616be71079..032af23055 100644
--- a/api/python/src/ELF/objects/CMakeLists.txt
+++ b/api/python/src/ELF/objects/CMakeLists.txt
@@ -1,32 +1,28 @@
-set(LIEF_PYTHON_ELF_OBJECT_SRC
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyParser.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyBinary.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyBuilder.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyDynamicEntry.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyDynamicEntryArray.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyDynamicEntryFlags.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyDynamicEntryLibrary.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyDynamicEntryRpath.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyDynamicEntryRunPath.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyDynamicSharedObject.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyGnuHash.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyHeader.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyParserConfig.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyNote.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyNoteDetails.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyRelocation.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySection.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySegment.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySymbol.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySymbolVersion.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySymbolVersionAux.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySymbolVersionAuxRequirement.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySymbolVersionRequirement.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySymbolVersionDefinition.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pySysvHash.cpp"
+target_sources(pyLIEF PRIVATE
+  pyParser.cpp
+  pyBinary.cpp
+  pyBuilder.cpp
+  pyDynamicEntry.cpp
+  pyDynamicEntryArray.cpp
+  pyDynamicEntryFlags.cpp
+  pyDynamicEntryLibrary.cpp
+  pyDynamicEntryRpath.cpp
+  pyDynamicEntryRunPath.cpp
+  pyDynamicSharedObject.cpp
+  pyGnuHash.cpp
+  pyHeader.cpp
+  pyParserConfig.cpp
+  pyNote.cpp
+  pyRelocation.cpp
+  pySection.cpp
+  pySegment.cpp
+  pySymbol.cpp
+  pySymbolVersion.cpp
+  pySymbolVersionAux.cpp
+  pySymbolVersionAuxRequirement.cpp
+  pySymbolVersionRequirement.cpp
+  pySymbolVersionDefinition.cpp
+  pySysvHash.cpp
 )
 
-target_sources(pyLIEF PRIVATE ${LIEF_PYTHON_ELF_OBJECT_SRC})
-
 add_subdirectory(NoteDetails)
-
diff --git a/api/python/src/ELF/objects/NoteDetails/CMakeLists.txt b/api/python/src/ELF/objects/NoteDetails/CMakeLists.txt
index b86d00ac79..46ef27c73d 100644
--- a/api/python/src/ELF/objects/NoteDetails/CMakeLists.txt
+++ b/api/python/src/ELF/objects/NoteDetails/CMakeLists.txt
@@ -1,9 +1,7 @@
-set(LIEF_PYTHON_ELF_NOTE_DETAILS_SRC
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyAndroidNote.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyNoteAbi.cpp"
+target_sources(pyLIEF PRIVATE
+  pyAndroidIdent.cpp
+  pyNoteAbi.cpp
 )
 
-target_sources(pyLIEF PRIVATE "${LIEF_PYTHON_ELF_NOTE_DETAILS_SRC}")
-
 add_subdirectory(core)
 
diff --git a/api/python/src/ELF/objects/NoteDetails/core/CMakeLists.txt b/api/python/src/ELF/objects/NoteDetails/core/CMakeLists.txt
index b4e16fbfff..628848bead 100644
--- a/api/python/src/ELF/objects/NoteDetails/core/CMakeLists.txt
+++ b/api/python/src/ELF/objects/NoteDetails/core/CMakeLists.txt
@@ -1,12 +1,7 @@
-set(LIEF_PYTHON_ELF_NOTE_DETAILS_CORE
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyCorePrPsInfo.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyCoreFile.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyCoreFileEntry.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyCorePrStatus.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyCoreAuxv.cpp"
-  "${CMAKE_CURRENT_SOURCE_DIR}/pyCoreSigInfo.cpp"
+target_sources(pyLIEF PRIVATE
+  pyCorePrPsInfo.cpp
+  pyCoreFile.cpp
+  pyCorePrStatus.cpp
+  pyCoreAuxv.cpp
+  pyCoreSigInfo.cpp
 )
-target_sources(pyLIEF PRIVATE "${LIEF_PYTHON_ELF_NOTE_DETAILS_CORE}")
-
-
-
diff --git a/api/python/src/ELF/objects/NoteDetails/core/pyCoreAuxv.cpp b/api/python/src/ELF/objects/NoteDetails/core/pyCoreAuxv.cpp
index 17ac476ae5..e708c8badf 100644
--- a/api/python/src/ELF/objects/NoteDetails/core/pyCoreAuxv.cpp
+++ b/api/python/src/ELF/objects/NoteDetails/core/pyCoreAuxv.cpp
@@ -21,90 +21,87 @@
 #include "ELF/pyELF.hpp"
 
 #include "LIEF/ELF/NoteDetails/core/CoreAuxv.hpp"
-#include "LIEF/ELF/EnumToString.hpp"
 
+#include "pyErr.hpp"
 #include "enums_wrapper.hpp"
 
-#define PY_ENUM(x) LIEF::ELF::to_string(x), x
 
 namespace LIEF::ELF::py {
 
 template<>
 void create<CoreAuxv>(nb::module_& m) {
 
-  nb::class_<CoreAuxv, NoteDetails> cls(m, "CoreAuxv");
-  LIEF::enum_<AUX_TYPE>(cls, "TYPES")
-    .value(PY_ENUM(AUX_TYPE::AT_NULL))
-    .value(PY_ENUM(AUX_TYPE::AT_IGNORE))
-    .value(PY_ENUM(AUX_TYPE::AT_EXECFD))
-    .value(PY_ENUM(AUX_TYPE::AT_PHDR))
-    .value(PY_ENUM(AUX_TYPE::AT_PHENT))
-    .value(PY_ENUM(AUX_TYPE::AT_PHNUM))
-    .value(PY_ENUM(AUX_TYPE::AT_PAGESZ))
-    .value(PY_ENUM(AUX_TYPE::AT_BASE))
-    .value(PY_ENUM(AUX_TYPE::AT_FLAGS))
-    .value(PY_ENUM(AUX_TYPE::AT_ENTRY))
-    .value(PY_ENUM(AUX_TYPE::AT_NOTELF))
-    .value(PY_ENUM(AUX_TYPE::AT_UID))
-    .value(PY_ENUM(AUX_TYPE::AT_EUID))
-    .value(PY_ENUM(AUX_TYPE::AT_GID))
-    .value(PY_ENUM(AUX_TYPE::AT_EGID))
-    .value(PY_ENUM(AUX_TYPE::AT_CLKTCK))
-    .value(PY_ENUM(AUX_TYPE::AT_PLATFORM))
-    .value(PY_ENUM(AUX_TYPE::AT_HWCAP))
-    .value(PY_ENUM(AUX_TYPE::AT_HWCAP2))
-    .value(PY_ENUM(AUX_TYPE::AT_FPUCW))
-    .value(PY_ENUM(AUX_TYPE::AT_DCACHEBSIZE))
-    .value(PY_ENUM(AUX_TYPE::AT_ICACHEBSIZE))
-    .value(PY_ENUM(AUX_TYPE::AT_UCACHEBSIZE))
-    .value(PY_ENUM(AUX_TYPE::AT_IGNOREPPC))
-    .value(PY_ENUM(AUX_TYPE::AT_SECURE))
-    .value(PY_ENUM(AUX_TYPE::AT_BASE_PLATFORM))
-    .value(PY_ENUM(AUX_TYPE::AT_RANDOM))
-    .value(PY_ENUM(AUX_TYPE::AT_EXECFN))
-    .value(PY_ENUM(AUX_TYPE::AT_SYSINFO))
-    .value(PY_ENUM(AUX_TYPE::AT_SYSINFO_EHDR))
-    .value(PY_ENUM(AUX_TYPE::AT_L1I_CACHESHAPE))
-    .value(PY_ENUM(AUX_TYPE::AT_L1D_CACHESHAPE));
+  nb::class_<CoreAuxv, Note> cls(m, "CoreAuxv");
+  #define PY_ENUM(X) .value(LIEF::ELF::to_string(CoreAuxv::TYPE::X), CoreAuxv::TYPE::X)
+  LIEF::enum_<CoreAuxv::TYPE>(cls, "TYPE")
+    PY_ENUM(END)
+    PY_ENUM(IGNORE)
+    PY_ENUM(EXECFD)
+    PY_ENUM(PHDR)
+    PY_ENUM(PHENT)
+    PY_ENUM(PHNUM)
+    PY_ENUM(PAGESZ)
+    PY_ENUM(BASE)
+    PY_ENUM(FLAGS)
+    PY_ENUM(ENTRY)
+    PY_ENUM(NOTELF)
+    PY_ENUM(UID)
+    PY_ENUM(EUID)
+    PY_ENUM(GID)
+    PY_ENUM(EGID)
+    PY_ENUM(TGT_PLATFORM)
+    PY_ENUM(HWCAP)
+    PY_ENUM(CLKTCK)
+    PY_ENUM(FPUCW)
+    PY_ENUM(DCACHEBSIZE)
+    PY_ENUM(ICACHEBSIZE)
+    PY_ENUM(UCACHEBSIZE)
+    PY_ENUM(IGNOREPPC)
+    PY_ENUM(SECURE)
+    PY_ENUM(BASE_PLATFORM)
+    PY_ENUM(RANDOM)
+    PY_ENUM(HWCAP2)
+    PY_ENUM(EXECFN)
+    PY_ENUM(SYSINFO)
+    PY_ENUM(SYSINFO_EHDR)
+  ;
+  #undef PY_ENUM
 
   cls
-    .def_prop_rw("values",
-        nb::overload_cast<>(&CoreAuxv::values, nb::const_),
-        nb::overload_cast<const CoreAuxv::val_context_t&>(&CoreAuxv::values),
-        "Current values as a dictionary for which keys are AUXV types"_doc)
+    .def_prop_ro("values", &CoreAuxv::values,
+      R"doc(
+      Return the auxiliary vector as a dictionary of :class:`.TYPE` / `int`
+      )doc"
+    )
 
     .def("get",
-        [] (const CoreAuxv& status, AUX_TYPE atype) -> nb::object {
-          bool error;
-          const uint64_t val = status.get(atype, &error);
-          if (error) {
-            return nb::none();
-          }
-          return nb::int_(val);
-        },
-        "Return the type value"_doc,
-        "type"_a)
-
-    .def("set",
-        &CoreAuxv::set,
-        "Set type value"_doc,
-        "type"_a, "value"_a)
-
-    .def("has",
-        &CoreAuxv::has,
-        "Check if a value is associated with the given type"_doc,
-        "type"_a)
+        [] (const CoreAuxv& self, CoreAuxv::TYPE type) {
+          return LIEF::py::value_or_none(&CoreAuxv::get, self, type);
+        }, "type"_a,
+        R"doc(
+        Get the auxv value from the provided type. Return `None` if
+        it is not present.
+        )doc"_doc
+    )
 
     .def("__getitem__",
-        &CoreAuxv::operator[],
-        nb::rv_policy::copy)
+        [] (const CoreAuxv& self, CoreAuxv::TYPE type) {
+          return LIEF::py::value_or_none(&CoreAuxv::get, self, type);
+        }
+    )
 
-    .def("__setitem__",
-        [] (CoreAuxv& status, AUX_TYPE atype, uint64_t val) {
-          status.set(atype, val);
-        })
+    .def("set", nb::overload_cast<CoreAuxv::TYPE, uint64_t>(&CoreAuxv::set),
+         "type"_a, "value"_a,
+         R"doc(
+         Change the value for the given type.
+         )doc")
+    .def("set", nb::overload_cast<std::map<CoreAuxv::TYPE, uint64_t>>(&CoreAuxv::set),
+         R"doc(
+         Replace **all** the values by the given dictionary.
+         )doc")
 
-    .def("__contains__", &CoreAuxv::has)
+    .def("__setitem__", nb::overload_cast<CoreAuxv::TYPE, uint64_t>(&CoreAuxv::set))
+    .def("__setitem__", nb::overload_cast<std::map<CoreAuxv::TYPE, uint64_t>>(&CoreAuxv::set))
 
     LIEF_DEFAULT_STR(CoreAuxv);
 }
diff --git a/api/python/src/ELF/objects/NoteDetails/core/pyCoreFile.cpp b/api/python/src/ELF/objects/NoteDetails/core/pyCoreFile.cpp
index 31e2fcccaa..af2ff2ed2e 100644
--- a/api/python/src/ELF/objects/NoteDetails/core/pyCoreFile.cpp
+++ b/api/python/src/ELF/objects/NoteDetails/core/pyCoreFile.cpp
@@ -28,10 +28,24 @@ namespace LIEF::ELF::py {
 
 template<>
 void create<CoreFile>(nb::module_& m) {
-
-  nb::class_<CoreFile, NoteDetails> cls(m, "CoreFile");
+  nb::class_<CoreFile, Note> cls(m, "CoreFile");
   nb::bind_vector<CoreFile::files_t>(cls, "files_t");
 
+  nb::class_<CoreFile::entry_t>(cls, "entry_t")
+    .def_rw("start", &CoreFile::entry_t::start,
+            "Start address of mapped file"_doc)
+
+    .def_rw("end", &CoreFile::entry_t::end,
+            "End address of mapped file"_doc)
+
+    .def_rw("file_ofs", &CoreFile::entry_t::file_ofs,
+             "Offset (in core) of mapped file"_doc)
+
+    .def_rw("path", &CoreFile::entry_t::path,
+            "Path of mapped file"_doc)
+
+    LIEF_DEFAULT_STR(CoreFile::entry_t);
+
   cls
     .def_prop_rw("files",
         nb::overload_cast<>(&CoreFile::files, nb::const_),
diff --git a/api/python/src/ELF/objects/NoteDetails/core/pyCoreFileEntry.cpp b/api/python/src/ELF/objects/NoteDetails/core/pyCoreFileEntry.cpp
deleted file mode 100644
index 4f355f4939..0000000000
--- a/api/python/src/ELF/objects/NoteDetails/core/pyCoreFileEntry.cpp
+++ /dev/null
@@ -1,45 +0,0 @@
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <string>
-#include <sstream>
-#include <nanobind/stl/string.h>
-
-#include "ELF/pyELF.hpp"
-#include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
-
-namespace LIEF::ELF::py {
-
-template<>
-void create<CoreFileEntry>(nb::module_& m) {
-
-  nb::class_<CoreFileEntry>(m, "CoreFileEntry")
-
-    .def_rw("start", &CoreFileEntry::start,
-      "Start address of mapped file"_doc)
-
-    .def_rw("end", &CoreFileEntry::end,
-      "End address of mapped file"_doc)
-
-    .def_rw("file_ofs", &CoreFileEntry::file_ofs,
-      "Offset (in core) of mapped file"_doc)
-
-    .def_rw("path", &CoreFileEntry::path,
-      "Path of mapped file"_doc)
-
-    LIEF_DEFAULT_STR(CoreFileEntry);
-}
-
-}
diff --git a/api/python/src/ELF/objects/NoteDetails/core/pyCorePrPsInfo.cpp b/api/python/src/ELF/objects/NoteDetails/core/pyCorePrPsInfo.cpp
index eaf682f1e8..dadf0b0f92 100644
--- a/api/python/src/ELF/objects/NoteDetails/core/pyCorePrPsInfo.cpp
+++ b/api/python/src/ELF/objects/NoteDetails/core/pyCorePrPsInfo.cpp
@@ -15,8 +15,10 @@
  */
 #include <string>
 #include <sstream>
+
 #include <nanobind/stl/string.h>
 
+#include "pyErr.hpp"
 #include "ELF/pyELF.hpp"
 
 #include "LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp"
@@ -25,49 +27,31 @@ namespace LIEF::ELF::py {
 
 template<>
 void create<CorePrPsInfo>(nb::module_& m) {
-
-  nb::class_<CorePrPsInfo, NoteDetails>(m, "CorePrPsInfo")
-
-    .def_prop_rw("file_name",
-        nb::overload_cast<>(&CorePrPsInfo::file_name, nb::const_),
-        nb::overload_cast<const std::string&>(&CorePrPsInfo::file_name),
-        "Process file name"_doc)
-
-    .def_prop_rw("flags",
-        nb::overload_cast<>(&CorePrPsInfo::flags, nb::const_),
-        nb::overload_cast<uint64_t>(&CorePrPsInfo::flags),
-        "Process flags"_doc)
-
-    .def_prop_rw("uid",
-        nb::overload_cast<>(&CorePrPsInfo::uid, nb::const_),
-        nb::overload_cast<uint32_t>(&CorePrPsInfo::uid),
-        "Process User ID"_doc)
-
-    .def_prop_rw("gid",
-        nb::overload_cast<>(&CorePrPsInfo::gid, nb::const_),
-        nb::overload_cast<uint32_t>(&CorePrPsInfo::gid),
-        "Process Group ID"_doc)
-
-    .def_prop_rw("pid",
-        nb::overload_cast<>(&CorePrPsInfo::pid, nb::const_),
-        nb::overload_cast<int32_t>(&CorePrPsInfo::pid),
-        "Process ID"_doc)
-
-    .def_prop_rw("ppid",
-        nb::overload_cast<>(&CorePrPsInfo::ppid, nb::const_),
-        nb::overload_cast<int32_t>(&CorePrPsInfo::ppid),
-        "Process parent ID"_doc)
-
-    .def_prop_rw("pgrp",
-        nb::overload_cast<>(&CorePrPsInfo::pgrp, nb::const_),
-        nb::overload_cast<int32_t>(&CorePrPsInfo::pgrp),
-        "Process session group ID"_doc)
-
-    .def_prop_rw("sid",
-        nb::overload_cast<>(&CorePrPsInfo::sid, nb::const_),
-        nb::overload_cast<int32_t>(&CorePrPsInfo::sid),
-        "Process session ID"_doc)
-
+  nb::class_<CorePrPsInfo, Note> cls(m, "CorePrPsInfo");
+  nb::class_<CorePrPsInfo::info_t>(cls, "info_t")
+    .def_rw("state", &CorePrPsInfo::info_t::state)
+    .def_rw("sname", &CorePrPsInfo::info_t::sname)
+    .def_rw("zombie", &CorePrPsInfo::info_t::zombie)
+    .def_rw("nice", &CorePrPsInfo::info_t::nice)
+    .def_rw("flag", &CorePrPsInfo::info_t::flag)
+    .def_rw("uid", &CorePrPsInfo::info_t::uid)
+    .def_rw("gid", &CorePrPsInfo::info_t::gid)
+    .def_rw("pid", &CorePrPsInfo::info_t::pid)
+    .def_rw("ppid", &CorePrPsInfo::info_t::ppid)
+    .def_rw("pgrp", &CorePrPsInfo::info_t::pgrp)
+    .def_rw("sid", &CorePrPsInfo::info_t::sid)
+    .def_rw("filename", &CorePrPsInfo::info_t::filename)
+    .def_rw("args", &CorePrPsInfo::info_t::args)
+    .def_prop_ro("filename_stripped", &CorePrPsInfo::info_t::filename_stripped)
+    .def_prop_ro("args_stripped", &CorePrPsInfo::info_t::args_stripped);
+
+  cls
+    .def_prop_rw("info",
+        [] (const CorePrPsInfo& self) {
+          return LIEF::py::value_or_none(nb::overload_cast<>(&CorePrPsInfo::info, nb::const_), self);
+        },
+        nb::overload_cast<const CorePrPsInfo::info_t&>(&CorePrPsInfo::info)
+    )
     LIEF_DEFAULT_STR(CorePrPsInfo);
 }
 
diff --git a/api/python/src/ELF/objects/NoteDetails/core/pyCorePrStatus.cpp b/api/python/src/ELF/objects/NoteDetails/core/pyCorePrStatus.cpp
index 7b2bc0b5a6..da5db779bf 100644
--- a/api/python/src/ELF/objects/NoteDetails/core/pyCorePrStatus.cpp
+++ b/api/python/src/ELF/objects/NoteDetails/core/pyCorePrStatus.cpp
@@ -17,235 +17,231 @@
 #include <sstream>
 #include <nanobind/stl/string.h>
 #include <nanobind/stl/map.h>
+#include <nanobind/stl/vector.h>
 
+#include "pyErr.hpp"
 #include "ELF/pyELF.hpp"
-
 #include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
 
-#include "LIEF/ELF/EnumToString.hpp"
 #include "enums_wrapper.hpp"
 
-#define PY_ENUM(x) LIEF::ELF::to_string(x), x
+#define GET_SET_REGISTER(ARCH)                                                                                                         \
+    .def("get", [] (const CorePrStatus& self, CorePrStatus::Registers::ARCH reg) {                                                     \
+          return LIEF::py::value_or_none(nb::overload_cast<CorePrStatus::Registers::ARCH>(&CorePrStatus::get, nb::const_), self, reg); \
+        }, "Get the register value or non if it is not present", "reg"_a)                                                              \
+    .def("__getitem__", [] (const CorePrStatus& self, CorePrStatus::Registers::ARCH reg) {                                             \
+          return LIEF::py::value_or_none(nb::overload_cast<CorePrStatus::Registers::ARCH>(&CorePrStatus::get, nb::const_), self, reg); \
+        })                                                                                                                             \
+    .def("set", nb::overload_cast<CorePrStatus::Registers::ARCH, uint64_t>(&CorePrStatus::set),                                        \
+         "Change the register value", "reg"_a, "value"_a)                                                                              \
+    .def("__setitem__", nb::overload_cast<CorePrStatus::Registers::ARCH, uint64_t>(&CorePrStatus::set))
 
 namespace LIEF::ELF::py {
 
 template<>
 void create<CorePrStatus>(nb::module_& m) {
+  nb::class_<CorePrStatus, Note> cls(m, "CorePrStatus");
 
-  nb::class_<CorePrStatus, NoteDetails> cls(m, "CorePrStatus");
-
-  nb::class_<CorePrStatus::timeval_t>(cls, "timeval")
+  nb::class_<CorePrStatus::timeval_t>(cls, "timeval_t")
     .def_rw("sec",  &CorePrStatus::timeval_t::sec)
     .def_rw("usec", &CorePrStatus::timeval_t::usec);
 
   nb::class_<CorePrStatus::siginfo_t>(cls, "siginfo_t")
-    .def_rw("sicode", &CorePrStatus::siginfo_t::si_code)
-    .def_rw("errno",  &CorePrStatus::siginfo_t::si_errno)
-    .def_rw("signo",  &CorePrStatus::siginfo_t::si_signo);
-
+    .def_rw("sicode", &CorePrStatus::siginfo_t::code)
+    .def_rw("errno",  &CorePrStatus::siginfo_t::err)
+    .def_rw("signo",  &CorePrStatus::siginfo_t::signo);
+
+  nb::class_<CorePrStatus::pr_status_t>(cls, "pr_status_t")
+    .def_rw("info",  &CorePrStatus::pr_status_t::info)
+    .def_rw("cursig",  &CorePrStatus::pr_status_t::cursig)
+    .def_rw("reserved",  &CorePrStatus::pr_status_t::reserved)
+    .def_rw("sigpend",  &CorePrStatus::pr_status_t::sigpend)
+    .def_rw("sighold",  &CorePrStatus::pr_status_t::sighold)
+    .def_rw("pid",  &CorePrStatus::pr_status_t::pid)
+    .def_rw("ppid",  &CorePrStatus::pr_status_t::ppid)
+    .def_rw("pgrp",  &CorePrStatus::pr_status_t::pgrp)
+    .def_rw("sid",  &CorePrStatus::pr_status_t::sid)
+    .def_rw("utime",  &CorePrStatus::pr_status_t::utime)
+    .def_rw("stime",  &CorePrStatus::pr_status_t::stime)
+    .def_rw("cutime",  &CorePrStatus::pr_status_t::cutime)
+    .def_rw("cstime",  &CorePrStatus::pr_status_t::cstime);
+
+  nb::class_<CorePrStatus::Registers> Registers(cls, "Registers");
   cls
-    .def_prop_rw("siginfo",
-        nb::overload_cast<>(&CorePrStatus::siginfo, nb::const_),
-        nb::overload_cast<const CorePrStatus::siginfo_t&>(&CorePrStatus::siginfo),
+    .def_prop_rw("status",
+        nb::overload_cast<>(&CorePrStatus::status, nb::const_),
+        nb::overload_cast<const CorePrStatus::pr_status_t&>(&CorePrStatus::status),
         "Info associated with the signal"_doc)
 
-    .def_prop_rw("current_sig",
-        nb::overload_cast<>(&CorePrStatus::current_sig, nb::const_),
-        nb::overload_cast<uint16_t>(&CorePrStatus::current_sig),
-        "Current Signal"_doc)
-
-    .def_prop_rw("sigpend",
-        nb::overload_cast<>(&CorePrStatus::sigpend, nb::const_),
-        nb::overload_cast<uint64_t>(&CorePrStatus::sigpend),
-        "Set of pending signals"_doc)
-
-    .def_prop_rw("sighold",
-        nb::overload_cast<>(&CorePrStatus::sighold, nb::const_),
-        nb::overload_cast<uint64_t>(&CorePrStatus::sighold),
-        "Set of held signals"_doc)
-
-    .def_prop_rw("pid",
-        nb::overload_cast<>(&CorePrStatus::pid, nb::const_),
-        nb::overload_cast<int32_t>(&CorePrStatus::pid),
-        "Process ID"_doc)
-
-    .def_prop_rw("ppid",
-        nb::overload_cast<>(&CorePrStatus::ppid, nb::const_),
-        nb::overload_cast<int32_t>(&CorePrStatus::ppid),
-        "Process parent ID"_doc)
-
-    .def_prop_rw("pgrp",
-        nb::overload_cast<>(&CorePrStatus::pgrp, nb::const_),
-        nb::overload_cast<int32_t>(&CorePrStatus::pgrp),
-        "Process group ID"_doc)
-
-    .def_prop_rw("sid",
-        nb::overload_cast<>(&CorePrStatus::sid, nb::const_),
-        nb::overload_cast<int32_t>(&CorePrStatus::sid),
-        "Process session ID"_doc)
-
-    .def_prop_rw("utime",
-        nb::overload_cast<>(&CorePrStatus::utime, nb::const_),
-        nb::overload_cast<CorePrStatus::timeval_t>(&CorePrStatus::utime),
-        "User time (" RST_CLASS_REF(lief.ELF.CorePrStatus.timeval) ")"_doc)
-
-    .def_prop_rw("utime",
-        nb::overload_cast<>(&CorePrStatus::utime, nb::const_),
-        nb::overload_cast<CorePrStatus::timeval_t>(&CorePrStatus::utime),
-        "User time (" RST_CLASS_REF(lief.ELF.CorePrStatus.timeval) ")"_doc)
-
-    .def_prop_rw("stime",
-        nb::overload_cast<>(&CorePrStatus::stime, nb::const_),
-        nb::overload_cast<CorePrStatus::timeval_t>(&CorePrStatus::stime),
-        "System time (" RST_CLASS_REF(lief.ELF.CorePrStatus.timeval) ")"_doc)
-
-    .def_prop_rw("cutime",
-        nb::overload_cast<>(&CorePrStatus::cutime, nb::const_),
-        nb::overload_cast<CorePrStatus::timeval_t>(&CorePrStatus::cutime),
-        "Cumulative user time (" RST_CLASS_REF(lief.ELF.CorePrStatus.timeval) ")"_doc)
-
-    .def_prop_rw("cstime",
-        nb::overload_cast<>(&CorePrStatus::cstime, nb::const_),
-        nb::overload_cast<CorePrStatus::timeval_t>(&CorePrStatus::cstime),
-        "Cumulative system time (" RST_CLASS_REF(lief.ELF.CorePrStatus.timeval) ")"_doc)
-
-    .def_prop_rw("register_context",
-        nb::overload_cast<>(&CorePrStatus::reg_context, nb::const_),
-        nb::overload_cast<const CorePrStatus::reg_context_t&>(&CorePrStatus::reg_context),
-        "Current registers state as a dictionary where the keys are "
-        RST_CLASS_REF(lief.ELF.CorePrStatus.REGISTERS) " and the values the register's value"_doc)
-
-    .def("get",
-        [] (const CorePrStatus& status, CorePrStatus::REGISTERS reg) -> nb::object {
-          bool error;
-          const uint64_t val = status.get(reg, &error);
-          if (error) {
-            return nb::none();
-          }
-          return nb::int_(val);
-        },
-        "Return the register value"_doc,
-        "register"_a)
-
-    .def("set",
-        &CorePrStatus::set,
-        "Set register value"_doc,
-        "register"_a, "value"_a)
+    .def_prop_ro("architecture", &CorePrStatus::architecture,
+        R"doc(Original target architecture.)doc"_doc)
 
-    .def("has",
-        &CorePrStatus::has,
-        "Check if a value is associated with the given register"_doc,
-        "register"_a)
+    .def_prop_ro("pc", [] (const CorePrStatus& self) {
+          return LIEF::py::value_or_none(&CorePrStatus::pc, self);
+        },
+        R"doc(
+        Return the program counter value (`rip`, `pc`, `eip` etc)
+        )doc"_doc)
 
-    .def("__getitem__",
-        &CorePrStatus::operator[],
-        nb::rv_policy::copy)
+    .def_prop_ro("sp", [] (const CorePrStatus& self) {
+          return LIEF::py::value_or_none(&CorePrStatus::sp, self);
+        },
+        R"doc(
+        Return the stack pointer value
+        )doc"_doc)
 
-    .def("__setitem__",
-        [] (CorePrStatus& status, CorePrStatus::REGISTERS reg, uint64_t val) {
-          status.set(reg, val);
-        })
+    .def_prop_ro("return_value", [] (const CorePrStatus& self) {
+          return LIEF::py::value_or_none(&CorePrStatus::return_value, self);
+        },
+        R"doc(
+        The value of the register that holds the return value according to
+        the calling convention.
+        )doc"_doc)
+
+    .def_prop_ro("register_values", &CorePrStatus::register_values,
+      R"doc(
+      List of the register values.
+      This list is **guarantee** to be as long as the number of registers defined
+      in the :class:`~.Registers` or empty if it can't be resolved.
+
+      Thus, one can access a specific register through:
+
+      .. code-block:: python
+
+        reg_vals: list[int] = note.register_values()
+        x20 = reg_vals[CorePrStatus.Registesr.AARCH64.X20.value]
+      )doc"_doc
+    )
+
+    GET_SET_REGISTER(X86)
+    GET_SET_REGISTER(X86_64)
+    GET_SET_REGISTER(ARM)
+    GET_SET_REGISTER(AARCH64)
+    LIEF_DEFAULT_STR(CorePrStatus);
 
-    .def("__contains__",
-        &CorePrStatus::has)
 
-    LIEF_DEFAULT_STR(CorePrStatus);
+  #define ENTRY(X) .value(to_string(CorePrStatus::Registers::X86::X), CorePrStatus::Registers::X86::X)
+  enum_<CorePrStatus::Registers::X86>(Registers, "X86",
+    R"doc(
+    Registers for the x86 architecture (:attr:`~.ARCH.i386`)
+    )doc"_doc)
+    ENTRY(EBX)
+    ENTRY(ECX)
+    ENTRY(EDX)
+    ENTRY(ESI)
+    ENTRY(EDI)
+    ENTRY(EBP)
+    ENTRY(EAX)
+    ENTRY(DS)
+    ENTRY(ES)
+    ENTRY(FS)
+    ENTRY(GS)
+    ENTRY(ORIG_EAX)
+    ENTRY(EIP)
+    ENTRY(CS)
+    ENTRY(EFLAGS)
+    ENTRY(ESP)
+    ENTRY(SS)
+  ;
+  #undef ENTRY
+
+  #define ENTRY(X) .value(to_string(CorePrStatus::Registers::X86_64::X), CorePrStatus::Registers::X86_64::X)
+  enum_<CorePrStatus::Registers::X86_64>(Registers, "X86_64",
+    R"doc(
+    Registers for the x86-64 architecture (:attr:`~.ARCH.x86_64`)
+    )doc"_doc)
+    ENTRY(R15)
+    ENTRY(R14)
+    ENTRY(R13)
+    ENTRY(R12)
+    ENTRY(RBP)
+    ENTRY(RBX)
+    ENTRY(R11)
+    ENTRY(R10)
+    ENTRY(R9)
+    ENTRY(R8)
+    ENTRY(RAX)
+    ENTRY(RCX)
+    ENTRY(RDX)
+    ENTRY(RSI)
+    ENTRY(RDI)
+    ENTRY(ORIG_RAX)
+    ENTRY(RIP)
+    ENTRY(CS)
+    ENTRY(EFLAGS)
+    ENTRY(RSP)
+    ENTRY(SS)
+  ;
+  #undef ENTRY
+
+  #define ENTRY(X) .value(to_string(CorePrStatus::Registers::ARM::X), CorePrStatus::Registers::ARM::X)
+  enum_<CorePrStatus::Registers::ARM>(Registers, "ARM",
+    R"doc(
+    Registers for the ARM architecture (:attr:`~.ARCH.ARM`)
+    )doc"_doc)
+    ENTRY(R0)
+    ENTRY(R1)
+    ENTRY(R2)
+    ENTRY(R3)
+    ENTRY(R4)
+    ENTRY(R5)
+    ENTRY(R6)
+    ENTRY(R7)
+    ENTRY(R8)
+    ENTRY(R9)
+    ENTRY(R10)
+    ENTRY(R11)
+    ENTRY(R12)
+    ENTRY(R13)
+    ENTRY(R14)
+    ENTRY(R15)
+    ENTRY(CPSR)
+  ;
+  #undef ENTRY
+
+  #define ENTRY(X) .value(to_string(CorePrStatus::Registers::AARCH64::X), CorePrStatus::Registers::AARCH64::X)
+  enum_<CorePrStatus::Registers::AARCH64>(Registers, "AARCH64",
+    R"doc(
+    Registers for the AARCH64 architecture (:attr:`~.ARCH.AARCH64`)
+    )doc"_doc)
+    ENTRY(X0)
+    ENTRY(X1)
+    ENTRY(X2)
+    ENTRY(X3)
+    ENTRY(X4)
+    ENTRY(X5)
+    ENTRY(X6)
+    ENTRY(X7)
+    ENTRY(X8)
+    ENTRY(X9)
+    ENTRY(X10)
+    ENTRY(X11)
+    ENTRY(X12)
+    ENTRY(X13)
+    ENTRY(X14)
+    ENTRY(X15)
+    ENTRY(X15)
+    ENTRY(X16)
+    ENTRY(X17)
+    ENTRY(X18)
+    ENTRY(X19)
+    ENTRY(X20)
+    ENTRY(X21)
+    ENTRY(X22)
+    ENTRY(X23)
+    ENTRY(X24)
+    ENTRY(X25)
+    ENTRY(X26)
+    ENTRY(X27)
+    ENTRY(X28)
+    ENTRY(X29)
+    ENTRY(X30)
+    ENTRY(X31)
+    ENTRY(PC)
+    ENTRY(PSTATE)
+  ;
+  #undef ENTRY
 
 
-  LIEF::enum_<CorePrStatus::REGISTERS>(cls, "REGISTERS")
-    .value(PY_ENUM(CorePrStatus::REGISTERS::UNKNOWN))
-
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_EBX))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_ECX))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_EDX))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_ESI))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_EDI))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_EBP))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_EAX))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_DS))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_ES))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_FS))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_GS))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86__))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_EIP))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_CS))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_EFLAGS))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_ESP))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_SS))
-
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_R15))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_R14))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_R13))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_R12))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RBP))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RBX))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_R11))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_R10))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_R9))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_R8))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RAX))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RCX))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RDX))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RSI))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RDI))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64__))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RIP))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_CS))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_EFLAGS))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_RSP))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::X86_64_SS))
-
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R0))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R1))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R2))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R3))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R4))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R5))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R6))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R7))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R8))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R9))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R10))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R11))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R12))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R13))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R14))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_R15))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::ARM_CPSR))
-
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X0))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X1))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X2))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X3))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X4))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X5))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X6))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X7))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X8))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X9))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X10))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X11))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X12))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X13))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X14))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X15))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X16))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X17))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X18))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X19))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X20))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X21))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X22))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X23))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X24))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X25))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X26))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X27))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X28))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X29))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X30))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_X31))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64_PC))
-    .value(PY_ENUM(CorePrStatus::REGISTERS::AARCH64__));
 }
 }
diff --git a/api/python/src/ELF/objects/NoteDetails/core/pyCoreSigInfo.cpp b/api/python/src/ELF/objects/NoteDetails/core/pyCoreSigInfo.cpp
index 7099214432..c2a451cbb2 100644
--- a/api/python/src/ELF/objects/NoteDetails/core/pyCoreSigInfo.cpp
+++ b/api/python/src/ELF/objects/NoteDetails/core/pyCoreSigInfo.cpp
@@ -17,6 +17,7 @@
 #include <sstream>
 #include <nanobind/stl/string.h>
 
+#include "pyErr.hpp"
 #include "ELF/pyELF.hpp"
 
 #include "LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp"
@@ -26,21 +27,26 @@ namespace LIEF::ELF::py {
 template<>
 void create<CoreSigInfo>(nb::module_& m) {
 
-  nb::class_<CoreSigInfo, NoteDetails>(m, "CoreSigInfo")
-
+  nb::class_<CoreSigInfo, Note>(m, "CoreSigInfo")
     .def_prop_rw("signo",
-        nb::overload_cast<>(&CoreSigInfo::signo, nb::const_),
-        nb::overload_cast<int32_t>(&CoreSigInfo::signo),
+        [] (const CoreSigInfo& self) {
+          return LIEF::py::value_or_none(nb::overload_cast<>(&CoreSigInfo::signo, nb::const_), self);
+        },
+        nb::overload_cast<uint32_t>(&CoreSigInfo::signo),
         "Signal number"_doc)
 
     .def_prop_rw("sigcode",
-        nb::overload_cast<>(&CoreSigInfo::sigcode, nb::const_),
-        nb::overload_cast<int32_t>(&CoreSigInfo::sigcode),
+        [] (const CoreSigInfo& self) {
+          return LIEF::py::value_or_none(nb::overload_cast<>(&CoreSigInfo::sigcode, nb::const_), self);
+        },
+        nb::overload_cast<uint32_t>(&CoreSigInfo::sigcode),
         "Signal code"_doc)
 
     .def_prop_rw("sigerrno",
-        nb::overload_cast<>(&CoreSigInfo::sigerrno, nb::const_),
-        nb::overload_cast<int32_t>(&CoreSigInfo::sigerrno),
+        [] (const CoreSigInfo& self) {
+          return LIEF::py::value_or_none(nb::overload_cast<>(&CoreSigInfo::sigerrno, nb::const_), self);
+        },
+        nb::overload_cast<uint32_t>(&CoreSigInfo::sigerrno),
         "If non-zero, an errno value associated with this signal"_doc)
 
     LIEF_DEFAULT_STR(CoreSigInfo);
diff --git a/api/python/src/ELF/objects/NoteDetails/pyAndroidNote.cpp b/api/python/src/ELF/objects/NoteDetails/pyAndroidIdent.cpp
similarity index 62%
rename from api/python/src/ELF/objects/NoteDetails/pyAndroidNote.cpp
rename to api/python/src/ELF/objects/NoteDetails/pyAndroidIdent.cpp
index de6f82c34c..d536112f05 100644
--- a/api/python/src/ELF/objects/NoteDetails/pyAndroidNote.cpp
+++ b/api/python/src/ELF/objects/NoteDetails/pyAndroidIdent.cpp
@@ -19,29 +19,29 @@
 
 #include "ELF/pyELF.hpp"
 
-#include "LIEF/ELF/NoteDetails/AndroidNote.hpp"
+#include "LIEF/ELF/NoteDetails/AndroidIdent.hpp"
 
 namespace LIEF::ELF::py {
 
 template<>
-void create<AndroidNote>(nb::module_& m) {
-  nb::class_<AndroidNote, NoteDetails>(m, "AndroidNote")
+void create<AndroidIdent>(nb::module_& m) {
+  nb::class_<AndroidIdent, Note>(m, "AndroidIdent")
     .def_prop_rw("sdk_version",
-        nb::overload_cast<>(&AndroidNote::sdk_version, nb::const_),
-        nb::overload_cast<uint32_t>(&AndroidNote::sdk_version),
+        nb::overload_cast<>(&AndroidIdent::sdk_version, nb::const_),
+        nb::overload_cast<uint32_t>(&AndroidIdent::sdk_version),
         "Target SDK platform"_doc)
 
     .def_prop_rw("ndk_version",
-        nb::overload_cast<>(&AndroidNote::ndk_version, nb::const_),
-        nb::overload_cast<const std::string&>(&AndroidNote::ndk_version),
+        nb::overload_cast<>(&AndroidIdent::ndk_version, nb::const_),
+        nb::overload_cast<const std::string&>(&AndroidIdent::ndk_version),
         "Android NDK version used to build the current binary"_doc)
 
     .def_prop_rw("ndk_build_number",
-        nb::overload_cast<>(&AndroidNote::ndk_build_number, nb::const_),
-        nb::overload_cast<const std::string&>(&AndroidNote::ndk_build_number),
+        nb::overload_cast<>(&AndroidIdent::ndk_build_number, nb::const_),
+        nb::overload_cast<const std::string&>(&AndroidIdent::ndk_build_number),
         "Android NDK build number"_doc)
 
-    LIEF_DEFAULT_STR(AndroidNote);
+    LIEF_DEFAULT_STR(AndroidIdent);
 }
 
 }
diff --git a/api/python/src/ELF/objects/NoteDetails/pyNoteAbi.cpp b/api/python/src/ELF/objects/NoteDetails/pyNoteAbi.cpp
index cc0106f6ca..5d0e85b2f8 100644
--- a/api/python/src/ELF/objects/NoteDetails/pyNoteAbi.cpp
+++ b/api/python/src/ELF/objects/NoteDetails/pyNoteAbi.cpp
@@ -21,19 +21,42 @@
 #include "ELF/pyELF.hpp"
 
 #include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
+#include "enums_wrapper.hpp"
+#include "pyErr.hpp"
 
 namespace LIEF::ELF::py {
 
 template<>
 void create<NoteAbi>(nb::module_& m) {
-  nb::class_<NoteAbi, NoteDetails>(m, "NoteAbi")
+  nb::class_<NoteAbi, Note> nabi(m, "NoteAbi",
+    R"doc(
+    Class that wraps the `NT_GNU_ABI_TAG` note
+    )doc"_doc
+  );
 
+  #define ENTRY(X) .value(to_string(NoteAbi::ABI::X), NoteAbi::ABI::X)
+  enum_<NoteAbi::ABI>(nabi, "ABI", "ABI recognized by this note"_doc)
+    ENTRY(LINUX)
+    ENTRY(GNU)
+    ENTRY(SOLARIS2)
+    ENTRY(FREEBSD)
+    ENTRY(NETBSD)
+    ENTRY(SYLLABLE)
+    ENTRY(NACL)
+  ;
+  #undef ENTRY
+
+  nabi
     .def_prop_ro("abi",
-        nb::overload_cast<>(&NoteAbi::abi, nb::const_),
-        "Return the target " RST_CLASS_REF(lief.ELF.NOTE_ABIS) ""_doc)
+        [] (const NoteAbi& self) {
+          return LIEF::py::value_or_none(nb::overload_cast<>(&NoteAbi::abi, nb::const_), self);
+        },
+        R"doc(Return the target :class:`~.ABI`)doc"_doc)
 
     .def_prop_ro("version",
-        nb::overload_cast<>(&NoteAbi::version, nb::const_),
+        [] (const NoteAbi& self) {
+          return LIEF::py::value_or_none(nb::overload_cast<>(&NoteAbi::version, nb::const_), self);
+        },
         "Return the target version as ``(Major, Minor, Patch)``"_doc)
 
     LIEF_DEFAULT_STR(NoteAbi);
diff --git a/api/python/src/ELF/objects/pyBinary.cpp b/api/python/src/ELF/objects/pyBinary.cpp
index 515ce380d3..a33018d990 100644
--- a/api/python/src/ELF/objects/pyBinary.cpp
+++ b/api/python/src/ELF/objects/pyBinary.cpp
@@ -344,10 +344,10 @@ void create<Binary>(nb::module_& m) {
         nb::rv_policy::reference_internal)
 
     .def("get",
-        nb::overload_cast<NOTE_TYPES>(&Binary::get),
+        nb::overload_cast<Note::TYPE>(&Binary::get),
         R"delim(
         Return the first binary's :class:`~lief.ELF.Note` from the given
-        :class:`~lief.ELF.NOTE_TYPES`.
+        :class:`~lief.ELF.Note.TYPE`.
 
         It returns None if the note can't be found.
         )delim"_doc,
@@ -379,8 +379,8 @@ void create<Binary>(nb::module_& m) {
         "type"_a)
 
     .def("has",
-        nb::overload_cast<NOTE_TYPES>(&Binary::has, nb::const_),
-        "Check if a " RST_CLASS_REF(lief.ELF.Note) " of *type* (" RST_CLASS_REF(lief.ELF.NOTE_TYPES) ") exists"_doc,
+        nb::overload_cast<Note::TYPE>(&Binary::has, nb::const_),
+        "Check if a " RST_CLASS_REF(lief.ELF.Note) " of *type* (" RST_CLASS_REF(lief.ELF.Note.TYPE) ") exists"_doc,
         "type"_a)
 
     .def("has",
@@ -516,8 +516,8 @@ void create<Binary>(nb::module_& m) {
         "note"_a)
 
     .def("remove",
-        nb::overload_cast<NOTE_TYPES>(&Binary::remove),
-        "Remove **all** the " RST_CLASS_REF(lief.ELF.Note) " with the given " RST_CLASS_REF(lief.ELF.NOTE_TYPES) ""_doc,
+        nb::overload_cast<Note::TYPE>(&Binary::remove),
+        "Remove **all** the " RST_CLASS_REF(lief.ELF.Note) " with the given " RST_CLASS_REF(lief.ELF.Note.TYPE) ""_doc,
         "type"_a)
 
     .def_prop_ro("has_notes",
@@ -728,20 +728,24 @@ void create<Binary>(nb::module_& m) {
     .def(nb::self += Segment(), nb::rv_policy::reference_internal)
     .def(nb::self += Section(), nb::rv_policy::reference_internal)
     .def(nb::self += DynamicEntry(), nb::rv_policy::reference_internal)
-    .def(nb::self += Note(), nb::rv_policy::reference_internal)
-
+    .def("__iadd__", [] (Binary& self, const Note& note) {
+        self += note;
+        return &self;
+    }, nb::rv_policy::reference_internal)
     .def(nb::self -= DynamicEntry(), nb::rv_policy::reference_internal)
     .def(nb::self -= DYNAMIC_TAGS(), nb::rv_policy::reference_internal)
-
-    .def(nb::self -= Note(), nb::rv_policy::reference_internal)
-    .def(nb::self -= NOTE_TYPES(), nb::rv_policy::reference_internal)
+    .def("__isub__", [] (Binary& self, const Note& note) {
+        self -= note;
+        return &self;
+    }, nb::rv_policy::reference_internal)
+    .def(nb::self -= Note::TYPE(), nb::rv_policy::reference_internal)
 
     .def("__getitem__",
         nb::overload_cast<SEGMENT_TYPES>(&Binary::operator[]),
         nb::rv_policy::reference_internal)
 
     .def("__getitem__",
-        nb::overload_cast<NOTE_TYPES>(&Binary::operator[]),
+        nb::overload_cast<Note::TYPE>(&Binary::operator[]),
         nb::rv_policy::reference_internal)
 
     .def("__getitem__",
@@ -761,8 +765,8 @@ void create<Binary>(nb::module_& m) {
         "Check if the " RST_CLASS_REF(lief.ELF.DynamicEntry) " associated with the given " RST_CLASS_REF(lief.ELF.DYNAMIC_TAGS) " exists"_doc)
 
     .def("__contains__",
-        nb::overload_cast<NOTE_TYPES>(&Binary::has, nb::const_),
-        "Check if the " RST_CLASS_REF(lief.ELF.Note) " associated with the given " RST_CLASS_REF(lief.ELF.NOTE_TYPES) " exists"_doc)
+        nb::overload_cast<Note::TYPE>(&Binary::has, nb::const_),
+        "Check if the " RST_CLASS_REF(lief.ELF.Note) " associated with the given " RST_CLASS_REF(lief.ELF.Note.TYPE) " exists"_doc)
 
     .def("__contains__",
         nb::overload_cast<ELF_SECTION_TYPES>(&Binary::has, nb::const_),
diff --git a/api/python/src/ELF/objects/pyNote.cpp b/api/python/src/ELF/objects/pyNote.cpp
index e9a4907ce7..d83ca0e3e8 100644
--- a/api/python/src/ELF/objects/pyNote.cpp
+++ b/api/python/src/ELF/objects/pyNote.cpp
@@ -17,68 +17,148 @@
 #include <sstream>
 #include <nanobind/stl/string.h>
 #include <nanobind/stl/vector.h>
+#include <nanobind/stl/unique_ptr.h>
+#include "nanobind/extra/memoryview.hpp"
+#include "nanobind/utils.hpp"
 
 #include "ELF/pyELF.hpp"
 
 #include "LIEF/ELF/Note.hpp"
 #include "LIEF/ELF/NoteDetails.hpp"
 
+#include "enums_wrapper.hpp"
+
 namespace LIEF::ELF::py {
 
 template<>
 void create<Note>(nb::module_& m) {
-  nb::class_<Note, LIEF::Object>(m, "Note",
+  nb::class_<Note, Object> note(m, "Note",
       R"delim(
       Class which represents an ELF note.
-      )delim"_doc)
-
-    .def(nb::init<>(),
-        "Default constructor")
-
-    .def(nb::init<const std::string&, NOTE_TYPES, const std::vector<uint8_t>&>(),
-        "Constructor from a ``name``, ``type`` and ``description``"_doc,
-        "name"_a, "type"_a, "description"_a)
-
-    .def_prop_ro("details",
-        nb::overload_cast<>(&Note::details),
-        "Parse the given note description and return a " RST_CLASS_REF(lief.ELF.NoteDetails) " object"_doc,
-        nb::rv_policy::reference_internal)
+      )delim"_doc);
+
+  #define ENTRY(X) .value(to_string(Note::TYPE::X), Note::TYPE::X)
+  enum_<Note::TYPE>(note, "TYPE", "LIEF representation of the ELF `NT_` values.")
+    ENTRY(UNKNOWN)
+    ENTRY(GNU_ABI_TAG)
+    ENTRY(GNU_HWCAP)
+    ENTRY(GNU_ABI_TAG)
+    ENTRY(GNU_HWCAP)
+    ENTRY(GNU_BUILD_ID)
+    ENTRY(GNU_GOLD_VERSION)
+    ENTRY(GNU_PROPERTY_TYPE_0)
+    ENTRY(GNU_BUILD_ATTRIBUTE_OPEN)
+    ENTRY(GNU_BUILD_ATTRIBUTE_FUNC)
+    ENTRY(CRASHPAD)
+    ENTRY(CORE_PRSTATUS)
+    ENTRY(CORE_FPREGSET)
+    ENTRY(CORE_PRPSINFO)
+    ENTRY(CORE_TASKSTRUCT)
+    ENTRY(CORE_AUXV)
+    ENTRY(CORE_PSTATUS)
+    ENTRY(CORE_FPREGS)
+    ENTRY(CORE_PSINFO)
+    ENTRY(CORE_LWPSTATUS)
+    ENTRY(CORE_LWPSINFO)
+    ENTRY(CORE_WIN32PSTATUS)
+    ENTRY(CORE_FILE)
+    ENTRY(CORE_PRXFPREG)
+    ENTRY(CORE_SIGINFO)
+    ENTRY(CORE_ARM_VFP)
+    ENTRY(CORE_ARM_TLS)
+    ENTRY(CORE_ARM_HW_BREAK)
+    ENTRY(CORE_ARM_HW_WATCH)
+    ENTRY(CORE_ARM_SYSTEM_CALL)
+    ENTRY(CORE_ARM_SVE)
+    ENTRY(CORE_ARM_PAC_MASK)
+    ENTRY(CORE_ARM_PACA_KEYS)
+    ENTRY(CORE_ARM_PACG_KEYS)
+    ENTRY(CORE_TAGGED_ADDR_CTRL)
+    ENTRY(CORE_PAC_ENABLED_KEYS)
+    ENTRY(CORE_X86_TLS)
+    ENTRY(CORE_X86_IOPERM)
+    ENTRY(CORE_X86_XSTATE)
+    ENTRY(CORE_X86_CET)
+    ENTRY(ANDROID_MEMTAG)
+    ENTRY(ANDROID_KUSER)
+    ENTRY(ANDROID_IDENT)
+    ENTRY(STAPSDT)
+    ENTRY(GO_BUILDID)
+  ;
+  #undef ENTRY
+
+  const auto create_overload_0 = nb::overload_cast<const std::string&, uint32_t, Note::description_t, E_TYPE, ARCH, ELF_CLASS>(&Note::create);
+  const auto create_overload_1 = nb::overload_cast<const std::string&, Note::TYPE, Note::description_t, ARCH, ELF_CLASS>(&Note::create);
+  note
+    .def_static("create", create_overload_0,
+      R"doc(
+      Create a note from the owner name, the original type (`NT_xxx` value)
+      and the description.
+
+      Depending on the note, the filetype, the architecture and the ELF class might be needed.
+      )doc"_doc,
+      "name"_a, "original_type"_a, "description"_a,
+      "file_type"_a = E_TYPE::ET_NONE, "arch"_a = ARCH::EM_NONE, "cls"_a = ELF_CLASS::ELFCLASSNONE)
+
+    .def_static("create",
+      [] (nb::bytes bytes, E_TYPE ftype, ARCH arch, ELF_CLASS cls) -> std::unique_ptr<Note> {
+        std::unique_ptr<LIEF::SpanStream> stream = to_stream(bytes);
+        if (!stream) {
+          return nullptr;
+        }
+        return Note::create(*stream, ftype, arch, cls);
+      },
+      R"doc(
+      Create a note from the given `bytes` buffer.
+
+      Depending on the note, the filetype, the architecture and the ELF class might
+      be needed.
+      )doc"_doc,
+      "raw"_a,
+      "file_type"_a = E_TYPE::ET_NONE, "arch"_a = ARCH::EM_NONE,
+      "cls"_a = ELF_CLASS::ELFCLASSNONE)
+
+    .def_static("create", create_overload_1,
+      R"doc(
+      Create the owner name, the type and the description
+
+      Depending on the note, the filetype, the architecture and the ELF class might
+      be needed.
+      )doc"_doc,
+      "name"_a, "type"_a, "description"_a,
+      "arch"_a = ARCH::EM_NONE, "cls"_a = ELF_CLASS::ELFCLASSNONE)
 
     .def_prop_rw("name",
         nb::overload_cast<>(&Note::name, nb::const_),
-        nb::overload_cast<const std::string&>(&Note::name),
-        "Return the *name* of the note (Usually the owner)."_doc)
+        nb::overload_cast<std::string>(&Note::name),
+        "Return the *name* of the note also known as the owner."_doc)
 
-    .def_prop_rw("type",
-        nb::overload_cast<>(&Note::type, nb::const_),
-        nb::overload_cast<NOTE_TYPES>(&Note::type),
-        "Return the type of the note. It can be one of the " RST_CLASS_REF(lief.ELF.NOTE_TYPES) " values"_doc)
+    .def_prop_ro("original_type",
+        nb::overload_cast<>(&Note::original_type, nb::const_),
+        R"doc(
+        Return the original `NT_` value of the note.
 
-    .def_prop_rw("type_core",
-        nb::overload_cast<>(&Note::type_core, nb::const_),
-        nb::overload_cast<NOTE_TYPES_CORE>(&Note::type_core),
-        "Return the type of the note for ELF Core (ET_CORE). It Can be one of the " RST_CLASS_REF(lief.ELF.NOTE_TYPES_CORE) " values"_doc)
+        This value should be interpreted according the the :attr:`~.name` of the
+        note.
+        )doc"_doc)
+
+    .def_prop_ro("type",
+        nb::overload_cast<>(&Note::type, nb::const_),
+        R"doc(
+        Return the LIEF type representation of the note.
+        )doc"_doc)
 
     .def_prop_rw("description",
-        nb::overload_cast<>(&Note::description, nb::const_),
-        nb::overload_cast<const Note::description_t&>(&Note::description),
+        [] (const Note& self) {
+          const std::vector<uint8_t>& content = self.description();
+          return nb::memoryview::from_memory(content.data(), content.size());
+        },
+        nb::overload_cast<Note::description_t>(&Note::description),
         "Return the description associated with the note"_doc)
 
-    .def_prop_ro("is_core",
-        &Note::is_core,
-        "True if the note is associated with a coredump"_doc)
-
-    .def_prop_ro("is_android",
-        &Note::is_android,
-        R"delim(
-        True if the current note is specific to Android.
-
-        If true, :attr:`lief.Note.details` returns a reference to the :class:`~lief.ELF.AndroidNote` object
-        )delim"_doc)
-
-    .def_prop_ro("size", &Note::size,
-        "Size of the **raw** note"_doc)
+    .def_prop_ro("size", &Note::size, "Size of the **raw** note"_doc)
 
+    LIEF_CLONABLE(Note)
     LIEF_DEFAULT_STR(Note);
 }
 
diff --git a/api/python/src/ELF/objects/pyNoteDetails.cpp b/api/python/src/ELF/objects/pyNoteDetails.cpp
deleted file mode 100644
index 4553e65437..0000000000
--- a/api/python/src/ELF/objects/pyNoteDetails.cpp
+++ /dev/null
@@ -1,34 +0,0 @@
-
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <string>
-#include <sstream>
-#include <nanobind/stl/string.h>
-
-#include "ELF/pyELF.hpp"
-
-#include "LIEF/ELF/NoteDetails.hpp"
-
-namespace LIEF::ELF::py {
-
-template<>
-void create<NoteDetails>(nb::module_& m) {
-  nb::class_<NoteDetails, LIEF::Object>(m, "NoteDetails")
-    LIEF_DEFAULT_STR(NoteDetails);
-}
-
-}
-
diff --git a/api/python/src/nanobind/utils.hpp b/api/python/src/nanobind/utils.hpp
new file mode 100644
index 0000000000..6e32a5d0c9
--- /dev/null
+++ b/api/python/src/nanobind/utils.hpp
@@ -0,0 +1,24 @@
+#ifndef PY_LIEF_NB_UTILS_H
+#define PY_LIEF_NB_UTILS_H
+
+#include <nanobind/nanobind.h>
+#include <LIEF/BinaryStream/SpanStream.hpp>
+#include <vector>
+#include <memory.h>
+
+NAMESPACE_BEGIN(NB_NAMESPACE)
+
+inline std::vector<uint8_t> to_vector(nanobind::bytes bytes) {
+  const auto* ptr = reinterpret_cast<const uint8_t*>(bytes.c_str());
+  return {ptr, ptr + bytes.size()};
+}
+
+
+inline std::unique_ptr<LIEF::SpanStream> to_stream(nanobind::bytes bytes) {
+  const auto* ptr = reinterpret_cast<const uint8_t*>(bytes.c_str());
+  return std::make_unique<LIEF::SpanStream>(ptr, bytes.size());
+}
+
+NAMESPACE_END(NB_NAMESPACE)
+
+#endif
diff --git a/api/python/src/pyErr.cpp b/api/python/src/pyErr.cpp
index 86377f57c2..7f363e9dbb 100644
--- a/api/python/src/pyErr.cpp
+++ b/api/python/src/pyErr.cpp
@@ -10,6 +10,17 @@ void init_errors(nb::module_& m) {
       )delim")
     .def("__bool__", [] (const ok_t&) { return true; });
 
+  nb::class_<ok_error_t>(m, "ok_error_t",
+      R"delim(
+      Return either: :class:`~.ok_t` (success) or :class:`~.lief_errors` (error)
+      )delim")
+    .def_prop_ro("is_error", [] (const ok_error_t& val) { return !val.has_value(); })
+    .def_prop_ro("is_value", [] (const ok_error_t& val) { return val.has_value(); })
+    .def_prop_ro("error", [] (const ok_error_t& val) { return val.error(); })
+    .def_prop_ro("value", [] (const ok_error_t& val) { return val.value(); })
+    .def("__bool__", [] (const ok_error_t& val) { return val.has_value(); });
+
+
   nb::enum_<lief_errors>(m, "lief_errors", R"delim(
   Enum class which represents an error generated by LIEF's functions
   )delim")
diff --git a/api/python/src/pyErr.hpp b/api/python/src/pyErr.hpp
index a8f9df528b..e3fe045e18 100644
--- a/api/python/src/pyErr.hpp
+++ b/api/python/src/pyErr.hpp
@@ -22,7 +22,6 @@
 #include "typing.hpp"
 
 namespace LIEF::py {
-
 template<class RetTy>
 struct typing_error : public nanobind::object {
   using value_type = typename RetTy::value_type;
@@ -41,9 +40,23 @@ struct typing_error : public nanobind::object {
     return true;
   }
 };
-}
 
-namespace LIEF::py {
+template<class RetTy>
+struct value_or_none_t : public nanobind::object {
+  LIEF_PY_DEFAULT_CTOR(value_or_none_t, nanobind::object);
+  using value_type = typename RetTy::value_type;
+
+  static constexpr auto Name = nanobind::detail::const_name("Optional[") +
+                 nanobind::detail::make_caster<value_type>::Name +
+      nanobind::detail::const_name("]");
+
+  NB_OBJECT_DEFAULT_NONAME(value_or_none_t, object, check)
+  static bool check(handle h) {
+    return true;
+  }
+};
+
+
 template <class Func, typename... Ts,
           class RetTy = std::invoke_result_t<Func, Ts...>,
           std::enable_if_t<!std::is_member_pointer<std::decay_t<Func>>{}, int> = 0>
@@ -68,6 +81,26 @@ typing_error<RetTy> error_or(Func f, Ts&&... args) {
   return nb::cast(ret.value());
 }
 
+
+template <class Func, typename... Ts,
+          class RetTy = std::invoke_result_t<Func, Ts...> >
+value_or_none_t<RetTy> value_or_none(Func f, Ts&&... args) {
+  namespace nb = nanobind;
+  if constexpr (std::is_member_pointer_v<std::decay_t<Func>>) {
+    auto&& ret = std::mem_fn(f)(std::forward<Ts>(args)...);
+    if (!ret) {
+      return nb::none();
+    }
+    return nb::cast(*ret);
+  } else {
+    auto&& ret = f(std::forward<Ts>(args)...);
+    if (!ret) {
+      return nb::none();
+    }
+    return nb::cast(*ret);
+  }
+}
+
 void init_errors(nanobind::module_&);
 }
 
diff --git a/doc/sphinx/api/cpp/elf.rst b/doc/sphinx/api/cpp/elf.rst
index 9b97f0484f..e3873c97c4 100644
--- a/doc/sphinx/api/cpp/elf.rst
+++ b/doc/sphinx/api/cpp/elf.rst
@@ -182,14 +182,6 @@ Note
 ----------
 
 
-Note Details
-************
-
-.. doxygenclass:: LIEF::ELF::NoteDetails
-   :project: lief
-
-----------
-
 Core PrPsInfo
 *************
 
@@ -207,15 +199,6 @@ Core File
 
 ----------
 
-
-Core File Entry
-***************
-
-.. doxygenstruct:: LIEF::ELF::CoreFileEntry
-   :project: lief
-
-----------
-
 Core PrStatus
 *************
 
@@ -434,33 +417,6 @@ Dynamic symbols counting
 ----------
 
 
-Note types
-~~~~~~~~~~
-
-.. doxygenenum:: LIEF::ELF::NOTE_TYPES
-   :project: lief
-
-----------
-
-Note Core types
-~~~~~~~~~~~~~~~
-
-.. doxygenenum:: LIEF::ELF::NOTE_TYPES_CORE
-   :project: lief
-
-----------
-
-
-Note ABIs
-~~~~~~~~~
-
-.. doxygenenum:: LIEF::ELF::NOTE_ABIS
-   :project: lief
-
-
-----------
-
-
 Relocation purpose
 ~~~~~~~~~~~~~~~~~~
 
diff --git a/doc/sphinx/api/python/elf.rst b/doc/sphinx/api/python/elf.rst
index 0114495fce..8693de404a 100644
--- a/doc/sphinx/api/python/elf.rst
+++ b/doc/sphinx/api/python/elf.rst
@@ -158,13 +158,6 @@ Note
 
 ----------
 
-Note Details
-************
-
-.. autoclass:: lief.ELF.NoteDetails
-
-----------
-
 Core PrPsInfo
 *************
 
@@ -184,12 +177,6 @@ Core File
 
 .. autoclass:: lief.ELF.CoreFile
 
-----------
-
-Core File Entry
-***************
-
-.. autoclass:: lief.ELF.CoreFileEntry
 
 ----------
 
@@ -208,10 +195,10 @@ Core Auxiliary Vector
 
 ----------
 
-Android Note
+Android Ident
 *************
 
-.. autoclass:: lief.ELF.AndroidNote
+.. autoclass:: lief.ELF.AndroidIdent
 
 ----------
 
@@ -429,27 +416,6 @@ Relocation purpose
 
 ----------
 
-Note types
-~~~~~~~~~~
-
-.. autoclass:: lief.ELF.NOTE_TYPES
-
-----------
-
-Note ABIs
-~~~~~~~~~
-
-.. autoclass:: lief.ELF.NOTE_ABIS
-
-----------
-
-
-Note Core types
-~~~~~~~~~~~~~~~
-
-.. autoclass:: lief.ELF.NOTE_TYPES_CORE
-
-----------
 
 ARM Processor flags
 ~~~~~~~~~~~~~~~~~~~
diff --git a/doc/sphinx/api/python/index.rst b/doc/sphinx/api/python/index.rst
index 9b8c46a6bf..3205257287 100644
--- a/doc/sphinx/api/python/index.rst
+++ b/doc/sphinx/api/python/index.rst
@@ -55,6 +55,8 @@ Error Handling
 
 .. autoclass:: lief.ok_t
 
+.. autoclass:: lief.ok_error_t
+
 See also the section :ref:`err_handling`
 
 Exceptions
diff --git a/doc/sphinx/changelog.rst b/doc/sphinx/changelog.rst
index faa67be317..fdb80b2215 100644
--- a/doc/sphinx/changelog.rst
+++ b/doc/sphinx/changelog.rst
@@ -6,6 +6,7 @@ Changelog
 
 :ELF:
 
+  * Refactoring of the ELF note processing
   * Fix relocation issue when using `-Wl,--emit-relocs` (c.f. :issue:`897` / :pr:`898` by :github_user:`adamjseitz`)
   * Improve the computation of the dynamic symbols thanks to :github_user:`adamjseitz` (c.f. :issue:`922`)
   * Add support for the LoongArch architecture thanks to :github_user:`loongson-zn` (c.f. :pr:`921`)
diff --git a/doc/sphinx/conf.py b/doc/sphinx/conf.py
index 8ddf09428b..cfb263f940 100644
--- a/doc/sphinx/conf.py
+++ b/doc/sphinx/conf.py
@@ -1,13 +1,22 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
-import re
+import inspect
 import lief
 import os
 import pathlib
-import inspect
-from pathlib import Path
+import re
+import sphinx
+import sys
 
+from datetime import date
 from docutils import nodes
+from docutils.nodes import Node
+from pathlib import Path
+from sphinx.ext.inheritance_diagram import InheritanceDiagram
+from sphinx.util import logging
+from sphinx.util.typing import OptionSpec
+from typing import Any
+
 from sphinx.util import logging
 from sphinx.util.inspect import (
     getdoc,
@@ -51,6 +60,7 @@
 extensions = [
     'sphinx.ext.mathjax',
     'sphinx.ext.autodoc',
+    'sphinx.ext.inheritance_diagram',
 ]
 
 
@@ -293,6 +303,38 @@ def _autodoc_process_docstring(app, what, name, obj, options, lines: list[str]):
         lines[:] = []
     return
 
+
+class LIEFInheritanceDiagram(InheritanceDiagram):
+    option_spec: OptionSpec = {
+        **InheritanceDiagram.option_spec,
+        'depth': int
+    }
+    def _check_module(self, module, obj, depth: int):
+        if not module.__name__.startswith("lief"):
+            return []
+
+        classes = []
+        for name, member in inspect.getmembers(module):
+            if inspect.isclass(member) and issubclass(member, obj):
+                mro = inspect.getmro(member)
+                if (0 < depth and mro.index(obj) <= depth) or depth == 0:
+                    classes.append(f"{module.__name__}.{name}")
+            if inspect.ismodule(member) and member != module:
+                classes.extend(self._check_module(member, obj, depth))
+        return list(set(classes))
+
+    def run(self) -> list[Node]:
+        cls = None
+        classes = []
+        depth = self.options.get('depth', 0)
+        if self.arguments[0].startswith("lief._lief"):
+            elements = self.arguments[0].split(".")[2:]
+            cls = lief._lief
+            for element in elements:
+                cls = getattr(cls, element)
+            self.arguments[0] = " ".join(self._check_module(lief, cls, depth))
+        return super().run()
+
 def setup(app):
     app.add_css_file('css/custom.css')  # may also be an URL
 
@@ -300,6 +342,7 @@ def setup(app):
     app.add_role('pr', pr_role)
     app.add_role('issue', issue_role)
     app.add_role('github_user', github_user)
+    app.add_directive('lief-inheritance', LIEFInheritanceDiagram)
 
     app.connect('autodoc-process-signature', _on_process_signature)
     app.connect('autodoc-process-docstring', _autodoc_process_docstring)
diff --git a/doc/sphinx/tutorials/12_elf_coredump.rst b/doc/sphinx/tutorials/12_elf_coredump.rst
index 4c10212343..34389d75d7 100644
--- a/doc/sphinx/tutorials/12_elf_coredump.rst
+++ b/doc/sphinx/tutorials/12_elf_coredump.rst
@@ -15,7 +15,7 @@ has been generated. The memory state embeds
 a *snapshot* of all segments mapped in the memory space of the program. The CPU state contains register values
 when the core dump has been generated.
 
-Coredump files use a subset of the ELF structures to register these information. **Segments** are used for
+Coredump files use a subset of the ELF structures to store this information. **Segments** are used for
 the memory state of the process while ELF notes (:class:`lief.ELF.Note`) are used for process metadata (pid, signal, ...)
 Especially, the CPU state is stored in a note with a special type.
 
@@ -31,7 +31,7 @@ For more details about coredump internal structure, one can look at the followin
 Coredump Analysis
 ~~~~~~~~~~~~~~~~~
 
-As corefiles are effectively ELF, we can open these files using the :func:`lief.parse` function:
+As core files are effectively ELF, we can open these files using the :func:`lief.parse` function:
 
 .. code-block:: python
 
@@ -53,34 +53,38 @@ To resolve the relationship between libraries and segments, we can look at the s
 
 .. code-block:: python
 
-   note_file = [n for n in core.notes if n.type_core == lief.ELF.NOTE_TYPES_CORE.FILE]
-   assert len(note_file) == 1
+   nt_core_file = core.get(lief.ELF.Note.TYPE.CORE.FILE)
 
-   note_file = note_file.pop()
+ELF notes are represented through the main :class:`lief.ELF.Note` interface. Some notes
+like (:class:`lief.ELF.CoreFile`) can expose additional API by extending the
+original :class:`lief.ELF.Note`.
 
-.. warning::
+.. lief-inheritance:: lief._lief.ELF.Note
+    :top-classes: lief._lief.ELF.Note
+    :depth: 1
+    :parts: 1
 
-   Due to enum conflict between :class:`lief.ELF.NOTE_TYPES` and :class:`lief.ELF.NOTE_TYPES_CORE`,
-   scripts must use :attr:`lief.ELF.Note.type_core` on ELF corefile instead of :attr:`lief.ELF.Note.type`
+.. note::
 
-The ``note_file`` variable is basically an object :class:`lief.ELF.Note` on which the attribute
-:attr:`lief.ELF.Note.details` can be access to have the underlying specialization of the note.
-In the case of a note type :attr:`lief.ELF.NOTE_TYPES_CORE.FILE`, the attribute :attr:`~lief.ELF.Note.details` returns an
-instance of :class:`lief.ELF.CoreFile`.
+    All note details inherit from the base class :class:`lief.ELF.Note` (or :cpp:class:`LIEF::ELF::Note`)
 
-.. note::
+    Especially, in C++ we must downcast according to the `classof` function:
 
-   All note details inherit from the base class :class:`lief.ELF.NoteDetails` (or :cpp:class:`LIEF::ELF::NoteDetails`)
+    .. code-block:: cpp
 
-   Especially, in C++ we must downcast the reference returned by :cpp:func:`LIEF::ELF::Note::details`:
+      for (const Note& note : binary->notes()) {
+        if (CoreFile::classof(&note)) {
+          const auto& nt_core_file = static_cast<const CoreFile&(note);
+        }
+      }
 
-   .. code-block:: cpp
+    Which is roughly equivalent in Python to:
 
-      note = ...
-      // Check Type
-      // ...
-      const auto& note_file = reinterpret_cast<const CoreFile&>(note.details());
+    .. code-block:: python
 
+      for note in binary.notes:
+          if isinstance(note, lief.ELF.CoreFile):
+              print("This is a CoreFile note")
 
 
 We can eventually use the attribute :attr:`lief.ELF.CoreFile.files` or directly iterate on
@@ -119,20 +123,18 @@ the :class:`lief.ELF.CoreFile` object. Both give access to the :class:`lief.ELF.
 From this output, we can see that the :class:`~lief.ELF.Segment` of the main executable
 (``/data/local/tmp/hello-exe``), are mapped from address ``0x5580b86000`` to address ``0x5580b99000``.
 
-One can also access to the registers state by using the note for which the :attr:`~lief.ELF.Note.type_core`
-is :class:`lief.ELF.CorePrStatus`.
+One can also access to the registers state by looking for the note: :class:`lief.ELF.CorePrStatus`.
 
 .. code-block:: python
 
    for note in core.notes:
-      if note.type_core == lief.ELF.NOTE_TYPES_CORE.PRSTATUS:
-         details = note.details
-         print(details)
+      if not isinstance(note, lief.ELF.CorePrStatus):
+          continue
 
-         # Print instruction pointer
-         print(hex(details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_PC]))
-         # or
-         print(hex(details.get(lief.ELF.CorePrStatus.REGISTERS.AARCH64_PC)))
+      # Both are equivalent
+      print(note.pc)
+      reg_values = note.register_values
+      print(reg_values[lief.ELF.CorePrStatus.Registers.AARCH64.PC.value])
 
 .. code-block:: text
 
@@ -148,8 +150,8 @@ we can update the register values as follows:
 
 .. code-block:: python
 
-   note_prstatus = [n for n in core.notes if n.type_core == lief.ELF.NOTE_TYPES_CORE.PRSTATUS][0]
-   note_prstatus.details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_PC] = 0xDEADC0DE
+   prstatus = elf_core.get(lief.ELF.Note.TYPE.CORE_PRSTATUS)
+   prstatus.set(lief.ELF.CorePrStatus.Registers.AARCH64.PC, 0xDEADC0DE)
 
    core.write("/tmp/new.core")
 
@@ -166,8 +168,8 @@ is that **relocations** and **dependencies** are resolved inside the coredump.
 
 This API could be used in association with other tools. For instance, we could use `Triton <https://triton.quarkslab.com/>`_ API:
 
-- `AArch64Cpu::setConcreteRegisterValue() <https://triton.quarkslab.com/documentation/doxygen/classtriton_1_1arch_1_1arm_1_1aarch64_1_1AArch64Cpu.html>`_
-- `AArch64Cpu::setConcreteMemoryAreaValue() <https://triton.quarkslab.com/documentation/doxygen/classtriton_1_1arch_1_1arm_1_1aarch64_1_1AArch64Cpu.html>`_
+- `AArch64Cpu::setConcreteRegisterValue() <https://github.com/JonathanSalwan/Triton/blob/a61651ce331ac53ec09e1d8fef5eab744e98c9de/src/libtriton/arch/architecture.cpp#L343>`_
+- `AArch64Cpu::setConcreteMemoryAreaValue() <https://github.com/JonathanSalwan/Triton/blob/a61651ce331ac53ec09e1d8fef5eab744e98c9de/src/libtriton/arch/architecture.cpp#L329-L340>`_
 
 to map the coredump in Triton and then use its engines: Taint analysis, symbolic execution.
 
@@ -176,12 +178,10 @@ to map the coredump in Triton and then use its engines: Taint analysis, symbolic
 
 .. [1] https://www.gabriel.urdhr.fr/2015/05/29/core-file/
 
-
-
 .. rubric:: API
 
 * :func:`lief.parse`
-* :attr:`lief.ELF.Note.details`
+* :class:`lief.ELF.Note`
 
 * :class:`lief.ELF.CorePrPsInfo`
 * :class:`lief.ELF.CorePrStatus`
diff --git a/include/LIEF/ELF.hpp b/include/LIEF/ELF.hpp
index 8c7040c42f..4ce17af132 100644
--- a/include/LIEF/ELF.hpp
+++ b/include/LIEF/ELF.hpp
@@ -47,11 +47,8 @@
 #include "LIEF/ELF/SymbolVersionDefinition.hpp"
 #include "LIEF/ELF/SymbolVersionRequirement.hpp"
 #include "LIEF/ELF/SysvHash.hpp"
-
-#include "LIEF/ELF/NoteDetails/AndroidNote.hpp"
-#include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
-#include "LIEF/ELF/NoteDetails/Core.hpp"
 #include "LIEF/ELF/NoteDetails.hpp"
+
 #endif
 
 #endif
diff --git a/include/LIEF/ELF/Binary.hpp b/include/LIEF/ELF/Binary.hpp
index 8578518257..49d5ef9913 100644
--- a/include/LIEF/ELF/Binary.hpp
+++ b/include/LIEF/ELF/Binary.hpp
@@ -25,6 +25,7 @@
 
 #include "LIEF/Abstract/Binary.hpp"
 
+#include "LIEF/ELF/Note.hpp"
 #include "LIEF/ELF/Header.hpp"
 #include "LIEF/ELF/Builder.hpp"
 
@@ -39,7 +40,6 @@ class DynamicEntry;
 class ExeLayout;
 class GnuHash;
 class Layout;
-class Note;
 class ObjectFileLayout;
 class Parser;
 class Relocation;
@@ -281,7 +281,7 @@ class LIEF_API Binary : public LIEF::Binary {
   void remove(const Note& note);
 
   //! Remove **all** notes with the given type
-  void remove(NOTE_TYPES type);
+  void remove(Note::TYPE type);
 
   //! Remove the given segment
   void remove(const Segment& seg);
@@ -692,8 +692,8 @@ class LIEF_API Binary : public LIEF::Binary {
 
   //! Return the **first** ELF::Note associated with the given type
   //! If a note can't be found, it returns a nullptr.
-  const Note* get(NOTE_TYPES type) const;
-  Note*       get(NOTE_TYPES type);
+  const Note* get(Note::TYPE type) const;
+  Note*       get(Note::TYPE type);
 
   //! Return the **first** ELF::Section associated with the given type
   //! If a section can't be found, it returns a nullptr.
@@ -707,7 +707,7 @@ class LIEF_API Binary : public LIEF::Binary {
   bool has(SEGMENT_TYPES type) const;
 
   //! Check if a ELF::Note associated with the given type exists.
-  bool has(NOTE_TYPES type) const;
+  bool has(Note::TYPE type) const;
 
   //! Check if a ELF::Section associated with the given type exists.
   bool has(ELF_SECTION_TYPES type) const;
@@ -783,7 +783,7 @@ class LIEF_API Binary : public LIEF::Binary {
   Binary& operator-=(DYNAMIC_TAGS tag);
 
   Binary& operator-=(const Note& note);
-  Binary& operator-=(NOTE_TYPES type);
+  Binary& operator-=(Note::TYPE type);
 
   Segment*       operator[](SEGMENT_TYPES type);
   const Segment* operator[](SEGMENT_TYPES type) const;
@@ -791,8 +791,8 @@ class LIEF_API Binary : public LIEF::Binary {
   DynamicEntry*       operator[](DYNAMIC_TAGS tag);
   const DynamicEntry* operator[](DYNAMIC_TAGS tag) const;
 
-  Note*       operator[](NOTE_TYPES type);
-  const Note* operator[](NOTE_TYPES type) const;
+  Note*       operator[](Note::TYPE type);
+  const Note* operator[](Note::TYPE type) const;
 
   Section*       operator[](ELF_SECTION_TYPES type);
   const Section* operator[](ELF_SECTION_TYPES type) const;
diff --git a/include/LIEF/ELF/Builder.hpp b/include/LIEF/ELF/Builder.hpp
index 7cd66f6a3f..32195a2d1d 100644
--- a/include/LIEF/ELF/Builder.hpp
+++ b/include/LIEF/ELF/Builder.hpp
@@ -171,7 +171,7 @@ class LIEF_API Builder {
   template<typename ELF_T>
   ok_error_t build_notes();
 
-  ok_error_t build(const Note& note, std::set<Section*>& sections);
+  ok_error_t update_note_section(const Note& note, std::set<const Note*>& notes);
 
   template<typename ELF_T>
   ok_error_t build_overlay();
diff --git a/include/LIEF/ELF/EnumToString.hpp b/include/LIEF/ELF/EnumToString.hpp
index a4cb859f7a..6c30fc0fc4 100644
--- a/include/LIEF/ELF/EnumToString.hpp
+++ b/include/LIEF/ELF/EnumToString.hpp
@@ -40,9 +40,6 @@ LIEF_API const char* to_string(ELF_CLASS e);
 LIEF_API const char* to_string(ELF_DATA e);
 LIEF_API const char* to_string(OS_ABI e);
 LIEF_API const char* to_string(DYNSYM_COUNT_METHODS e);
-LIEF_API const char* to_string(NOTE_TYPES e);
-LIEF_API const char* to_string(NOTE_TYPES_CORE e);
-LIEF_API const char* to_string(NOTE_ABIS e);
 LIEF_API const char* to_string(RELOCATION_PURPOSES e);
 LIEF_API const char* to_string(IDENTITY e);
 LIEF_API const char* to_string(SYMBOL_SECTION_INDEX e);
@@ -50,7 +47,6 @@ LIEF_API const char* to_string(DYNAMIC_FLAGS e);
 LIEF_API const char* to_string(DYNAMIC_FLAGS_1 e);
 LIEF_API const char* to_string(ELF_SEGMENT_FLAGS e);
 LIEF_API const char* to_string(ELF_SYMBOL_VISIBILITY e);
-LIEF_API const char* to_string(AUX_TYPE e);
 
 LIEF_API const char* to_string(PPC64_EFLAGS e);
 LIEF_API const char* to_string(ARM_EFLAGS e);
diff --git a/include/LIEF/ELF/Note.hpp b/include/LIEF/ELF/Note.hpp
index 99fe25b3e9..aa20512a94 100644
--- a/include/LIEF/ELF/Note.hpp
+++ b/include/LIEF/ELF/Note.hpp
@@ -22,96 +22,229 @@
 
 #include "LIEF/Object.hpp"
 #include "LIEF/visibility.h"
+#include "LIEF/errors.hpp"
 
 #include "LIEF/ELF/enums.hpp"
 
 namespace LIEF {
+class BinaryStream;
 namespace ELF {
 
-class Parser;
-class Builder;
-class Binary;
-class NoteDetails;
-
-//! Class which represents an ELF note
+/// Class which represents an ELF note. This class can be instantiated using
+/// the static Note::create functions.
 class LIEF_API Note : public Object {
-
   friend class Parser;
   friend class Builder;
   friend class Binary;
-  friend class NoteDetails;
 
   public:
   //! Container used to handle the description data
   using description_t = std::vector<uint8_t>;
 
-  public:
-  Note();
-  Note(std::string  name, uint32_t type, description_t description, Binary* binary=nullptr);
-  Note(const std::string& name, NOTE_TYPES type, const description_t& description, Binary* binary=nullptr);
-  Note(const std::string& name, NOTE_TYPES_CORE type, const description_t& description, Binary* binary=nullptr);
-
-  Note& operator=(Note copy);
-  Note(const Note& copy);
-
-  ~Note() override;
+  /// LIEF representation of the ELF `NT_` values.
+  enum class TYPE {
+    UNKNOWN = 0,
+    /// Match `NT_GNU_ABI_TAG`: Operating system (OS) ABI information
+    ///
+    /// See: NoteAbi
+    GNU_ABI_TAG,
+    /// Match `NT_HWCAP`: Synthetic hardware capabilities information
+    GNU_HWCAP,
+    /// Match `NT_GNU_BUILD_ID`: Unique build ID as generated by the GNU ld
+    GNU_BUILD_ID,
+    /// Match `NT_GNU_GOLD_VERSION`: The version of gold used to link
+    GNU_GOLD_VERSION,
+    /// Match `NT_GNU_PROPERTY_TYPE_0`: Program property note, as described in
+    /// "Linux Extensions to the gABI".
+    GNU_PROPERTY_TYPE_0,
+
+    GNU_BUILD_ATTRIBUTE_OPEN,
+    GNU_BUILD_ATTRIBUTE_FUNC,
+
+    /// Crashpad note used by the Chromium project
+    CRASHPAD,
+
+    /// Coredump that wraps the `elf_prstatus` structure
+    CORE_PRSTATUS,
+    CORE_FPREGSET,
+    /// Coredump that wraps the `elf_prpsinfo` structure
+    ///
+    /// See: CorePrPsInfo
+    CORE_PRPSINFO,
+    CORE_TASKSTRUCT,
+    /// Coredump that contains a copy of all the auxiliary vectors (auxv)
+    ///
+    /// See: CoreAuxv
+    CORE_AUXV,
+    CORE_PSTATUS,
+    /// Coredump that wraps the `fpregset` structure
+    CORE_FPREGS,
+    /// Coredump that wraps the `psinfo` structure
+    CORE_PSINFO,
+    CORE_LWPSTATUS,
+    CORE_LWPSINFO,
+    CORE_WIN32PSTATUS,
+    CORE_FILE,
+    CORE_PRXFPREG,
+    CORE_SIGINFO,
+
+    CORE_ARM_VFP,
+    CORE_ARM_TLS,
+    CORE_ARM_HW_BREAK,
+    CORE_ARM_HW_WATCH,
+    CORE_ARM_SYSTEM_CALL,
+    CORE_ARM_SVE,
+    CORE_ARM_PAC_MASK,
+    CORE_ARM_PACA_KEYS,
+    CORE_ARM_PACG_KEYS,
+    CORE_TAGGED_ADDR_CTRL,
+    CORE_PAC_ENABLED_KEYS,
+
+    CORE_X86_TLS,
+    CORE_X86_IOPERM,
+    CORE_X86_XSTATE,
+    CORE_X86_CET,
+
+    /// Note that is specific to Android and that describes information such as
+    /// the NDK version or the SDK build number.
+    ///
+    /// See AndroidIdent
+    ANDROID_IDENT,
+    ANDROID_MEMTAG,
+    ANDROID_KUSER,
+
+    /// Note specific to Go binaries
+    GO_BUILDID,
+    /// Note for SystemTap probes
+    STAPSDT,
+  };
 
-  //! Return the *name* of the note
-  const std::string& name() const;
-
-  //! Return the type of the note. It could be one of the NOTE_TYPES values
-  NOTE_TYPES type() const;
-
-  //! Return the type of the note for core ELF (ET_CORE).
-  //! It could be one of the NOTE_TYPES_CORE values
-  NOTE_TYPES_CORE type_core() const;
+  public:
+  /// Convert the raw integer note type into a TYPE according to the owner
+  static result<TYPE> convert_type(E_TYPE ftype, uint32_t type,
+                                   const std::string& name);
+
+  /// Try to determine the ELF section name associated with the TYPE
+  /// provided in parameter
+  static result<const char*> type_to_section(TYPE type);
+
+  static result<const char*> note_to_section(const Note& note) {
+    return type_to_section(note.type());
+  }
+
+  /// Try to determine the owner's name of the TYPE provided in parameter
+  static result<const char*> type_owner(TYPE type);
+
+  /// Create a new note from the given parameters. Additional information
+  /// such as the architecture or the ELF class could be required for
+  /// creating notes like Coredump notes.
+  static std::unique_ptr<Note> create(
+      const std::string& name, uint32_t type, description_t description,
+      E_TYPE ftype = E_TYPE::ET_NONE, ARCH arch = ARCH::EM_NONE,
+      ELF_CLASS cls = ELF_CLASS::ELFCLASSNONE);
+
+  /// Create a new note from the given parameters. Additional information
+  /// such as the architecture or the ELF class could be required for
+  /// creating notes like Coredump notes.
+  static std::unique_ptr<Note> create(
+      const std::string& name, TYPE type, description_t description,
+      ARCH arch = ARCH::EM_NONE, ELF_CLASS cls = ELF_CLASS::ELFCLASSNONE);
+
+  /// Create a new note from the given stream. Additional information
+  /// such as the architecture or the ELF class could be required for
+  /// creating notes like Coredump notes.
+  static std::unique_ptr<Note> create(BinaryStream& stream,
+      E_TYPE ftype = E_TYPE::ET_NONE, ARCH arch = ARCH::EM_NONE,
+      ELF_CLASS cls = ELF_CLASS::ELFCLASSNONE);
+
+  Note& operator=(const Note& copy) = default;
+  Note(const Note& copy) = default;
+
+  ~Note() override = default;
+
+  /// Clone the current note and keep its polymorphic type
+  virtual std::unique_ptr<Note> clone() const {
+    return std::unique_ptr<Note>(new Note(*this));
+  }
+
+  /// Return the *name* of the note (also known as 'owner' )
+  const std::string& name() const {
+    return name_;
+  }
+
+  /// Return the type of the note. This type does not match the `NT_` type
+  /// value. For accessing the original `NT_` value, check original_type()
+  TYPE type() const {
+    return type_;
+  }
+
+  /// The original `NT_xxx` integer value. The meaning of this value likely
+  /// depends on the owner of the note.
+  uint32_t original_type() const {
+    return original_type_;
+  }
 
   //! Return the description associated with the note
-  const description_t& description() const;
-
-  description_t& description();
-
-  //! True if the current note is associated with a core dump
-  bool is_core() const;
+  const description_t& description() const {
+    return description_;
+  }
 
-  //! True if the current note is specific to Android.
-  //!
-  //! If true, ``details()`` returns a reference to the LIEF::ELF::AndroidNote object
-  bool is_android() const;
+  description_t& description() {
+    return description_;
+  }
 
-  const NoteDetails& details() const;
-  NoteDetails& details();
+  void name(std::string name) {
+    name_ = std::move(name);
+  }
 
-  void name(const std::string& name);
-  void type(NOTE_TYPES type);
-  void type_core(NOTE_TYPES_CORE type);
-  void description(const description_t& description);
+  /// Change the description of the note
+  void description(description_t description) {
+    description_ = std::move(description);
+  }
 
-  //! Size of the **raw** note
+  /// Size of the **raw** note which includes padding
   uint64_t size() const;
 
   virtual void dump(std::ostream& os) const;
 
-
-  void swap(Note& other);
-
   void accept(Visitor& visitor) const override;
 
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const Note& note);
+  LIEF_API friend
+  std::ostream& operator<<(std::ostream& os, const Note& note) {
+    note.dump(os);
+    return os;
+  }
 
   protected:
-  Binary*       binary_{nullptr};
-  std::string   name_;
-  NOTE_TYPES    type_;
-  description_t description_;
+  Note() = default;
+  Note(std::string name, TYPE type, uint32_t original_type,
+       description_t description) :
+    name_(std::move(name)),
+    type_(type),
+    original_type_(original_type),
+    description_(std::move(description))
+  {}
+
+  template<class T>
+  LIEF_LOCAL result<T> read_at(size_t offset) const;
+
+  template<class T>
+  LIEF_LOCAL ok_error_t write_at(size_t offset, const T& value);
 
-  private:
-  bool is_core_{false};
-  std::pair<NOTE_TYPES, std::unique_ptr<NoteDetails>> details_;
+  LIEF_LOCAL ok_error_t write_string_at(size_t offset, const std::string& value);
+
+  LIEF_LOCAL result<std::string>
+  read_string_at(size_t offset, size_t maxsize = 0) const;
+
+  std::string  name_;
+  TYPE type_ = TYPE::UNKNOWN;
+  uint32_t original_type_ = 0;
+  description_t description_;
 };
 
+LIEF_API const char* to_string(Note::TYPE type);
+
 
 } // namepsace ELF
 } // namespace LIEF
-
 #endif
diff --git a/include/LIEF/ELF/NoteDetails.hpp b/include/LIEF/ELF/NoteDetails.hpp
index 53ccc0b5d1..1010160b1d 100644
--- a/include/LIEF/ELF/NoteDetails.hpp
+++ b/include/LIEF/ELF/NoteDetails.hpp
@@ -15,64 +15,7 @@
  */
 #ifndef LIEF_ELF_NOTE_DETAILS_H
 #define LIEF_ELF_NOTE_DETAILS_H
-
-#include <vector>
-#include <cstdint>
-#include <ostream>
-
-#include "LIEF/Object.hpp"
-#include "LIEF/visibility.h"
-
-namespace LIEF {
-namespace ELF {
-
-class Parser;
-class Builder;
-class Binary;
-class Note;
-
-class LIEF_API NoteDetails : public Object {
-
-  friend class Parser;
-  friend class Builder;
-  friend class Binary;
-
-  public:
-  using description_t = std::vector<uint8_t>;
-  NoteDetails();
-
-  protected:
-  NoteDetails(Note& note);
-
-  public:
-  ~NoteDetails() override;
-
-  virtual NoteDetails* clone() const;
-
-  const description_t& description() const;
-
-  virtual void dump(std::ostream& os) const;
-
-
-  void accept(Visitor& visitor) const override;
-
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const NoteDetails& note);
-
-  protected:
-  virtual void parse();
-  virtual void build();
-
-  description_t& description();
-  Binary* binary();
-  const Binary* binary() const;
-
-  private:
-  Note* note_{nullptr};
-  description_t empty_;
-};
-
-
-} // namepsace ELF
-} // namespace LIEF
-
+#include "LIEF/ELF/NoteDetails/AndroidIdent.hpp"
+#include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
+#include "LIEF/ELF/NoteDetails/Core.hpp"
 #endif
diff --git a/include/LIEF/ELF/NoteDetails/AndroidNote.hpp b/include/LIEF/ELF/NoteDetails/AndroidIdent.hpp
similarity index 58%
rename from include/LIEF/ELF/NoteDetails/AndroidNote.hpp
rename to include/LIEF/ELF/NoteDetails/AndroidIdent.hpp
index 8e5d54e1aa..d69d9c3d87 100644
--- a/include/LIEF/ELF/NoteDetails/AndroidNote.hpp
+++ b/include/LIEF/ELF/NoteDetails/AndroidIdent.hpp
@@ -13,85 +13,67 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef LIEF_ELF_ANDROID_NOTE_H
-#define LIEF_ELF_ANDROID_NOTE_H
+#ifndef LIEF_ELF_ANDROID_IDENT_H
+#define LIEF_ELF_ANDROID_IDENT_H
 
 #include <vector>
 #include <ostream>
+#include <memory>
 
-#include "LIEF/Object.hpp"
 #include "LIEF/visibility.h"
-#include "LIEF/ELF/NoteDetails.hpp"
+#include "LIEF/ELF/Note.hpp"
 
 namespace LIEF {
 namespace ELF {
 
-class Parser;
-class Builder;
-class Binary;
-class Note;
-
 //! Class representing the ".note.android.ident" section
 //!
 //! @see: https://android.googlesource.com/platform/ndk/+/ndk-release-r16/sources/crt/crtbrand.S#39
-class LIEF_API AndroidNote : public NoteDetails {
-
-  friend class Parser;
-  friend class Builder;
-  friend class Binary;
-
+class LIEF_API AndroidIdent : public Note {
   public:
-  static constexpr const char NAME[] = "Android";
-
-  static constexpr size_t sdk_version_offset      = 0;
   static constexpr size_t sdk_version_size        = sizeof(uint32_t);
-
-  static constexpr size_t ndk_version_offset      = sdk_version_offset + sdk_version_size;
   static constexpr size_t ndk_version_size        = 64 * sizeof(char);
-
-  static constexpr size_t ndk_build_number_offset = ndk_version_offset + ndk_version_size;
   static constexpr size_t ndk_build_number_size   = 64 * sizeof(char);
 
-  static AndroidNote make(Note& note);
-
   public:
-  using NoteDetails::NoteDetails;
-  using description_t = std::vector<uint8_t>;
-
-  AndroidNote* clone() const override;
+  std::unique_ptr<Note> clone() const override {
+    return std::unique_ptr<AndroidIdent>(new AndroidIdent(*this));
+  }
 
-  //! Target SDK version
+  //! Target SDK version (or 0 if it can't be resolved)
   uint32_t sdk_version() const;
 
-  //! NDK version used
+  //! NDK version used (or an empty string if it can't be parsed)
   std::string ndk_version() const;
 
-  //! NDK build number
+  //! NDK build number (or an empty string if it can't be parsed)
   std::string ndk_build_number() const;
 
   void sdk_version(uint32_t version);
   void ndk_version(const std::string& ndk_version);
   void ndk_build_number(const std::string& ndk_build_number);
 
-
   void dump(std::ostream& os) const override;
 
   void accept(Visitor& visitor) const override;
 
-  ~AndroidNote() override;
+  static bool classof(const Note* note) {
+    return note->type() == Note::TYPE::ANDROID_IDENT;
+  }
 
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const AndroidNote& note);
+  ~AndroidIdent() override = default;
 
-  protected:
-  void parse() override;
-  void build() override;
-
-  private:
-  AndroidNote(Note& note);
+  static constexpr size_t description_size() {
+    return sdk_version_size + ndk_version_size + ndk_build_number_size;
+  }
 
-  uint32_t sdk_version_ = 0;
-  std::string ndk_version_;
-  std::string ndk_build_number_;
+  LIEF_API friend
+  std::ostream& operator<<(std::ostream& os, const AndroidIdent& note) {
+    note.dump(os);
+    return os;
+  }
+  protected:
+  using Note::Note;
 };
 
 
diff --git a/include/LIEF/ELF/NoteDetails/NoteAbi.hpp b/include/LIEF/ELF/NoteDetails/NoteAbi.hpp
index c0f39941ef..9609cd74e4 100644
--- a/include/LIEF/ELF/NoteDetails/NoteAbi.hpp
+++ b/include/LIEF/ELF/NoteDetails/NoteAbi.hpp
@@ -13,79 +13,81 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef LIEF_ELF_NOTE_DETAILS_ABI_H
-#define LIEF_ELF_NOTE_DETAILS_ABI_H
+#ifndef LIEF_ELF_NOTE_ABI_H
+#define LIEF_ELF_NOTE_ABI_H
 
 #include <vector>
 #include <ostream>
 #include <array>
+#include <memory>
 
-#include "LIEF/Object.hpp"
 #include "LIEF/visibility.h"
 #include "LIEF/ELF/Note.hpp"
-#include "LIEF/ELF/NoteDetails.hpp"
 
 namespace LIEF {
 namespace ELF {
 
-class Parser;
-class Builder;
-class Binary;
-
-//! Class representing the ``.note.android.ident`` section
-//!
-//! @see: https://android.googlesource.com/platform/ndk/+/ndk-release-r16/sources/crt/crtbrand.S#39
-class LIEF_API NoteAbi : public NoteDetails {
-
-  friend class Parser;
-  friend class Builder;
-  friend class Binary;
-
+/// Class that wraps the `NT_GNU_ABI_TAG` note
+class LIEF_API NoteAbi : public Note {
   public:
-
-  //! Version type: (Major, Minor, Patch)
+  /// ABI recognized by this note
+  enum class ABI {
+    LINUX = 0,
+    GNU,
+    SOLARIS2,
+    FREEBSD,
+    NETBSD,
+    SYLLABLE,
+    NACL
+  };
+  /// Version type: (Major, Minor, Patch)
   using version_t = std::array<uint32_t, 3>;
 
   static constexpr size_t abi_offset      = 0;
   static constexpr size_t abi_size        = sizeof(uint32_t);
 
-  static constexpr size_t version_offset  = abi_offset + abi_size;
+  static constexpr size_t version_offset  = abi_size;
   static constexpr size_t version_size    = 3 * sizeof(uint32_t);
 
-  static NoteAbi make(Note& note);
-
-  NoteAbi* clone() const override;
-
   public:
-  using NoteDetails::NoteDetails;
-  using description_t = typename Note::description_t;
+  using Note::Note;
+
+  std::unique_ptr<Note> clone() const override {
+    return std::unique_ptr<Note>(new NoteAbi(*this));
+  }
 
-  //! @brief Return the target version as ``<Major, Minor, Patch>``.
-  version_t version() const;
+  /// Return the version or an error if it can't be parsed
+  result<version_t> version() const;
 
-  //! @brief Return the target ABI. Require a NT_GNU_ABI_TAG type
-  NOTE_ABIS abi() const;
+  /// Return the ABI or an error if it can't be parsed
+  result<ABI> abi() const;
 
+  void version(const version_t& version);
+  void version(ABI abi);
 
   void dump(std::ostream& os) const override;
 
   void accept(Visitor& visitor) const override;
 
-  ~NoteAbi() override;
-
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const NoteAbi& note);
-
-  protected:
-  void parse() override;
+  static bool classof(const Note* note) {
+    return note->type() == Note::TYPE::GNU_ABI_TAG;
+  }
 
-  private:
-  NoteAbi(Note& note);
+  //// Size of the description content
+  static constexpr uint8_t description_size() {
+    return /* abi */ sizeof(uint32_t) + /* version */ 3 * sizeof(uint32_t);
+  }
 
-  version_t version_;
-  NOTE_ABIS abi_;
+  ~NoteAbi() override = default;
 
+  LIEF_API friend
+  std::ostream& operator<<(std::ostream& os, const NoteAbi& note) {
+    note.dump(os);
+    return os;
+  }
 };
 
+LIEF_API const char* to_string(NoteAbi::ABI abi);
 
 } // namepsace ELF
 } // namespace LIEF
diff --git a/include/LIEF/ELF/NoteDetails/core/CoreAuxv.hpp b/include/LIEF/ELF/NoteDetails/core/CoreAuxv.hpp
index 41c6eaa3d3..015b898583 100644
--- a/include/LIEF/ELF/NoteDetails/core/CoreAuxv.hpp
+++ b/include/LIEF/ELF/NoteDetails/core/CoreAuxv.hpp
@@ -21,74 +21,99 @@
 #include <map>
 #include <utility>
 
-#include "LIEF/Object.hpp"
 #include "LIEF/visibility.h"
 #include "LIEF/ELF/enums.hpp"
-#include "LIEF/ELF/NoteDetails.hpp"
+#include "LIEF/ELF/Note.hpp"
 
 namespace LIEF {
 namespace ELF {
 
-class Parser;
-class Builder;
-class Binary;
-class Note;
-
-//! Class representing core Auxv object
-class LIEF_API CoreAuxv : public NoteDetails {
-
-  public:
-  using NoteDetails::NoteDetails;
-
-  using val_context_t = std::map<AUX_TYPE, uint64_t>;
-
+//! Class representing core auxv object
+class LIEF_API CoreAuxv : public Note {
   public:
-  static CoreAuxv make(Note& note);
-
-  CoreAuxv* clone() const override;
-
-  //! Auxiliary values
-  const val_context_t& values() const;
-
-  //! Get an auxiliary value. If ``error`` is set,
-  //! this function and the value exists, the function set the boolean value to ``false``
-  //! Otherwise it set the value to ``true``
-  uint64_t get(AUX_TYPE atype, bool* error = nullptr) const;
-
-  //! Check if the given register is present in the info
-  bool has(AUX_TYPE reg) const;
-
-  void values(const val_context_t& ctx);
-
-  bool set(AUX_TYPE atype, uint64_t value);
-
-
-  uint64_t& operator[](AUX_TYPE atype);
+  enum class TYPE {
+    END = 0,       /**< End of vector */
+    IGNORE,        /**< Entry should be ignored */
+    EXECFD,        /**< File descriptor of program */
+    PHDR,          /**< Program headers for program */
+    PHENT,         /**< Size of program header entry */
+    PHNUM,         /**< Number of program headers */
+    PAGESZ,        /**< System page size */
+    BASE,          /**< Base address of interpreter */
+    FLAGS,         /**< Flags */
+    ENTRY,         /**< Entry point of program */
+    NOTELF,        /**< Program is not ELF */
+    UID,           /**< Real uid */
+    EUID,          /**< Effective uid */
+    GID,           /**< Real gid */
+    EGID,          /**< Effective gid */
+    TGT_PLATFORM,  /**< String identifying platform.  */
+    HWCAP,         /**< Machine dependent hints about processor capabilities.  */
+    CLKTCK,        /**< Frequency of times() */
+    FPUCW,         /**< Used FPU control word.  */
+    DCACHEBSIZE,   /**< Data cache block size.  */
+    ICACHEBSIZE,   /**< Instruction cache block size.  */
+    UCACHEBSIZE,   /**< Instruction cache block size.  */
+    IGNOREPPC,     /**< Entry should be ignored.  */
+    SECURE,        /**< Boolean, was exec setuid-like?.  */
+    BASE_PLATFORM, /**< String identifying real platform  */
+    RANDOM,        /**< Address of 16 random bytes  */
+    HWCAP2,        /**< Extension of AT_HWCAP  */
+    //ENTRY27,
+    //ENTRY28,
+    //ENTRY29,
+    //ENTRY30,
+    EXECFN = 31,   /**< Filename of executable  */
+    SYSINFO,       /**< Filename of executable  */
+    SYSINFO_EHDR,  /**<  Pointer to ELF header of system-supplied DSO. */
+  };
+
+  CoreAuxv(ARCH arch, ELF_CLASS cls, std::string name,
+           uint32_t type, description_t description) :
+    Note(std::move(name), Note::TYPE::CORE_AUXV, type, std::move(description)),
+    arch_(arch), class_(cls)
+  {}
+
+  std::unique_ptr<Note> clone() const override {
+    return std::unique_ptr<Note>(new CoreAuxv(*this));
+  }
+
+  /// A map of CoreAuxv::TYPE and the value
+  std::map<TYPE, uint64_t> values() const;
+
+  /// Return the value associated with the provided TYPE or
+  /// a lief_errors::not_found if the type is not present.
+  result<uint64_t> get(TYPE type) const;
+
+  result<uint64_t> operator[](TYPE type) const {
+    return get(type);
+  }
+
+  bool set(TYPE type, uint64_t value);
+  bool set(std::map<TYPE, uint64_t> values);
 
   void dump(std::ostream& os) const override;
 
   void accept(Visitor& visitor) const override;
 
-  ~CoreAuxv() override;
+  static bool classof(const Note* note) {
+    return note->type() == Note::TYPE::CORE_AUXV;
+  }
 
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const CoreAuxv& note);
+  ~CoreAuxv() override = default;
 
-  protected:
-  template <typename ELF_T>
-  LIEF_LOCAL void parse_();
+  LIEF_API friend
+  std::ostream& operator<<(std::ostream& os, const CoreAuxv& note) {
+    note.dump(os);
+    return os;
+  }
 
-  template <typename ELF_T>
-  LIEF_LOCAL void build_();
-
-  void parse() override;
-  void build() override;
-
-  private:
-  CoreAuxv(Note& note);
-
-  val_context_t ctx_;
+  protected:
+  ARCH arch_ = ARCH::EM_NONE;
+  ELF_CLASS class_ = ELF_CLASS::ELFCLASSNONE;
 };
 
+LIEF_API const char* to_string(CoreAuxv::TYPE type);
 
 } // namepsace ELF
 } // namespace LIEF
diff --git a/include/LIEF/ELF/NoteDetails/core/CoreFile.hpp b/include/LIEF/ELF/NoteDetails/core/CoreFile.hpp
index 00f6591dee..4a7891da32 100644
--- a/include/LIEF/ELF/NoteDetails/core/CoreFile.hpp
+++ b/include/LIEF/ELF/NoteDetails/core/CoreFile.hpp
@@ -19,83 +19,94 @@
 #include <vector>
 #include <ostream>
 
-#include "LIEF/Object.hpp"
 #include "LIEF/visibility.h"
 
-#include "LIEF/ELF/NoteDetails.hpp"
+#include "LIEF/ELF/Note.hpp"
 
 namespace LIEF {
 namespace ELF {
 
-class Parser;
-class Builder;
-class Binary;
-class Note;
-
-//! Core file entry
-struct CoreFileEntry {
-  uint64_t      start;    ///< Start address of mapped file
-  uint64_t      end;      ///< End address of mapped file
-  uint64_t      file_ofs; ///< Offset (in core) of mapped file
-  std::string   path;     ///< Path of mapped file
-
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const CoreFileEntry& entry);
-};
-
-//! Class representing core PrPsInfo object
-class LIEF_API CoreFile : public NoteDetails {
-
+/// Class representing a core `NT_FILE` which describes the mapped files
+/// of the process
+class LIEF_API CoreFile : public Note {
   public:
-  using NoteDetails::NoteDetails;
-
-  using files_t        = std::vector<CoreFileEntry>;
+  //! Core file entry
+  struct entry_t {
+    uint64_t start = 0;    /// Start address of mapped file
+    uint64_t end = 0;      ///< End address of mapped file
+    uint64_t file_ofs = 0; ///< Offset (in core) of mapped file
+    std::string path;      ///< Path of mapped file
+
+    LIEF_API friend
+    std::ostream& operator<<(std::ostream& os, const entry_t& entry);
+  };
+
+  using files_t        = std::vector<entry_t>;
   using iterator       = files_t::iterator;
   using const_iterator = files_t::const_iterator;
 
   public:
-  static CoreFile make(Note& note);
+  CoreFile(ARCH arch, ELF_CLASS cls, std::string name,
+           uint32_t type, Note::description_t description);
 
-  CoreFile* clone() const override;
+  std::unique_ptr<Note> clone() const override {
+    return std::unique_ptr<Note>(new CoreFile(*this));
+  }
 
   //! Number of coredump file entries
-  uint64_t count() const;
+  uint64_t count() const {
+    return files_.size();
+  }
 
   //! Coredump file entries
-  const files_t& files() const;
+  const files_t& files() const {
+    return files_;
+  }
 
-  iterator begin();
-  iterator end();
+  iterator begin() {
+    return files_.begin();
+  }
 
-  const_iterator begin() const;
-  const_iterator end() const;
+  iterator end() {
+    return files_.end();
+  }
 
-  void files(const files_t&);
+  const_iterator begin() const {
+    return files_.begin();
+  }
 
+  const_iterator end() const {
+    return files_.end();
+  }
 
-  void dump(std::ostream& os) const override;
+  void files(const files_t& file);
 
+  void dump(std::ostream& os) const override;
   void accept(Visitor& visitor) const override;
 
-  ~CoreFile() override;
+  static bool classof(const Note* note) {
+    return note->type() == Note::TYPE::CORE_FILE;
+  }
 
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const CoreFile& note);
+  ~CoreFile() override = default;
 
-  protected:
-  template <typename ELF_T>
-  LIEF_LOCAL void parse_();
+  LIEF_API friend
+  std::ostream& operator<<(std::ostream& os, const CoreFile& note) {
+    note.dump(os);
+    return os;
+  }
 
-  template <typename ELF_T>
-  LIEF_LOCAL void build_();
-
-  void parse() override;
-  void build() override;
+  protected:
+  template<class T>
+  LIEF_LOCAL void read_files();
 
-  private:
-  CoreFile(Note& note);
+  template<class T>
+  LIEF_LOCAL void write_files();
 
-  private:
   files_t  files_;
-  uint64_t page_size_;
+  uint64_t page_size_ = 0;
+  ARCH arch_ = ARCH::EM_NONE;
+  ELF_CLASS class_ = ELF_CLASS::ELFCLASSNONE;
 };
 
 } // namepsace ELF
diff --git a/include/LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp b/include/LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp
index aedb9dba30..e4e14038f6 100644
--- a/include/LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp
+++ b/include/LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp
@@ -13,100 +13,80 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef LIEF_ELF_CORE_PSINFO_H
-#define LIEF_ELF_CORE_PSINFO_H
+#ifndef LIEF_ELF_CORE_PRPSINFO_H
+#define LIEF_ELF_CORE_PRPSINFO_H
 
 #include <vector>
 #include <ostream>
 
-#include "LIEF/Object.hpp"
 #include "LIEF/visibility.h"
-
-#include "LIEF/ELF/NoteDetails.hpp"
+#include "LIEF/ELF/enums.hpp"
+#include "LIEF/ELF/Note.hpp"
 
 namespace LIEF {
 namespace ELF {
 
-class Note;
-class Parser;
-class Builder;
-class Binary;
-
-//! Class representing core PrPsInfo object
-class LIEF_API CorePrPsInfo : public NoteDetails {
-
+/// Class representing the NT_PRPSINFO core note.
+/// This kind of note represents general information about the process
+class LIEF_API CorePrPsInfo : public Note {
   public:
-  using NoteDetails::NoteDetails;
-
-  public:
-  static CorePrPsInfo make(Note& note);
-
-  CorePrPsInfo* clone() const override;
-
-  //! Process file name
-  std::string file_name() const;
-
-  //! Process flag
-  uint64_t flags() const;
-
-  //! Process user id
-  uint32_t uid() const;
-
-  //! Process group id
-  uint32_t gid() const;
-
-  //! Process ID
-  int32_t pid() const;
-
-  //! Process parent ID
-  int32_t ppid() const;
-
-  //! Process session group ID
-  int32_t pgrp() const;
-
-  //! Process session ID
-  int32_t sid() const;
-
-  void file_name(const std::string& file_name);
-  void flags(uint64_t);
-  void uid(uint32_t);
-  void gid(uint32_t);
-  void pid(int32_t);
-  void ppid(int32_t);
-  void pgrp(int32_t);
-  void sid(int32_t);
-
+  struct info_t {
+    uint8_t state = 0;    /// Numeric process state
+    char sname = ' ';     /// printable character representing state
+    bool zombie = false;  /// Whether the process is a zombie
+    uint8_t nice = 0;     /// Nice value
+    uint64_t flag = 0;    /// Process flag
+    uint32_t uid = 0;     /// Process user ID
+    uint32_t gid = 0;     /// Process group ID
+    uint32_t pid = 0;     /// Process ID
+    uint32_t ppid = 0;    /// Process parent ID
+    uint32_t pgrp = 0;    /// Process group
+    uint32_t sid = 0;     /// Process session id
+    std::string filename; /// Filename of the executable
+    std::string args;     /// Initial part of the arguments
+
+    /// Return the filename without the ending '\x00'
+    std::string filename_stripped() const {
+      return filename.c_str();
+    }
+
+    /// Return the args without the ending '\x00'
+    std::string args_stripped() const {
+      return args.c_str();
+    }
+  };
+  CorePrPsInfo(ARCH arch, ELF_CLASS cls, std::string name,
+               uint32_t type, description_t description) :
+    Note(std::move(name), TYPE::CORE_PRPSINFO, type, std::move(description)),
+    arch_(arch), class_(cls)
+  {}
+
+  std::unique_ptr<Note> clone() const override {
+    return std::unique_ptr<Note>(new CorePrPsInfo(*this));
+  }
+
+  /// Return a `elf_prpsinfo`-like structure or an error if it can't be parsed.
+  result<info_t> info() const;
+  void info(const info_t& info);
 
   void dump(std::ostream& os) const override;
 
   void accept(Visitor& visitor) const override;
 
-  ~CorePrPsInfo() override;
+  static bool classof(const Note* note) {
+    return note->type() == Note::TYPE::CORE_PRPSINFO;
+  }
 
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const CorePrPsInfo& note);
-
-  protected:
-  template <typename ELF_T>
-  LIEF_LOCAL void parse_();
-
-  template <typename ELF_T>
-  LIEF_LOCAL void build_();
-
-  void parse() override;
-  void build() override;
-
-  private:
-  CorePrPsInfo(Note& note);
+  ~CorePrPsInfo() override = default;
 
+  LIEF_API friend
+  std::ostream& operator<<(std::ostream& os, const CorePrPsInfo& note) {
+    note.dump(os);
+    return os;
+  }
   private:
-  std::string file_name_;
-  uint64_t flags_;
-  uint32_t uid_;
-  uint32_t gid_;
-  int32_t pid_;
-  int32_t ppid_;
-  int32_t pgrp_;
-  int32_t sid_;
+  ARCH arch_ = ARCH::EM_NONE;
+  ELF_CLASS class_ = ELF_CLASS::ELFCLASSNONE;
 };
 
 } // namepsace ELF
diff --git a/include/LIEF/ELF/NoteDetails/core/CorePrStatus.hpp b/include/LIEF/ELF/NoteDetails/core/CorePrStatus.hpp
index c1bfe462a1..f779253bdc 100644
--- a/include/LIEF/ELF/NoteDetails/core/CorePrStatus.hpp
+++ b/include/LIEF/ELF/NoteDetails/core/CorePrStatus.hpp
@@ -18,13 +18,11 @@
 
 #include <vector>
 #include <ostream>
-#include <map>
 #include <utility>
 
-#include "LIEF/Object.hpp"
 #include "LIEF/visibility.h"
-
-#include "LIEF/ELF/NoteDetails.hpp"
+#include "LIEF/ELF/enums.hpp"
+#include "LIEF/ELF/Note.hpp"
 
 namespace LIEF {
 namespace ELF {
@@ -34,188 +32,168 @@ class Builder;
 class Binary;
 
 //! Class representing core PrPsInfo object
-class LIEF_API CorePrStatus : public NoteDetails {
-
+class LIEF_API CorePrStatus : public Note {
   public:
-  using NoteDetails::NoteDetails;
   struct siginfo_t {
-    int32_t si_signo;
-    int32_t si_code;
-    int32_t si_errno;
+    int32_t signo = 0;
+    int32_t code = 0;
+    int32_t err = 0;
   };
 
   struct timeval_t {
-    uint64_t sec;
-    uint64_t usec;
-  };
-
-
-  enum class REGISTERS  {
-    UNKNOWN,
-
-    // x86
-    // ===
-    X86_START,
-      X86_EBX, X86_ECX, X86_EDX, X86_ESI, X86_EDI, X86_EBP, X86_EAX,
-      X86_DS, X86_ES, X86_FS, X86_GS, X86__, X86_EIP, X86_CS, X86_EFLAGS, X86_ESP, X86_SS,
-    X86_END,
-
-    // x86-64
-    // ======
-    X86_64_START,
-      X86_64_R15, X86_64_R14, X86_64_R13, X86_64_R12, X86_64_RBP, X86_64_RBX, X86_64_R11, X86_64_R10,
-      X86_64_R9, X86_64_R8, X86_64_RAX, X86_64_RCX, X86_64_RDX, X86_64_RSI, X86_64_RDI, X86_64__,
-      X86_64_RIP, X86_64_CS, X86_64_EFLAGS, X86_64_RSP, X86_64_SS,
-    X86_64_END,
-
-    // ARM
-    // ===
-    ARM_START,
-      ARM_R0, ARM_R1, ARM_R2,  ARM_R3,  ARM_R4,  ARM_R5,  ARM_R6,  ARM_R7,
-      ARM_R8, ARM_R9, ARM_R10, ARM_R11, ARM_R12, ARM_R13, ARM_R14, ARM_R15,
-      ARM_CPSR,
-    ARM_END,
-
-    // AArch64
-    // =======
-    AARCH64_START,
-      AARCH64_X0,  AARCH64_X1,  AARCH64_X2,  AARCH64_X3,  AARCH64_X4,  AARCH64_X5,  AARCH64_X6,  AARCH64_X7,
-      AARCH64_X8,  AARCH64_X9,  AARCH64_X10, AARCH64_X11, AARCH64_X12, AARCH64_X13, AARCH64_X14, AARCH64_X15,
-      AARCH64_X16, AARCH64_X17, AARCH64_X18, AARCH64_X19, AARCH64_X20, AARCH64_X21, AARCH64_X22, AARCH64_X23,
-      AARCH64_X24, AARCH64_X25, AARCH64_X26, AARCH64_X27, AARCH64_X28, AARCH64_X29, AARCH64_X30, AARCH64_X31,
-      AARCH64_PC, AARCH64__,
-    AARCH64_END,
+    uint64_t sec = 0;
+    uint64_t usec = 0;
   };
-  using reg_context_t = std::map<REGISTERS, uint64_t>;
-
-  public:
-  static CorePrStatus make(Note& note);
-
-  CorePrStatus* clone() const override;
-
-  //! Info associated with the signal
-  const siginfo_t& siginfo() const;
-
-  //! Current Signal
-  uint16_t current_sig() const;
-
-  //! Set of pending signals
-  uint64_t sigpend() const;
-
-  //! Set of held signals
-  uint64_t sighold() const;
-
-  //! Process ID
-  int32_t pid() const;
-
-  //! Process parent ID
-  int32_t ppid() const;
-
-  //! Process group ID
-  int32_t pgrp() const;
-
-  //! Process session ID
-  int32_t sid() const;
-
-  //! User time
-  timeval_t utime() const;
-
-  //! System time
-  timeval_t stime() const;
-
-  //! Cumulative user time
-  timeval_t cutime() const;
-
-  //! Cumulative system time
-  timeval_t cstime() const;
 
-  //! GP registers state
-  const reg_context_t& reg_context() const;
+  struct pr_status_t {
+    siginfo_t info;
 
-  //! Return the program counter
-  uint64_t pc() const;
+    uint16_t cursig = 0;
+    uint16_t reserved = 0;
 
-  //! Return the stack pointer
-  uint64_t sp() const;
+    uint64_t sigpend = 0;
+    uint64_t sighold = 0;
 
-  //! Get register value. If ``error`` is set,
-  //! this function and the register exists, the function set the boolean value to ``false``
-  //! Otherwise it set the value to ``true``
-  uint64_t get(REGISTERS reg, bool* error = nullptr) const;
+    int32_t  pid = 0;
+    int32_t  ppid = 0;
+    int32_t  pgrp = 0;
+    int32_t  sid = 0;
 
-  //! Check if the given register is present in the info
-  bool has(REGISTERS reg) const;
-
-  void siginfo(const siginfo_t& siginfo);
-  void current_sig(uint16_t current_sig);
-
-  void sigpend(uint64_t sigpend);
-  void sighold(uint64_t sighold);
-
-  void pid(int32_t pid);
-  void ppid(int32_t ppid);
-  void pgrp(int32_t pgrp);
-  void sid(int32_t sid);
-
-  void utime(timeval_t utime);
-  void stime(timeval_t stime);
-  void cutime(timeval_t cutime);
-  void cstime(timeval_t cstime);
-
-  void reg_context(const reg_context_t& ctx);
-
-  bool set(REGISTERS reg, uint64_t value);
+    timeval_t utime;
+    timeval_t stime;
+    timeval_t cutime;
+    timeval_t cstime;
+  };
 
+  struct Registers {
+    /// Register for the x86 architecture (ARCH::EM_386).
+    enum class X86 {
+      EBX = 0, ECX, EDX, ESI, EDI, EBP, EAX,
+      DS, ES, FS, GS, ORIG_EAX, EIP, CS, EFLAGS, ESP, SS,
+      _COUNT
+    };
+
+    /// Register for the x86-64 architecture (ARCH::EM_X86_64).
+    enum class X86_64 {
+      R15 = 0, R14, R13, R12, RBP, RBX, R11, R10,
+      R9, R8, RAX, RCX, RDX, RSI, RDI, ORIG_RAX,
+      RIP, CS, EFLAGS, RSP, SS,
+      _COUNT
+    };
+
+    /// Register for the ARM architecture (ARCH::EM_ARM).
+    enum class ARM {
+      R0 = 0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15,
+      CPSR,
+      _COUNT
+    };
+
+    /// Register for the AARCH64 architecture (ARCH::AARCH64).
+    enum class AARCH64 {
+      X0 = 0, X1, X2, X3, X4, X5, X6, X7, X8, X9, X10, X11, X12, X13, X14, X15,
+      X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28, X29, X30,
+      X31, PC, PSTATE,
+      _COUNT
+    };
+  };
 
-  uint64_t& operator[](REGISTERS reg);
+  public:
+  CorePrStatus(ARCH arch, ELF_CLASS cls, std::string name,
+               uint32_t type, description_t description) :
+    Note(std::move(name), TYPE::CORE_PRSTATUS, type, std::move(description)),
+    arch_(arch), class_(cls)
+  {}
+
+  std::unique_ptr<Note> clone() const override {
+    return std::unique_ptr<CorePrStatus>(new CorePrStatus(*this));
+  }
+
+  /// Return the pr_status_t structure
+  pr_status_t status() const;
+  void status(const pr_status_t& status);
+
+  ARCH architecture() const {
+    return arch_;
+  }
+
+  /// The program counter or an error if not found
+  result<uint64_t> pc() const;
+
+  /// The stack pointer or an error if not found
+  result<uint64_t> sp() const;
+
+  /// The value of the register that holds the return value according to
+  /// the calling convention.
+  result<uint64_t> return_value() const;
+
+  /// Get the value for the given X86 register or return an error
+  result<uint64_t> get(Registers::X86 reg) const;
+  /// Get the value for the given X86_64 register or return an error
+  result<uint64_t> get(Registers::X86_64 reg) const;
+  /// Get the value for the given ARM register or return an error
+  result<uint64_t> get(Registers::ARM reg) const;
+  /// Get the value for the given AARCH64 register or return an error
+  result<uint64_t> get(Registers::AARCH64 reg) const;
+
+  ok_error_t set(Registers::X86 reg, uint64_t value);
+  ok_error_t set(Registers::X86_64 reg, uint64_t value);
+  ok_error_t set(Registers::ARM reg, uint64_t value);
+  ok_error_t set(Registers::AARCH64 reg, uint64_t value);
+
+  /// A list of the register values.
+  /// This list is **guarantee** to be as long as the Registers::ARM::_COUNT or
+  /// empty if it can't be resolved. Thus, one can access a specific register
+  /// with:
+  /// ```cpp
+  /// if (architecture() == ARCH::EM_AARCH64) {
+  ///   auto reg_vals = register_values()
+  ///   if (!reg_vals.empty()) {
+  ///     auto x20 = reg_vals[static_cast<size_t>(Register::AARCH64::X20)]
+  ///   }
+  /// }
+  /// ```
+  std::vector<uint64_t> register_values() const;
+
+  result<uint64_t> operator[](Registers::X86 reg) const {
+    return get(reg);
+  }
+
+  result<uint64_t> operator[](Registers::X86_64 reg) const {
+    return get(reg);
+  }
+
+  result<uint64_t> operator[](Registers::ARM reg) const {
+    return get(reg);
+  }
+
+  result<uint64_t> operator[](Registers::AARCH64 reg) const {
+    return get(reg);
+  }
 
   void dump(std::ostream& os) const override;
-  static std::ostream& dump(std::ostream& os, const timeval_t& time);
-  static std::ostream& dump(std::ostream& os, const siginfo_t& siginfo);
-  static std::ostream& dump(std::ostream& os, const reg_context_t& ctx);
-
   void accept(Visitor& visitor) const override;
 
-  ~CorePrStatus() override;
+  static bool classof(const Note* note) {
+    return note->type() == Note::TYPE::CORE_PRSTATUS;
+  }
 
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const CorePrStatus& note);
+  ~CorePrStatus() override = default;
 
-  protected:
-  template <typename ELF_T>
-  LIEF_LOCAL void parse_();
-
-  template <typename ELF_T>
-  LIEF_LOCAL void build_();
-
-  void parse() override;
-  void build() override;
+  LIEF_API friend
+  std::ostream& operator<<(std::ostream& os, const CorePrStatus& note) {
+    note.dump(os);
+    return os;
+  }
 
   private:
-  CorePrStatus(Note& note);
-
-  std::pair<size_t, size_t> reg_enum_range() const;
-
-  siginfo_t siginfo_;
-  uint16_t  cursig_;
-
-  uint64_t sigpend_;
-  uint64_t sighold_;
-
-  int32_t pid_;
-  int32_t ppid_;
-  int32_t pgrp_;
-  int32_t sid_;
-
-  timeval_t utime_;
-  timeval_t stime_;
-  timeval_t cutime_;
-  timeval_t cstime_;
-
-  reg_context_t ctx_;
+  ARCH arch_ = ARCH::EM_NONE;
+  ELF_CLASS class_ = ELF_CLASS::ELFCLASSNONE;
 };
 
-
-LIEF_API const char* to_string(CorePrStatus::REGISTERS e);
+LIEF_API const char* to_string(CorePrStatus::Registers::X86 e);
+LIEF_API const char* to_string(CorePrStatus::Registers::X86_64 e);
+LIEF_API const char* to_string(CorePrStatus::Registers::ARM e);
+LIEF_API const char* to_string(CorePrStatus::Registers::AARCH64 e);
 
 } // namepsace ELF
 } // namespace LIEF
diff --git a/include/LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp b/include/LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp
index dba93185ee..8a1f883daa 100644
--- a/include/LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp
+++ b/include/LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp
@@ -18,71 +18,51 @@
 
 #include <vector>
 #include <ostream>
-#include <map>
-#include <utility>
+#include <memory>
 
-#include "LIEF/Object.hpp"
 #include "LIEF/visibility.h"
-
-#include "LIEF/ELF/NoteDetails.hpp"
+#include "LIEF/ELF/Note.hpp"
+#include "LIEF/errors.hpp"
 
 namespace LIEF {
 namespace ELF {
 
-class Note;
-class Parser;
-class Builder;
-class Binary;
-
-//! Class representing core siginfo object
-class LIEF_API CoreSigInfo : public NoteDetails {
-
+//! Class representing a core siginfo object
+class LIEF_API CoreSigInfo : public Note {
   public:
-  using NoteDetails::NoteDetails;
-
-  public:
-  static CoreSigInfo make(Note& note);
-
-  CoreSigInfo* clone() const override;
-
-  //! Signal number.
-  int32_t signo() const;
+  std::unique_ptr<Note> clone() const override {
+    return std::unique_ptr<CoreSigInfo>(new CoreSigInfo(*this));
+  }
 
-  //! Signal code.
-  int32_t sigcode() const;
+  /// Signal number of an error if it can't be resolved
+  result<int32_t> signo() const;
+  /// Signal code of an error if it can't be resolved
+  result<int32_t> sigcode() const;
 
-  //! If non-zero, an errno value associated with this signal.
-  int32_t sigerrno() const;
-
-  void signo(int32_t signo);
-  void sigcode(int32_t sigcode);
-  void sigerrno(int32_t sigerrno);
+  /// Signal error number of an error if it can't be resolved
+  result<int32_t> sigerrno() const;
 
+  void signo(uint32_t value);
+  void sigcode(uint32_t value);
+  void sigerrno(uint32_t value);
 
   void dump(std::ostream& os) const override;
-
   void accept(Visitor& visitor) const override;
 
-  ~CoreSigInfo() override;
+  ~CoreSigInfo() override = default;
 
-  LIEF_API friend std::ostream& operator<<(std::ostream& os, const CoreSigInfo& note);
+  static bool classof(const Note* note) {
+    return note->type() == Note::TYPE::CORE_SIGINFO;
+  }
 
+  LIEF_API friend
+  std::ostream& operator<<(std::ostream& os, const CoreSigInfo& note) {
+    note.dump(os);
+    return os;
+  }
   protected:
-  void parse() override;
-  void build() override;
-
-  private:
-  CoreSigInfo(Note& note);
-  struct siginfo_t {
-    int32_t si_signo;
-    int32_t si_code;
-    int32_t si_errno;
-  };
-
-  siginfo_t siginfo_;
+  using Note::Note;
 };
-
-
 } // namepsace ELF
 } // namespace LIEF
 
diff --git a/include/LIEF/ELF/Parser.hpp b/include/LIEF/ELF/Parser.hpp
index bd2525e90c..f4f4bddf55 100644
--- a/include/LIEF/ELF/Parser.hpp
+++ b/include/LIEF/ELF/Parser.hpp
@@ -38,6 +38,7 @@ class Section;
 class Binary;
 class Segment;
 class Symbol;
+class Note;
 
 //! Class which parses and transforms an ELF file into a ELF::Binary object
 class LIEF_API Parser : public LIEF::Parser {
@@ -51,7 +52,6 @@ class LIEF_API Parser : public LIEF::Parser {
   static constexpr uint32_t NB_MAX_RELOCATIONS     = 3000000;
   static constexpr uint32_t NB_MAX_DYNAMIC_ENTRIES = 1000;
   static constexpr uint32_t NB_MAX_MASKWORD        = 512;
-  static constexpr uint32_t MAX_NOTE_DESCRIPTION   = 1_MB;
   static constexpr uint32_t MAX_SECTION_SIZE       = 2_GB;
   static constexpr uint32_t MAX_SEGMENT_SIZE       = 3_GB;
 
@@ -224,6 +224,8 @@ class LIEF_API Parser : public LIEF::Parser {
   //! Parse Note (.gnu.note)
   ok_error_t parse_notes(uint64_t offset, uint64_t size);
 
+  std::unique_ptr<Note> get_note(uint32_t type, std::string name, std::vector<uint8_t> desc_bytes);
+
   //! Parse Symbols's SYSV hash
   ok_error_t parse_symbol_sysv_hash(uint64_t offset);
 
diff --git a/include/LIEF/ELF/enums.hpp b/include/LIEF/ELF/enums.hpp
index 204567a0f9..a291b7ddb8 100644
--- a/include/LIEF/ELF/enums.hpp
+++ b/include/LIEF/ELF/enums.hpp
@@ -885,64 +885,6 @@ enum {
   VER_NEED_CURRENT = 1
 };
 
-
-enum class AUX_TYPE: size_t  {
-
-   AT_NULL          = 0,     /**< End of vector */
-   AT_IGNORE        = 1,     /**< Entry should be ignored */
-   AT_EXECFD        = 2,     /**< File descriptor of program */
-   AT_PHDR          = 3,     /**< Program headers for program */
-   AT_PHENT         = 4,     /**< Size of program header entry */
-   AT_PHNUM         = 5,     /**< Number of program headers */
-   AT_PAGESZ        = 6,     /**< System page size */
-   AT_BASE          = 7,     /**< Base address of interpreter */
-   AT_FLAGS         = 8,     /**< Flags */
-   AT_ENTRY         = 9,     /**< Entry point of program */
-   AT_NOTELF        = 10,    /**< Program is not ELF */
-   AT_UID           = 11,    /**< Real uid */
-   AT_EUID          = 12,    /**< Effective uid */
-   AT_GID           = 13,    /**< Real gid */
-   AT_EGID          = 14,    /**< Effective gid */
-   AT_CLKTCK        = 17,    /**< Frequency of times() */
-
-   /* Some more special a_type values describing the hardware.  */
-
-   AT_PLATFORM      = 15,    /**< String identifying platform.  */
-   AT_HWCAP         = 16,    /**< Machine dependent hints about processor capabilities.  */
-
-   /* This entry gives some information about the FPU initialization
-      performed by the kernel. */
-
-   AT_FPUCW        = 18,    /**< Used FPU control word.  */
-
-   /* Cache block sizes. */
-   AT_DCACHEBSIZE   = 19,    /**< Data cache block size.  */
-   AT_ICACHEBSIZE   = 20,    /**< Instruction cache block size.  */
-   AT_UCACHEBSIZE   = 21,    /**< Unified cache block size.  */
-
-   /* A special ignored value for PPC, used by the kernel to control the
-      interpretation of the AUXV. Must be > 16.  */
-
-   AT_IGNOREPPC     = 22,    /**< Entry should be ignored.  */
-   AT_SECURE        = 23,    /**< Boolean, was exec setuid-like?  */
-   AT_BASE_PLATFORM = 24,    /**< String identifying real platforms.*/
-   AT_RANDOM        = 25,    /**< Address of 16 random bytes.  */
-   AT_HWCAP2        = 26,    /**< Extension of AT_HWCAP.  */
-   AT_EXECFN        = 31,    /**< Filename of executable.  */
-
-   /* Pointer to the global system page used for system calls and other
-      nice things. */
-   AT_SYSINFO       = 32,
-   AT_SYSINFO_EHDR  = 33,
-
-   /* Shapes of the caches.  Bits 0-3 contains associativity; bits 4-7 contains
-      log2 of line size; mask those to get cache size.  */
-   AT_L1I_CACHESHAPE  = 34,
-   AT_L1D_CACHESHAPE  = 35,
-   AT_L2_CACHESHAPE   = 36,
-   AT_L3_CACHESHAPE   = 37
-};
-
 /** Methods that can be used by the LIEF::ELF::Parser
     to count the number of dynamic symbols */
 enum class DYNSYM_COUNT_METHODS: size_t  {
@@ -952,53 +894,6 @@ enum class DYNSYM_COUNT_METHODS: size_t  {
   COUNT_RELOCATIONS = 3, /**< Count based on PLT/GOT relocations (very reliable but not accurate) */
 };
 
-enum class NOTE_TYPES: size_t  {
-  NT_UNKNOWN                  = 0,
-  NT_GNU_ABI_TAG              = 1,
-  NT_GNU_HWCAP                = 2,
-  NT_GNU_BUILD_ID             = 3,
-  NT_GNU_GOLD_VERSION         = 4,
-  NT_GNU_PROPERTY_TYPE_0      = 5,
-  NT_GNU_BUILD_ATTRIBUTE_OPEN = 0x100,
-  NT_GNU_BUILD_ATTRIBUTE_FUNC = 0x101,
-  NT_CRASHPAD                 = 0x4f464e49,
-};
-
-enum class NOTE_TYPES_CORE: size_t  {
-  NT_CORE_UNKNOWN     = 0,
-  NT_PRSTATUS         = 1,
-  NT_PRFPREG          = 2,
-  NT_PRPSINFO         = 3,
-  NT_TASKSTRUCT       = 4,
-  NT_AUXV             = 6,
-  NT_SIGINFO          = 0x53494749,
-  NT_FILE             = 0x46494c45,
-  NT_PRXFPREG         = 0x46e62b7f,
-
-  NT_ARM_VFP          = 0x400,
-  NT_ARM_TLS          = 0x401,
-  NT_ARM_HW_BREAK     = 0x402,
-  NT_ARM_HW_WATCH     = 0x403,
-  NT_ARM_SYSTEM_CALL  = 0x404,
-  NT_ARM_SVE          = 0x405,
-
-  NT_386_TLS          = 0x200,
-  NT_386_IOPERM       = 0x201,
-  NT_386_XSTATE       = 0x202,
-
-};
-
-
-enum class NOTE_ABIS: size_t  {
-  ELF_NOTE_UNKNOWN     = ~(unsigned int)(0),
-  ELF_NOTE_OS_LINUX    = 0,
-  ELF_NOTE_OS_GNU      = 1,
-  ELF_NOTE_OS_SOLARIS2 = 2,
-  ELF_NOTE_OS_FREEBSD  = 3,
-  ELF_NOTE_OS_NETBSD   = 4,
-  ELF_NOTE_OS_SYLLABLE = 5,
-};
-
 enum class RELOCATION_PURPOSES: size_t  {
   RELOC_PURPOSE_NONE    = 0,
   RELOC_PURPOSE_PLTGOT  = 1,
diff --git a/include/LIEF/ELF/hash.hpp b/include/LIEF/ELF/hash.hpp
index 5a24393e46..de74afd209 100644
--- a/include/LIEF/ELF/hash.hpp
+++ b/include/LIEF/ELF/hash.hpp
@@ -82,8 +82,7 @@ class LIEF_API Hash : public LIEF::Hash {
   void visit(const SymbolVersionRequirement& svr)   override;
   void visit(const SymbolVersionDefinition& svd)    override;
   void visit(const Note& note)                      override;
-  void visit(const NoteDetails& details)            override;
-  void visit(const AndroidNote& note)               override;
+  void visit(const AndroidIdent& note)              override;
   void visit(const NoteAbi& note)                   override;
   void visit(const CorePrPsInfo& pinfo)             override;
   void visit(const CorePrStatus& pstatus)           override;
diff --git a/include/LIEF/Visitor.hpp b/include/LIEF/Visitor.hpp
index 4dadb6bc25..613f15d1e8 100644
--- a/include/LIEF/Visitor.hpp
+++ b/include/LIEF/Visitor.hpp
@@ -131,7 +131,7 @@ LIEF_ELF_FORWARD(SymbolVersionAux)
 LIEF_ELF_FORWARD(SymbolVersionAuxRequirement)
 LIEF_ELF_FORWARD(Note)
 LIEF_ELF_FORWARD(NoteDetails)
-LIEF_ELF_FORWARD(AndroidNote)
+LIEF_ELF_FORWARD(AndroidIdent)
 LIEF_ELF_FORWARD(NoteAbi)
 LIEF_ELF_FORWARD(CorePrPsInfo)
 LIEF_ELF_FORWARD(CorePrStatus)
@@ -269,7 +269,7 @@ class LIEF_API Visitor {
   LIEF_ELF_VISITABLE(SymbolVersionAuxRequirement)
   LIEF_ELF_VISITABLE(Note)
   LIEF_ELF_VISITABLE(NoteDetails)
-  LIEF_ELF_VISITABLE(AndroidNote)
+  LIEF_ELF_VISITABLE(AndroidIdent)
   LIEF_ELF_VISITABLE(NoteAbi)
   LIEF_ELF_VISITABLE(CorePrPsInfo)
   LIEF_ELF_VISITABLE(CorePrStatus)
diff --git a/src/ELF/Binary.cpp b/src/ELF/Binary.cpp
index dc96643d02..ab976c4705 100644
--- a/src/ELF/Binary.cpp
+++ b/src/ELF/Binary.cpp
@@ -309,16 +309,16 @@ void Binary::remove(const Note& note) {
 
   if (it_note == std::end(notes_)) {
     LIEF_WARN("Can't find the note with the type {}. It can't be removed!",
-              to_string(static_cast<NOTE_TYPES>(note.type())));
+              to_string(static_cast<Note::TYPE>(note.type())));
     return;
   }
   notes_.erase(it_note);
 }
 
-void Binary::remove(NOTE_TYPES type) {
+void Binary::remove(Note::TYPE type) {
   for (auto it = std::begin(notes_); it != std::end(notes_);) {
     std::unique_ptr<Note>& n = *it;
-    if (static_cast<NOTE_TYPES>(n->type()) == type) {
+    if (n->type() == type) {
       n.reset(nullptr);
       it = notes_.erase(it);
     } else {
@@ -1864,10 +1864,10 @@ Segment* Binary::get(SEGMENT_TYPES type) {
   return const_cast<Segment*>(static_cast<const Binary*>(this)->get(type));
 }
 
-const Note* Binary::get(NOTE_TYPES type) const {
+const Note* Binary::get(Note::TYPE type) const {
   const auto it_note = std::find_if(std::begin(notes_), std::end(notes_),
                               [type] (const std::unique_ptr<Note>& note) {
-                                return static_cast<NOTE_TYPES>(note->type()) == type;
+                                return note->type() == type;
                               });
   if (it_note == std::end(notes_)) {
     return nullptr;
@@ -1877,7 +1877,7 @@ const Note* Binary::get(NOTE_TYPES type) const {
 }
 
 
-Note* Binary::get(NOTE_TYPES type) {
+Note* Binary::get(Note::TYPE type) {
   return const_cast<Note*>(static_cast<const Binary*>(this)->get(type));
 }
 
@@ -1902,7 +1902,7 @@ bool Binary::has(SEGMENT_TYPES type) const {
   return get(type) != nullptr;
 }
 
-bool Binary::has(NOTE_TYPES type) const {
+bool Binary::has(Note::TYPE type) const {
   return get(type) != nullptr;
 }
 
@@ -1969,7 +1969,6 @@ Binary::it_notes Binary::notes() {
   return notes_;
 }
 
-
 void Binary::accept(LIEF::Visitor& visitor) const {
   visitor.visit(*this);
 }
@@ -3347,7 +3346,7 @@ Binary& Binary::operator-=(const Note& note) {
   return *this;
 }
 
-Binary& Binary::operator-=(NOTE_TYPES type) {
+Binary& Binary::operator-=(Note::TYPE type) {
   remove(type);
   return *this;
 }
@@ -3370,11 +3369,11 @@ const DynamicEntry* Binary::operator[](DYNAMIC_TAGS tag) const {
   return get(tag);
 }
 
-Note* Binary::operator[](NOTE_TYPES type) {
+Note* Binary::operator[](Note::TYPE type) {
   return get(type);
 }
 
-const Note* Binary::operator[](NOTE_TYPES type) const {
+const Note* Binary::operator[](Note::TYPE type) const {
   return get(type);
 }
 
diff --git a/src/ELF/Builder.cpp b/src/ELF/Builder.cpp
index 4e0856e92f..70a5fa611b 100644
--- a/src/ELF/Builder.cpp
+++ b/src/ELF/Builder.cpp
@@ -26,7 +26,6 @@
 #include "LIEF/ELF/Symbol.hpp"
 #include "LIEF/ELF/Note.hpp"
 
-#include "notes_utils.hpp"
 
 #include "Builder.tcc"
 
@@ -186,76 +185,54 @@ ok_error_t Builder::build_empty_symbol_gnuhash() {
   return ok();
 }
 
-
-
-ok_error_t Builder::build(const Note& note, std::set<Section*>& sections) {
-  using value_t = typename note_to_section_map_t::value_type;
-
+ok_error_t Builder::update_note_section(const Note& note,
+                                        std::set<const Note*>& notes)
+{
   Segment* segment_note = binary_->get(SEGMENT_TYPES::PT_NOTE);
   if (segment_note == nullptr) {
     LIEF_ERR("Can't find the PT_NOTE segment");
     return make_error_code(lief_errors::not_found);
   }
-  const auto& note_to_section_map = get_note_to_section();
-  auto range_secname = note_to_section_map.equal_range(note.type());
 
-  const bool known_section = (range_secname.first != range_secname.second);
-
-  const auto it_section_name = std::find_if(
-      range_secname.first, range_secname.second,
-      [this] (value_t p) {
-        return binary_->has_section(p.second);
-      });
+  auto res_secname = Note::type_to_section(note.type());
+  if (!res_secname) {
+    LIEF_ERR("LIEF doesn't know the section name for note: '{}'",
+             to_string(note.type()));
+    return make_error_code(lief_errors::not_supported);
+  }
 
-  bool has_section = (it_section_name != range_secname.second);
+  Section* note_sec = binary_->get_section(*res_secname);
+  if (!note_sec) {
+    LIEF_ERR("Section {} not present", *res_secname);
+    return make_error_code(lief_errors::not_found);
+  }
 
-  std::string section_name;
-  if (has_section) {
-    section_name = it_section_name->second;
-  } else if (known_section) {
-    section_name = range_secname.first->second;
-  } else {
-    section_name = fmt::format(".note.{:x}", static_cast<uint32_t>(note.type()));
+  const auto& offset_map = static_cast<ExeLayout*>(layout_.get())->note_off_map();
+  auto it_offset = offset_map.find(&note);
+  if (it_offset == offset_map.end()) {
+    LIEF_ERR("Can't find offset for note '{}'", to_string(note.type()));
+    return make_error_code(lief_errors::not_found);
   }
 
-  const std::unordered_map<const Note*, size_t>& offset_map = reinterpret_cast<ExeLayout*>(layout_.get())->note_off_map();
-  const auto& it_offset = offset_map.find(&note);
+  if (!notes.insert(&note).second) {
+    LIEF_DEBUG("Note '{}' has already been processed", to_string(note.type()));
+    note_sec->virtual_address(0);
+    note_sec->size(note_sec->size() + note.size());
+    return ok_t();
+  }
 
-  // Link section and notes
-  if (binary_->has(note.type()) && has_section) {
-    if (it_offset == std::end(offset_map)) {
-      LIEF_ERR("Can't find {}", to_string(note.type()));
-      return make_error_code(lief_errors::not_found);
-    }
-    const size_t note_offset = it_offset->second;
-    Section* section = binary_->get_section(section_name);
-    if (section == nullptr) {
-      LIEF_ERR("Can't find section {}", section_name);
-      return make_error_code(lief_errors::not_found);
-    }
-    if (sections.insert(section).second) {
-      section->offset(segment_note->file_offset() + note_offset);
-      section->size(note.size());
-      section->virtual_address(segment_note->virtual_address() + note_offset);
-      // Special process for GNU_PROPERTY:
-      // This kind of note has a dedicated segment while others don't
-      // Therefore, when relocating this note, we need
-      // to update the segment as well.
-      if (note.type() == NOTE_TYPES::NT_GNU_PROPERTY_TYPE_0 &&
-          binary_->has(SEGMENT_TYPES::PT_GNU_PROPERTY))
-      {
-        Segment* seg = binary_->get(SEGMENT_TYPES::PT_GNU_PROPERTY);
-        if (seg == nullptr) return ok(); // Should not append as it is checked by has(...)
-
-        seg->file_offset(section->offset());
-        seg->physical_size(section->size());
-        seg->virtual_address(section->virtual_address());
-        seg->physical_address(section->virtual_address());
-        seg->virtual_size(section->size());
-      }
-    } else /* We already handled this kind of note */ {
-      section->virtual_address(0);
-      section->size(section->size() + note.size());
+  const uint64_t note_offset = it_offset->second;
+  note_sec->offset(segment_note->file_offset() + note_offset);
+  note_sec->size(note.size());
+  note_sec->virtual_address(segment_note->virtual_address() + note_offset);
+
+  if (note.type() == Note::TYPE::GNU_PROPERTY_TYPE_0) {
+    if (Segment* seg = binary_->get(SEGMENT_TYPES::PT_GNU_PROPERTY)) {
+      seg->file_offset(note_sec->offset());
+      seg->physical_size(note_sec->size());
+      seg->virtual_address(note_sec->virtual_address());
+      seg->physical_address(note_sec->virtual_address());
+      seg->virtual_size(note_sec->size());
     }
   }
   return ok();
diff --git a/src/ELF/Builder.tcc b/src/ELF/Builder.tcc
index d223408dd2..0db631873c 100644
--- a/src/ELF/Builder.tcc
+++ b/src/ELF/Builder.tcc
@@ -1720,22 +1720,16 @@ ok_error_t Builder::build_notes() {
   }
   // Clear the original content of the segment
   note_segment->content(std::vector<uint8_t>(note_segment->physical_size(), 0));
-
+  // Write the cached note
   note_segment->content(static_cast<ExeLayout*>(layout_.get())->raw_notes());
 
-  //TODO: .note.netbds etc
   if (binary_->header().file_type() == E_TYPE::ET_CORE) {
-    LIEF_WARN("Building note for coredump is not supported yet");
-    return make_error_code(lief_errors::not_supported);
+    return ok_t();
   }
 
-  // Track the list of the sections we wrote
-  // NOTE(romain): it is only used by the function build() itself but
-  //               to avoid creating an instance field, we create this
-  //               variable in the scode of this function
-  std::set<Section*> sections;
-  for (Note& note: binary_->notes()) {
-    build(note, sections);
+  std::set<const Note*> notes;
+  for (const Note& note: binary_->notes()) {
+    update_note_section(note, notes);
   }
   return ok();
 }
diff --git a/src/ELF/CMakeLists.txt b/src/ELF/CMakeLists.txt
index 0e925729b3..7c75e91dc2 100644
--- a/src/ELF/CMakeLists.txt
+++ b/src/ELF/CMakeLists.txt
@@ -18,7 +18,6 @@ target_sources(LIB_LIEF PRIVATE
   Header.cpp
   Layout.cpp
   Note.cpp
-  NoteDetails.cpp
   Parser.cpp
   Parser.tcc
   Relocation.cpp
@@ -33,7 +32,6 @@ target_sources(LIB_LIEF PRIVATE
   SymbolVersionRequirement.cpp
   SysvHash.cpp
   hash.cpp
-  note_utils.cpp
   utils.cpp
   json_api.cpp)
 
diff --git a/src/ELF/EnumToString.cpp b/src/ELF/EnumToString.cpp
index ea4ae69203..1fc9fea9b2 100644
--- a/src/ELF/EnumToString.cpp
+++ b/src/ELF/EnumToString.cpp
@@ -1293,68 +1293,6 @@ const char* to_string(DYNSYM_COUNT_METHODS e) {
   return it == enumStrings.end() ? "UNDEFINED" : it->second;
 }
 
-
-const char* to_string(NOTE_TYPES e) {
-  CONST_MAP(NOTE_TYPES, const char*, 9) enumStrings {
-    { NOTE_TYPES::NT_UNKNOWN,                  "UNKNOWN"},
-    { NOTE_TYPES::NT_GNU_ABI_TAG,              "ABI_TAG"},
-    { NOTE_TYPES::NT_GNU_HWCAP,                "HWCAP"},
-    { NOTE_TYPES::NT_GNU_BUILD_ID,             "BUILD_ID"},
-    { NOTE_TYPES::NT_GNU_GOLD_VERSION,         "GOLD_VERSION"},
-    { NOTE_TYPES::NT_GNU_PROPERTY_TYPE_0,      "PROPERTY_TYPE_0"},
-    { NOTE_TYPES::NT_GNU_BUILD_ATTRIBUTE_OPEN, "GNU_BUILD_ATTRIBUTE_OPEN"},
-    { NOTE_TYPES::NT_GNU_BUILD_ATTRIBUTE_FUNC, "GNU_BUILD_ATTRIBUTE_FUNC"},
-    { NOTE_TYPES::NT_CRASHPAD,                 "CRASHPAD"},
-  };
-
-  const auto it = enumStrings.find(e);
-  return it == enumStrings.end() ? "UNDEFINED" : it->second;
-}
-
-
-const char* to_string(NOTE_TYPES_CORE e) {
-  CONST_MAP(NOTE_TYPES_CORE, const char*, 17) enumStrings {
-    { NOTE_TYPES_CORE::NT_CORE_UNKNOWN,     "UNKNOWN"},
-    { NOTE_TYPES_CORE::NT_PRSTATUS,         "PRSTATUS"},
-    { NOTE_TYPES_CORE::NT_PRFPREG,          "PRFPREG"},
-    { NOTE_TYPES_CORE::NT_PRPSINFO,         "PRPSINFO"},
-    { NOTE_TYPES_CORE::NT_TASKSTRUCT,       "TASKSTRUCT"},
-    { NOTE_TYPES_CORE::NT_AUXV,             "AUXV"},
-    { NOTE_TYPES_CORE::NT_SIGINFO,          "SIGINFO"},
-    { NOTE_TYPES_CORE::NT_FILE,             "FILE"},
-
-    { NOTE_TYPES_CORE::NT_ARM_VFP,          "ARM_VFP"},
-    { NOTE_TYPES_CORE::NT_ARM_TLS,          "ARM_TLS"},
-    { NOTE_TYPES_CORE::NT_ARM_HW_BREAK,     "ARM_HW_BREAK"},
-    { NOTE_TYPES_CORE::NT_ARM_HW_WATCH,     "ARM_HW_WATCH"},
-    { NOTE_TYPES_CORE::NT_ARM_SYSTEM_CALL,  "ARM_SYSTEM_CALL"},
-    { NOTE_TYPES_CORE::NT_ARM_SVE,          "ARM_SVE"},
-
-    { NOTE_TYPES_CORE::NT_386_TLS,          "I386_TLS"},
-    { NOTE_TYPES_CORE::NT_386_IOPERM,       "I386_IOPERM"},
-    { NOTE_TYPES_CORE::NT_386_XSTATE,       "I386_XSTATE"},
-  };
-
-  const auto it = enumStrings.find(e);
-  return it == enumStrings.end() ? "UNKNOWN" : it->second;
-}
-
-
-const char* to_string(NOTE_ABIS e) {
-  CONST_MAP(NOTE_ABIS, const char*, 7) enumStrings {
-    { NOTE_ABIS::ELF_NOTE_UNKNOWN,     "UNKNOWN"},
-    { NOTE_ABIS::ELF_NOTE_OS_LINUX,    "LINUX"},
-    { NOTE_ABIS::ELF_NOTE_OS_GNU,      "GNU"},
-    { NOTE_ABIS::ELF_NOTE_OS_SOLARIS2, "SOLARIS2"},
-    { NOTE_ABIS::ELF_NOTE_OS_FREEBSD,  "FREEBSD"},
-    { NOTE_ABIS::ELF_NOTE_OS_NETBSD,   "NETBSD"},
-    { NOTE_ABIS::ELF_NOTE_OS_SYLLABLE, "SYLLABLE"},
-  };
-
-  const auto it = enumStrings.find(e);
-  return it == enumStrings.end() ? "UNDEFINED" : it->second;
-}
-
 const char* to_string(RELOCATION_PURPOSES e) {
   CONST_MAP(RELOCATION_PURPOSES, const char*, 4) enumStrings {
     { RELOCATION_PURPOSES::RELOC_PURPOSE_NONE,    "NONE"},
@@ -1579,47 +1517,6 @@ const char* to_string(ELF_SEGMENT_FLAGS e) {
 }
 
 
-const char* to_string(AUX_TYPE e) {
-  CONST_MAP(AUX_TYPE, const char*, 32) enum_strings {
-    { AUX_TYPE::AT_NULL, "NULL" },
-    { AUX_TYPE::AT_IGNORE, "IGNORE" },
-    { AUX_TYPE::AT_EXECFD, "EXECFD" },
-    { AUX_TYPE::AT_PHDR, "PHDR" },
-    { AUX_TYPE::AT_PHENT, "PHENT" },
-    { AUX_TYPE::AT_PHNUM, "PHNUM" },
-    { AUX_TYPE::AT_PAGESZ, "PAGESZ" },
-    { AUX_TYPE::AT_BASE, "BASE" },
-    { AUX_TYPE::AT_FLAGS, "FLAGS" },
-    { AUX_TYPE::AT_ENTRY, "ENTRY" },
-    { AUX_TYPE::AT_NOTELF, "NOTELF" },
-    { AUX_TYPE::AT_UID, "UID" },
-    { AUX_TYPE::AT_EUID, "EUID" },
-    { AUX_TYPE::AT_GID, "GID" },
-    { AUX_TYPE::AT_EGID, "EGID" },
-    { AUX_TYPE::AT_CLKTCK, "CKLTCK" },
-    { AUX_TYPE::AT_PLATFORM, "PLATFORM" },
-    { AUX_TYPE::AT_HWCAP, "HWCAP" },
-    { AUX_TYPE::AT_HWCAP2, "HWCAP2" },
-    { AUX_TYPE::AT_FPUCW, "FPUCW" },
-    { AUX_TYPE::AT_DCACHEBSIZE, "DCACHEBSIZE" },
-    { AUX_TYPE::AT_ICACHEBSIZE, "ICACHEBSIZE" },
-    { AUX_TYPE::AT_UCACHEBSIZE, "UCACHEBSIZE" },
-    { AUX_TYPE::AT_IGNOREPPC, "IGNOREPPC" },
-    { AUX_TYPE::AT_SECURE, "SECURE" },
-    { AUX_TYPE::AT_BASE_PLATFORM, "BASE_PLATFORM" },
-    { AUX_TYPE::AT_RANDOM, "RANDOM" },
-    { AUX_TYPE::AT_EXECFN, "EXECFN" },
-    { AUX_TYPE::AT_SYSINFO, "SYSINFO" },
-    { AUX_TYPE::AT_SYSINFO_EHDR, "SYSINFO_EHDR" },
-    { AUX_TYPE::AT_L1I_CACHESHAPE, "L1I_CACHESHAPE" },
-    { AUX_TYPE::AT_L1D_CACHESHAPE, "L1D_CACHESHAPE" },
-  };
-
-  const auto it = enum_strings.find(e);
-  return it == enum_strings.end() ? "UNDEFINED" : it->second;
-}
-
-
 const char* to_string(ELF_SYMBOL_VISIBILITY e) {
   CONST_MAP(ELF_SYMBOL_VISIBILITY, const char*, 4) enum_strings {
     { ELF_SYMBOL_VISIBILITY::STV_DEFAULT,   "DEFAULT"   },
@@ -1632,119 +1529,5 @@ const char* to_string(ELF_SYMBOL_VISIBILITY e) {
   return it == enum_strings.end() ? "UNDEFINED" : it->second;
 }
 
-
-const char* to_string(CorePrStatus::REGISTERS e) {
-  CONST_MAP(CorePrStatus::REGISTERS, const char*, 90) enum_strings {
-    { CorePrStatus::REGISTERS::UNKNOWN,     "UNKNOWN"   },
-
-    // X86
-    // ===
-    { CorePrStatus::REGISTERS::X86_EBX,     "X86_EBX"    },
-    { CorePrStatus::REGISTERS::X86_ECX,     "X86_ECX"    },
-    { CorePrStatus::REGISTERS::X86_EDX,     "X86_EDX"    },
-    { CorePrStatus::REGISTERS::X86_ESI,     "X86_ESI"    },
-    { CorePrStatus::REGISTERS::X86_EDI,     "X86_EDI"    },
-    { CorePrStatus::REGISTERS::X86_EBP,     "X86_EBP"    },
-    { CorePrStatus::REGISTERS::X86_EAX,     "X86_EAX"    },
-    { CorePrStatus::REGISTERS::X86_DS,      "X86_DS"     },
-    { CorePrStatus::REGISTERS::X86_ES,      "X86_ES"     },
-    { CorePrStatus::REGISTERS::X86_FS,      "X86_FS"     },
-    { CorePrStatus::REGISTERS::X86_GS,      "X86_GS"     },
-    { CorePrStatus::REGISTERS::X86__,       "X86__"      },
-    { CorePrStatus::REGISTERS::X86_EIP,     "X86_EIP"    },
-    { CorePrStatus::REGISTERS::X86_CS,      "X86_CS"     },
-    { CorePrStatus::REGISTERS::X86_EFLAGS,  "X86_EFLAGS" },
-    { CorePrStatus::REGISTERS::X86_ESP,     "X86_ESP"    },
-    { CorePrStatus::REGISTERS::X86_SS,      "X86_SS"     },
-
-
-    { CorePrStatus::REGISTERS::X86_64_R15,    "X86_64_R15"    },
-    { CorePrStatus::REGISTERS::X86_64_R14,    "X86_64_R14"    },
-    { CorePrStatus::REGISTERS::X86_64_R13,    "X86_64_R13"    },
-    { CorePrStatus::REGISTERS::X86_64_R12,    "X86_64_R12"    },
-    { CorePrStatus::REGISTERS::X86_64_RBP,    "X86_64_RBP"    },
-    { CorePrStatus::REGISTERS::X86_64_RBX,    "X86_64_RBX"    },
-    { CorePrStatus::REGISTERS::X86_64_R11,    "X86_64_R11"    },
-    { CorePrStatus::REGISTERS::X86_64_R10,    "X86_64_R10"    },
-    { CorePrStatus::REGISTERS::X86_64_R9,     "X86_64_R9"     },
-    { CorePrStatus::REGISTERS::X86_64_R8,     "X86_64_R8"     },
-    { CorePrStatus::REGISTERS::X86_64_RAX,    "X86_64_RAX"    },
-    { CorePrStatus::REGISTERS::X86_64_RCX,    "X86_64_RCX"    },
-    { CorePrStatus::REGISTERS::X86_64_RDX,    "X86_64_RDX"    },
-    { CorePrStatus::REGISTERS::X86_64_RSI,    "X86_64_RSI"    },
-    { CorePrStatus::REGISTERS::X86_64_RDI,    "X86_64_RDI"    },
-    { CorePrStatus::REGISTERS::X86_64__,      "X86_64__"      },
-    { CorePrStatus::REGISTERS::X86_64_RIP,    "X86_64_RIP"    },
-    { CorePrStatus::REGISTERS::X86_64_CS,     "X86_64_CS"     },
-    { CorePrStatus::REGISTERS::X86_64_EFLAGS, "X86_64_EFLAGS" },
-    { CorePrStatus::REGISTERS::X86_64_RSP,    "X86_64_RSP"    },
-    { CorePrStatus::REGISTERS::X86_64_SS,     "X86_64_SS"     },
-
-    { CorePrStatus::REGISTERS::ARM_R0,  "ARM_R0"  },
-    { CorePrStatus::REGISTERS::ARM_R1,  "ARM_R1"  },
-    { CorePrStatus::REGISTERS::ARM_R2,  "ARM_R2"  },
-    { CorePrStatus::REGISTERS::ARM_R3,  "ARM_R3"  },
-    { CorePrStatus::REGISTERS::ARM_R4,  "ARM_R4"  },
-    { CorePrStatus::REGISTERS::ARM_R5,  "ARM_R5"  },
-    { CorePrStatus::REGISTERS::ARM_R6,  "ARM_R6"  },
-    { CorePrStatus::REGISTERS::ARM_R7,  "ARM_R7"  },
-    { CorePrStatus::REGISTERS::ARM_R8,  "ARM_R8"  },
-    { CorePrStatus::REGISTERS::ARM_R9,  "ARM_R9"  },
-    { CorePrStatus::REGISTERS::ARM_R10, "ARM_R10" },
-    { CorePrStatus::REGISTERS::ARM_R11, "ARM_R11" },
-    { CorePrStatus::REGISTERS::ARM_R12, "ARM_R12" },
-    { CorePrStatus::REGISTERS::ARM_R13, "ARM_R13" },
-    { CorePrStatus::REGISTERS::ARM_R14, "ARM_R14" },
-    { CorePrStatus::REGISTERS::ARM_R15, "ARM_R15" },
-    { CorePrStatus::REGISTERS::ARM_CPSR, "ARM_CPSR" },
-
-    { CorePrStatus::REGISTERS::AARCH64_X0,  "AARCH64_X0"   },
-    { CorePrStatus::REGISTERS::AARCH64_X1,  "AARCH64_X1"   },
-    { CorePrStatus::REGISTERS::AARCH64_X2,  "AARCH64_X2"   },
-    { CorePrStatus::REGISTERS::AARCH64_X3,  "AARCH64_X3"   },
-    { CorePrStatus::REGISTERS::AARCH64_X4,  "AARCH64_X4"   },
-    { CorePrStatus::REGISTERS::AARCH64_X5,  "AARCH64_X5"   },
-    { CorePrStatus::REGISTERS::AARCH64_X6,  "AARCH64_X6"   },
-    { CorePrStatus::REGISTERS::AARCH64_X7,  "AARCH64_X7"   },
-    { CorePrStatus::REGISTERS::AARCH64_X8,  "AARCH64_X8"   },
-    { CorePrStatus::REGISTERS::AARCH64_X9,  "AARCH64_X9"   },
-    { CorePrStatus::REGISTERS::AARCH64_X10, "AARCH64_X10"  },
-    { CorePrStatus::REGISTERS::AARCH64_X11, "AARCH64_X11"  },
-    { CorePrStatus::REGISTERS::AARCH64_X12, "AARCH64_X12"  },
-    { CorePrStatus::REGISTERS::AARCH64_X13, "AARCH64_X13"  },
-    { CorePrStatus::REGISTERS::AARCH64_X14, "AARCH64_X14"  },
-    { CorePrStatus::REGISTERS::AARCH64_X15, "AARCH64_X15"  },
-    { CorePrStatus::REGISTERS::AARCH64_X16, "AARCH64_X16"  },
-    { CorePrStatus::REGISTERS::AARCH64_X17, "AARCH64_X17"  },
-    { CorePrStatus::REGISTERS::AARCH64_X18, "AARCH64_X18"  },
-    { CorePrStatus::REGISTERS::AARCH64_X19, "AARCH64_X19"  },
-    { CorePrStatus::REGISTERS::AARCH64_X20, "AARCH64_X20"  },
-    { CorePrStatus::REGISTERS::AARCH64_X21, "AARCH64_X21"  },
-    { CorePrStatus::REGISTERS::AARCH64_X22, "AARCH64_X22"  },
-    { CorePrStatus::REGISTERS::AARCH64_X23, "AARCH64_X23"  },
-    { CorePrStatus::REGISTERS::AARCH64_X24, "AARCH64_X24"  },
-    { CorePrStatus::REGISTERS::AARCH64_X25, "AARCH64_X25"  },
-    { CorePrStatus::REGISTERS::AARCH64_X26, "AARCH64_X26"  },
-    { CorePrStatus::REGISTERS::AARCH64_X27, "AARCH64_X27"  },
-    { CorePrStatus::REGISTERS::AARCH64_X28, "AARCH64_X28"  },
-    { CorePrStatus::REGISTERS::AARCH64_X29, "AARCH64_X29"  },
-    { CorePrStatus::REGISTERS::AARCH64_X30, "AARCH64_X30"  },
-    { CorePrStatus::REGISTERS::AARCH64_X31, "AARCH64_X31"  },
-    { CorePrStatus::REGISTERS::AARCH64_PC,  "AARCH64_PC"   },
-    { CorePrStatus::REGISTERS::AARCH64__,   "AARCH64__"    },
-
-  };
-
-  const auto it = enum_strings.find(e);
-  return it == enum_strings.end() ? "UNKNOWN" : it->second;
-}
-
-
-
-
-
 } // namespace ELF
 } // namespace LIEF
-
-
-
diff --git a/src/ELF/ExeLayout.hpp b/src/ELF/ExeLayout.hpp
index 0015fdacad..8c390107e5 100644
--- a/src/ELF/ExeLayout.hpp
+++ b/src/ELF/ExeLayout.hpp
@@ -43,15 +43,12 @@
 
 #include "ELF/Structures.hpp"
 #include "internal_utils.hpp"
-#include "notes_utils.hpp"
 #include "logging.hpp"
 #include "Layout.hpp"
 
 namespace LIEF {
 namespace ELF {
 
-class Note;
-
 //! Compute the size and the offset of the elements
 //! needed to rebuild the ELF file.
 class LIEF_LOCAL ExeLayout : public Layout {
@@ -215,8 +212,8 @@ class LIEF_LOCAL ExeLayout : public Layout {
       raw_notes.write_conv<uint32_t>(descsz);
 
       // Then the note's type
-      const NOTE_TYPES type = note.type();
-      raw_notes.write_conv<uint32_t>(static_cast<uint32_t>(type));
+      const uint32_t type = note.original_type();
+      raw_notes.write_conv<uint32_t>(type);
 
       // Then we write the note's name
       const std::string& name = note.name();
@@ -1278,65 +1275,45 @@ class LIEF_LOCAL ExeLayout : public Layout {
     }
 
     // Process note sections
-    const Segment* segment_note = binary_->get(SEGMENT_TYPES::PT_NOTE);
-    if (segment_note != nullptr) {
-      using value_t = typename note_to_section_map_t::value_type;
-      const note_to_section_map_t& note_to_section_map = get_note_to_section();
+    if (const Segment* segment_note = binary_->get(SEGMENT_TYPES::PT_NOTE)) {
+      //using value_t = typename note_to_section_map_t::value_type;
+      //const note_to_section_map_t& note_to_section_map = get_note_to_section();
       for (const Note& note : binary_->notes()) {
-        auto range_secname = note_to_section_map.equal_range(note.type());
-        const bool known_section = (range_secname.first != range_secname.second);
-
-        const NOTE_TYPES type = note.type();
-
-        const auto it_section_name = std::find_if(
-            range_secname.first, range_secname.second,
-            [this] (value_t p) {
-              return binary_->has_section(p.second);
-            });
-
-        bool has_section = (it_section_name != range_secname.second);
-
+        auto section_res = Note::note_to_section(note);
         const auto& it_offset = notes_off_map_.find(&note);
 
-        std::string section_name;
-        if (has_section) {
-          section_name = it_section_name->second;
-        } else if (known_section) {
-          section_name = range_secname.first->second;
-        } else {
-          section_name = fmt::format(".note.{:x}", static_cast<uint32_t>(type));
+        if (!section_res) {
+          LIEF_ERR("Note type: {} ('{}') is not supported",
+                   to_string(note.type()), note.name());
+          continue;
         }
 
-        // If the binary does not have the note "type"
-        // but still have the section, then remove the section
-        if (!binary_->has(note.type()) && has_section) {
-          binary_->remove_section(section_name, true);
-        }
+        const char* sec_name = *section_res;
 
         // If the binary has the note type but does not have
         // the section (likly because the user added the note manually)
         // then, create the section
-        if (binary_->has(type) && !has_section) {
+        if (const Section* nsec = binary_->get_section(*section_res);
+            nsec == nullptr)
+        {
           if (it_offset == std::end(notes_off_map_)) {
-            LIEF_ERR("Can't find {}", to_string(type));
-          } else {
-            const size_t note_offset = it_offset->second;
-
-            const Note& note = *binary_->get(type);
-
-            Section section{section_name, ELF_SECTION_TYPES::SHT_NOTE};
-            section += ELF_SECTION_FLAGS::SHF_ALLOC;
-
-            Section* section_added = binary_->add(section, /*loaded */ false);
-            if (section_added == nullptr) {
-              LIEF_ERR("Can't add SHT_NOTE section");
-              return make_error_code(lief_errors::build_error);
-            }
-            section_added->offset(segment_note->file_offset() + note_offset);
-            section_added->size(note.size());
-            section.virtual_address(segment_note->virtual_address() + note_offset);
-            section_added->alignment(4);
+            LIEF_ERR("Can't find raw data for note: '{}'", to_string(note.type()));
+            continue;
+          }
+          const size_t note_offset = it_offset->second;
+
+          Section section{sec_name, ELF_SECTION_TYPES::SHT_NOTE};
+          section += ELF_SECTION_FLAGS::SHF_ALLOC;
+
+          Section* section_added = binary_->add(section, /*loaded */ false);
+          if (section_added == nullptr) {
+            LIEF_ERR("Can't add SHT_NOTE section");
+            return make_error_code(lief_errors::build_error);
           }
+          section_added->offset(segment_note->file_offset() + note_offset);
+          section_added->size(note.size());
+          section.virtual_address(segment_note->virtual_address() + note_offset);
+          section_added->alignment(4);
         }
       }
     }
diff --git a/src/ELF/Layout.hpp b/src/ELF/Layout.hpp
index fe51f574cb..bdab9c1bda 100644
--- a/src/ELF/Layout.hpp
+++ b/src/ELF/Layout.hpp
@@ -28,27 +28,27 @@ class Layout {
   public:
   Layout(Binary& bin);
 
-  inline virtual const std::unordered_map<std::string, size_t>& shstr_map() const {
+  virtual const std::unordered_map<std::string, size_t>& shstr_map() const {
     return shstr_name_map_;
   }
 
-  inline virtual const std::unordered_map<std::string, size_t>& strtab_map() const {
+  virtual const std::unordered_map<std::string, size_t>& strtab_map() const {
     return strtab_name_map_;
   }
 
-  inline virtual const std::vector<uint8_t>& raw_shstr() const {
+  virtual const std::vector<uint8_t>& raw_shstr() const {
     return raw_shstrtab_;
   }
 
-  inline virtual const std::vector<uint8_t>& raw_strtab() const {
+  virtual const std::vector<uint8_t>& raw_strtab() const {
     return raw_strtab_;
   }
 
-  inline void set_strtab_section(Section& section) {
+  void set_strtab_section(Section& section) {
     strtab_section_ = &section;
   }
 
-  inline void set_dyn_sym_idx(int32_t val) {
+  void set_dyn_sym_idx(int32_t val) {
     new_symndx_ = val;
   }
 
diff --git a/src/ELF/Note.cpp b/src/ELF/Note.cpp
index 0ea74a0281..33f7ed9f72 100644
--- a/src/ELF/Note.cpp
+++ b/src/ELF/Note.cpp
@@ -20,204 +20,554 @@
 #include <algorithm>
 #include <utility>
 
-
 #include "LIEF/utils.hpp"
-
 #include "LIEF/ELF/hash.hpp"
-
 #include "LIEF/ELF/EnumToString.hpp"
-
 #include "LIEF/ELF/Note.hpp"
-#include "LIEF/ELF/NoteDetails.hpp"
-#include "LIEF/ELF/NoteDetails/AndroidNote.hpp"
 #include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
-#include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
-#include "LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp"
-#include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
+#include "LIEF/ELF/NoteDetails/AndroidIdent.hpp"
 #include "LIEF/ELF/NoteDetails/core/CoreAuxv.hpp"
+#include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
+#include "LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp"
+#include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
 #include "LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp"
+#include "LIEF/iostream.hpp"
+#include "LIEF/BinaryStream/BinaryStream.hpp"
+#include "LIEF/BinaryStream/SpanStream.hpp"
+
+#include "frozen.hpp"
+#include "logging.hpp"
+#include "internal_utils.hpp"
+
+#define IMPL_READ_AT(T) \
+  template result<T> Note::read_at(size_t) const;
+
+#define IMPL_WRITE_AT(T) \
+  template ok_error_t Note::write_at(size_t offset, const T& value);
 
 namespace LIEF {
 namespace ELF {
 
-Note::~Note() = default;
+static constexpr auto NT_CORE_NAME = "CORE";
+static constexpr auto NT_GNU_NAME = "GNU";
+static constexpr auto NT_ANDROID_NAME = "Android";
+static constexpr auto NT_LINUX_NAME = "LINUX";
+static constexpr auto NT_GO_NAME = "Go";
+static constexpr auto NT_STAPSDT_NAME = "stapsdt";
+static constexpr auto NT_CRASHPAD_NAME = "Crashpad";
+
+CONST_MAP_ALT GNU_TYPES {
+  std::pair(1,     Note::TYPE::GNU_ABI_TAG),
+  std::pair(2,     Note::TYPE::GNU_HWCAP),
+  std::pair(3,     Note::TYPE::GNU_BUILD_ID),
+  std::pair(4,     Note::TYPE::GNU_GOLD_VERSION),
+  std::pair(5,     Note::TYPE::GNU_PROPERTY_TYPE_0),
+};
+
+CONST_MAP_ALT GENERIC_TYPES {
+  std::pair(0x100, Note::TYPE::GNU_BUILD_ATTRIBUTE_OPEN),
+  std::pair(0x101, Note::TYPE::GNU_BUILD_ATTRIBUTE_FUNC),
+};
+
+/* Core note types. */
+CONST_MAP_ALT CORE_TYPES {
+  std::pair(1,          Note::TYPE::CORE_PRSTATUS),
+  std::pair(2,          Note::TYPE::CORE_FPREGSET),
+  std::pair(3,          Note::TYPE::CORE_PRPSINFO),
+  std::pair(4,          Note::TYPE::CORE_TASKSTRUCT),
+  std::pair(6,          Note::TYPE::CORE_AUXV),
+  std::pair(10,         Note::TYPE::CORE_PSTATUS),
+  std::pair(12,         Note::TYPE::CORE_FPREGS),
+  std::pair(13,         Note::TYPE::CORE_PSINFO),
+  std::pair(16,         Note::TYPE::CORE_LWPSTATUS),
+  std::pair(17,         Note::TYPE::CORE_LWPSINFO),
+  std::pair(18,         Note::TYPE::CORE_WIN32PSTATUS),
+  std::pair(0x53494749, Note::TYPE::CORE_SIGINFO),
+  std::pair(0x46e62b7f, Note::TYPE::CORE_PRXFPREG),
+  std::pair(0x46494c45, Note::TYPE::CORE_FILE),
+};
+
+CONST_MAP_ALT CORE_ARM_YPES {
+  std::pair(0x400,      Note::TYPE::CORE_ARM_VFP),
+  std::pair(0x401,      Note::TYPE::CORE_ARM_TLS),
+  std::pair(0x402,      Note::TYPE::CORE_ARM_HW_BREAK),
+  std::pair(0x403,      Note::TYPE::CORE_ARM_HW_WATCH),
+  std::pair(0x404,      Note::TYPE::CORE_ARM_SYSTEM_CALL),
+  std::pair(0x405,      Note::TYPE::CORE_ARM_SVE),
+  std::pair(0x406,      Note::TYPE::CORE_ARM_PAC_MASK),
+  std::pair(0x407,      Note::TYPE::CORE_ARM_PACA_KEYS),
+  std::pair(0x408,      Note::TYPE::CORE_ARM_PACG_KEYS),
+  std::pair(0x409,      Note::TYPE::CORE_TAGGED_ADDR_CTRL),
+  std::pair(0x40a,      Note::TYPE::CORE_PAC_ENABLED_KEYS),
+};
+
+CONST_MAP_ALT CORE_X86_TYPES {
+  std::pair(0x200,      Note::TYPE::CORE_X86_TLS),
+  std::pair(0x201,      Note::TYPE::CORE_X86_IOPERM),
+  std::pair(0x202,      Note::TYPE::CORE_X86_XSTATE),
+  std::pair(0x203,      Note::TYPE::CORE_X86_CET),
+};
+
+
+/* Android notes */
+CONST_MAP_ALT ANDROID_TYPES {
+  std::pair(1, Note::TYPE::ANDROID_IDENT),
+  std::pair(3, Note::TYPE::ANDROID_KUSER),
+  std::pair(4, Note::TYPE::ANDROID_MEMTAG),
+};
+
+/* Go types. */
+CONST_MAP_ALT GO_TYPES {
+  std::pair(4, Note::TYPE::GO_BUILDID),
+};
+
+/* Stapsdt types. */
+CONST_MAP_ALT STAPSDT_TYPES {
+  std::pair(3, Note::TYPE::STAPSDT),
+};
+
+/* Crashpad types. */
+CONST_MAP_ALT CRASHPAD_TYPES {
+  std::pair(0x4f464e49, Note::TYPE::CRASHPAD),
+};
+
+static inline std::string strip_zero(const std::string& name) {
+  return name.c_str();
+}
 
-Note& Note::operator=(Note other) {
-  swap(other);
-  return *this;
+result<const char*> Note::type_to_section(TYPE type) {
+  CONST_MAP_ALT TYPE2SECTION {
+    std::pair(TYPE::GNU_ABI_TAG,              ".note.ABI-tag"),
+    std::pair(TYPE::GNU_HWCAP,                ".note.gnu.hwcap"),
+    std::pair(TYPE::GNU_BUILD_ID,             ".note.gnu.build-id"),
+    std::pair(TYPE::GNU_GOLD_VERSION,         ".note.gnu.gold-version"),
+    std::pair(TYPE::GNU_PROPERTY_TYPE_0,      ".note.gnu.property"),
+    std::pair(TYPE::GNU_BUILD_ATTRIBUTE_OPEN, ".gnu.build.attributes"),
+    std::pair(TYPE::GNU_BUILD_ATTRIBUTE_FUNC, ".gnu.build.attributes"),
+
+    std::pair(TYPE::STAPSDT,                  ".note.stapsdt"),
+    std::pair(TYPE::CRASHPAD,                 ".note.crashpad.info"),
+    std::pair(TYPE::ANDROID_IDENT,            ".note.android.ident"),
+    std::pair(TYPE::GO_BUILDID,               ".note.go.buildid"),
+  };
+
+  if (auto it = TYPE2SECTION.find(type); it != TYPE2SECTION.end()) {
+    return it->second;
+  }
+  return make_error_code(lief_errors::not_found);
 }
 
-Note::Note(const Note& other):
-  Object(other),
-  binary_(other.binary_),
-  name_(other.name_),
-  type_(other.type_),
-  description_(other.description_)
-{
-  const auto& details = other.details_;
-  details_ = std::make_pair(details.first, std::unique_ptr<NoteDetails>{details.second->clone()});
+inline result<uint32_t> raw_type(const std::string& name, Note::TYPE type) {
+  std::string norm_name = strip_zero(name);
+
+  if (norm_name == NT_CORE_NAME) {
+    for (const auto& [nt, ntype] : CORE_TYPES) {
+      if (ntype == type) {
+        return nt;
+      }
+    }
+    return make_error_code(lief_errors::not_found);
+  }
+
+  if (norm_name == NT_LINUX_NAME) {
+    for (const auto& [nt, ntype] : CORE_ARM_YPES) {
+      if (ntype == type) {
+        return nt;
+      }
+    }
+    for (const auto& [nt, ntype] : CORE_X86_TYPES) {
+      if (ntype == type) {
+        return nt;
+      }
+    }
+    return make_error_code(lief_errors::not_found);
+  }
+
+  if (norm_name == NT_ANDROID_NAME) {
+    for (const auto& [nt, ntype] : ANDROID_TYPES) {
+      if (ntype == type) {
+        return nt;
+      }
+    }
+    return make_error_code(lief_errors::not_found);
+  }
+
+  if (norm_name == NT_GNU_NAME) {
+    for (const auto& [nt, ntype] : GNU_TYPES) {
+      if (ntype == type) {
+        return nt;
+      }
+    }
+
+    return make_error_code(lief_errors::not_found);
+  }
+
+  if (norm_name == NT_GO_NAME) {
+    for (const auto& [nt, ntype] : GO_TYPES) {
+      if (ntype == type) {
+        return nt;
+      }
+    }
+
+    return make_error_code(lief_errors::not_found);
+  }
+
+  if (norm_name == NT_STAPSDT_NAME) {
+    for (const auto& [nt, ntype] : STAPSDT_TYPES) {
+      if (ntype == type) {
+        return nt;
+      }
+    }
+    return make_error_code(lief_errors::not_found);
+  }
+
+  if (norm_name == NT_CRASHPAD_NAME) {
+    for (const auto& [nt, ntype] : CRASHPAD_TYPES) {
+      if (ntype == type) {
+        return nt;
+      }
+    }
+
+    return make_error_code(lief_errors::not_found);
+  }
+
+  for (const auto& [nt, ntype] : GENERIC_TYPES) {
+    if (ntype == type) {
+      return nt;
+    }
+  }
+
+  return make_error_code(lief_errors::not_found);
 }
 
-void Note::swap(Note& other) {
-  std::swap(binary_,      other.binary_);
-  std::swap(name_,        other.name_);
-  std::swap(type_,        other.type_);
-  std::swap(description_, other.description_);
-  std::swap(details_,     other.details_);
+
+
+result<const char*> Note::type_owner(Note::TYPE type) {
+  CONST_MAP_ALT TYPE2OWNER {
+    std::pair(TYPE::GNU_ABI_TAG           ,NT_GNU_NAME),
+    std::pair(TYPE::GNU_HWCAP             ,NT_GNU_NAME),
+    std::pair(TYPE::GNU_BUILD_ID          ,NT_GNU_NAME),
+    std::pair(TYPE::GNU_GOLD_VERSION      ,NT_GNU_NAME),
+    std::pair(TYPE::GNU_PROPERTY_TYPE_0   ,NT_GNU_NAME),
+    std::pair(TYPE::CORE_PRSTATUS         ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_FPREGSET         ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_PRPSINFO         ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_TASKSTRUCT       ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_AUXV             ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_PSTATUS          ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_FPREGS           ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_PSINFO           ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_LWPSTATUS        ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_LWPSINFO         ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_WIN32PSTATUS     ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_FILE             ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_PRXFPREG         ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_SIGINFO          ,NT_CORE_NAME),
+    std::pair(TYPE::CORE_ARM_VFP          ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_ARM_TLS          ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_ARM_HW_BREAK     ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_ARM_HW_WATCH     ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_ARM_SYSTEM_CALL  ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_ARM_SVE          ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_ARM_PAC_MASK     ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_ARM_PACA_KEYS    ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_ARM_PACG_KEYS    ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_TAGGED_ADDR_CTRL ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_PAC_ENABLED_KEYS ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_X86_TLS          ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_X86_IOPERM       ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_X86_XSTATE       ,NT_LINUX_NAME),
+    std::pair(TYPE::CORE_X86_CET          ,NT_LINUX_NAME),
+    std::pair(TYPE::ANDROID_MEMTAG        ,NT_ANDROID_NAME),
+    std::pair(TYPE::ANDROID_KUSER         ,NT_ANDROID_NAME),
+    std::pair(TYPE::ANDROID_IDENT         ,NT_ANDROID_NAME),
+    std::pair(TYPE::GO_BUILDID            ,NT_GO_NAME),
+    std::pair(TYPE::STAPSDT               ,NT_STAPSDT_NAME),
+    std::pair(TYPE::CRASHPAD              ,NT_CRASHPAD_NAME),
+  };
+
+  if (auto it = TYPE2OWNER.find(type); it != TYPE2OWNER.end()) {
+    return it->second;
+  }
+  return make_error_code(lief_errors::not_found);
 }
 
-Note::Note() :
-  type_{NOTE_TYPES::NT_UNKNOWN},
-  details_{std::make_pair(NOTE_TYPES::NT_UNKNOWN, std::make_unique<NoteDetails>())}
-{}
-
-Note::Note(std::string name, uint32_t type, description_t description, Binary* binary):
-  binary_{binary},
-  name_{std::move(name)},
-  type_{static_cast<NOTE_TYPES>(type)},
-  description_{std::move(description)},
-  details_{std::make_pair(NOTE_TYPES::NT_UNKNOWN, std::make_unique<NoteDetails>())}
-{}
-
-Note::Note(const std::string& name, NOTE_TYPES type, const description_t& description, Binary* binary):
-  Note::Note{name, static_cast<uint32_t>(type), description, binary}
-{}
-
-Note::Note(const std::string& name, NOTE_TYPES_CORE type, const description_t& description, Binary* binary):
-  Note::Note{name, static_cast<uint32_t>(type), description, binary}
+
+result<Note::TYPE> Note::convert_type(E_TYPE ftype, uint32_t type,
+                                      const std::string& name)
 {
-  is_core_ = true;
-  details();
-}
+  std::string norm_name = strip_zero(name);
+
+  if (ftype == E_TYPE::ET_CORE) {
+    if (norm_name == NT_CORE_NAME) {
+      if (auto it = CORE_TYPES.find(type); it != CORE_TYPES.end()) {
+        return it->second;
+      }
+      return make_error_code(lief_errors::not_found);
+    }
 
+    if (norm_name == NT_LINUX_NAME) {
+      if (auto it = CORE_ARM_YPES.find(type); it != CORE_ARM_YPES.end()) {
+        return it->second;
+      }
+      if (auto it = CORE_X86_TYPES.find(type); it != CORE_X86_TYPES.end()) {
+        return it->second;
+      }
+      return make_error_code(lief_errors::not_found);
+    }
+    return make_error_code(lief_errors::not_found);
+  }
 
-const std::string& Note::name() const {
-  return name_;
-}
+  if (norm_name == NT_ANDROID_NAME) {
+    if (auto it = ANDROID_TYPES.find(type); it != ANDROID_TYPES.end()) {
+      return it->second;
+    }
+    return make_error_code(lief_errors::not_found);
+  }
 
-NOTE_TYPES Note::type() const {
-  return type_;
-}
+  if (norm_name == NT_GNU_NAME) {
+    if (auto it = GNU_TYPES.find(type); it != GNU_TYPES.end()) {
+      return it->second;
+    }
+    return make_error_code(lief_errors::not_found);
+  }
 
-NOTE_TYPES_CORE Note::type_core() const {
-  return static_cast<NOTE_TYPES_CORE>(type_);
-}
+  if (norm_name == NT_GO_NAME) {
+    if (auto it = GO_TYPES.find(type); it != GO_TYPES.end()) {
+      return it->second;
+    }
+    return make_error_code(lief_errors::not_found);
+  }
 
-const Note::description_t& Note::description() const {
-  return description_;
-}
+  if (norm_name == NT_STAPSDT_NAME) {
+    if (auto it = STAPSDT_TYPES.find(type); it != STAPSDT_TYPES.end()) {
+      return it->second;
+    }
+    return make_error_code(lief_errors::not_found);
+  }
 
-Note::description_t& Note::description() {
-  return description_;
-}
+  if (norm_name == NT_CRASHPAD_NAME) {
+    if (auto it = CRASHPAD_TYPES.find(type); it != CRASHPAD_TYPES.end()) {
+      return it->second;
+    }
+    return make_error_code(lief_errors::not_found);
+  }
 
-bool Note::is_core() const {
-  return is_core_;
+  // Generic types
+  if (auto it = GENERIC_TYPES.find(type); it != GENERIC_TYPES.end()) {
+    return it->second;
+  }
+
+  return make_error_code(lief_errors::not_found);
 }
 
+std::unique_ptr<Note>
+Note::create(BinaryStream& stream, E_TYPE ftype, ARCH arch, ELF_CLASS cls) {
+  static constexpr uint32_t MAX_NOTE_DESCRIPTION = 1_MB;
+  const size_t pos = stream.pos();
+  auto res_namesz = stream.read_conv<uint32_t>();
+  if (!res_namesz) {
+    return nullptr;
+  }
 
-bool Note::is_android() const {
-  return name() == AndroidNote::NAME;
-}
+  const auto namesz = *res_namesz;
+  LIEF_DEBUG("[0x{:06x}] Name size: 0x{:x}", pos, namesz);
 
-const NoteDetails& Note::details() const {
-  return *(details_.second);
-}
+  auto res_descz = stream.read_conv<uint32_t>();
+  if (!res_descz) {
+    return nullptr;
+  }
+
+  uint32_t descsz = std::min(*res_descz, MAX_NOTE_DESCRIPTION);
+  LIEF_DEBUG("Description size: 0x{:x}", descsz);
 
-NoteDetails& Note::details() {
-  NOTE_TYPES type = this->type();
-  auto& dcache = details_;
+  auto res_type = stream.read_conv<uint32_t>();
+  if (!res_type) {
+    return nullptr;
+  }
+
+  uint32_t type = *res_type;
+  LIEF_DEBUG("Type: 0x{:x}", type);
 
-  // already in cache
-  if (dcache.first == type) {
-    return *(dcache.second);
+  if (namesz == 0) { // System reserves
+    return nullptr;
   }
+  std::vector<uint8_t> name_buffer(namesz, 0);
+  if (!stream.read_data(name_buffer, namesz)) {
+    LIEF_ERR("Can't read note name");
+    return nullptr;
+  }
+
+  std::string name(reinterpret_cast<const char*>(name_buffer.data()), name_buffer.size());
+  LIEF_DEBUG("Name: {}", name);
 
-  std::unique_ptr<NoteDetails> details = nullptr;
+  stream.align(sizeof(uint32_t));
 
-  if (is_android()) {
-    details = std::make_unique<AndroidNote>(AndroidNote::make(*this));
+  std::vector<uint32_t> description;
+  if (descsz > 0) {
+    const size_t nb_chunks = (descsz - 1) / sizeof(uint32_t) + 1;
+    description.reserve(nb_chunks);
+    for (size_t i = 0; i < nb_chunks; ++i) {
+      if (const auto chunk = stream.read_conv<uint32_t>()) {
+        description.push_back(*chunk);
+      } else {
+        break;
+      }
+    }
+    stream.align(sizeof(uint32_t));
+  }
+  std::vector<uint8_t> desc_bytes;
+  if (!description.empty()) {
+    const auto* start_ptr = reinterpret_cast<const uint8_t*>(description.data());
+    desc_bytes = {start_ptr,
+                  start_ptr + description.size() * sizeof(uint32_t)};
   }
 
-  if (is_core()) {
-    auto type_core = static_cast<NOTE_TYPES_CORE>(type);
+  return create(name, type, std::move(desc_bytes), ftype, arch, cls);
+}
 
-    switch(type_core) {
-      case NOTE_TYPES_CORE::NT_PRPSINFO:
-        {
-          details = std::make_unique<CorePrPsInfo>(CorePrPsInfo::make(*this));
-          break;
-        }
+std::unique_ptr<Note>
+Note::create(const std::string& name, Note::TYPE ntype, Note::description_t description,
+             ARCH arch, ELF_CLASS cls)
+{
+  std::string owner;
+  auto res_owner = type_owner(ntype);
+  if (res_owner) {
+    if (!name.empty() && strip_zero(name) != *res_owner) {
+      LIEF_WARN("The note owner of '{}' should be '{}' while '{}' is provided",
+                 to_string(ntype), *res_owner, name);
+    }
+    owner = std::move(*res_owner);
+  } else {
+    if (!name.empty()) {
+      owner = name;
+    } else {
+      LIEF_ERR("Note owner name can't be resolved!");
+      return nullptr;
+    }
+  }
 
-      case NOTE_TYPES_CORE::NT_FILE:
-        {
-          details = std::make_unique<CoreFile>(CoreFile::make(*this));
-          break;
-        }
+  auto int_type = raw_type(owner, ntype);
+  if (!int_type) {
+    LIEF_ERR("Can't determine the raw type of note '{}' for the owner '{}'",
+             to_string(ntype), owner);
+    return nullptr;
+  }
+  std::string norm_name = owner;
 
-      case NOTE_TYPES_CORE::NT_PRSTATUS:
-        {
-          details = std::make_unique<CorePrStatus>(CorePrStatus::make(*this));
-          break;
+  // The name of these types have a special meaning
+  if (ntype != Note::TYPE::GNU_BUILD_ATTRIBUTE_FUNC &&
+      ntype != Note::TYPE::GNU_BUILD_ATTRIBUTE_OPEN)
+  {
+    norm_name = strip_zero(owner);
+  }
+  switch (ntype) {
+    case Note::TYPE::CORE_PRSTATUS:
+      {
+        if (cls != ELF_CLASS::ELFCLASS32 && cls != ELF_CLASS::ELFCLASS64) {
+          LIEF_WARN("CORE_PRSTATUS requires a valid ELF class");
+          return nullptr;
         }
 
-      case NOTE_TYPES_CORE::NT_AUXV:
-        {
-          details = std::make_unique<CoreAuxv>(CoreAuxv::make(*this));
-          break;
+        if (arch == ARCH::EM_NONE) {
+          LIEF_WARN("CORE_PRSTATUS requires a valid architecture");
+          return nullptr;
+        }
+        return std::unique_ptr<CorePrStatus>(new CorePrStatus(
+              arch, cls, std::move(norm_name), *int_type, std::move(description)
+        ));
+      }
+    case Note::TYPE::CORE_PRPSINFO:
+      {
+        if (cls != ELF_CLASS::ELFCLASS32 && cls != ELF_CLASS::ELFCLASS64) {
+          LIEF_WARN("CORE_PRPSINFO requires a valid ELF class");
+          return nullptr;
         }
 
-      case NOTE_TYPES_CORE::NT_SIGINFO:
-        {
-          details = std::make_unique<CoreSigInfo>(CoreSigInfo::make(*this));
-          break;
+        if (arch == ARCH::EM_NONE) {
+          LIEF_WARN("CORE_PRPSINFO requires a valid architecture");
+          return nullptr;
+        }
+        return std::unique_ptr<CorePrPsInfo>(new CorePrPsInfo(
+            arch, cls, std::move(norm_name), *int_type, std::move(description)
+        ));
+      }
+    case Note::TYPE::CORE_FILE:
+      {
+        if (cls != ELF_CLASS::ELFCLASS32 && cls != ELF_CLASS::ELFCLASS64) {
+          LIEF_WARN("CORE_FILE requires a valid ELF class");
+          return nullptr;
         }
 
-      default:
-        break;
-    }
-  }
+        if (arch == ARCH::EM_NONE) {
+          LIEF_WARN("CORE_FILE requires a valid architecture");
+          return nullptr;
+        }
+        return std::unique_ptr<CoreFile>(new CoreFile(
+            arch, cls, std::move(norm_name), *int_type, std::move(description)
+        ));
+      }
+    case Note::TYPE::CORE_AUXV:
+      {
+        if (cls != ELF_CLASS::ELFCLASS32 && cls != ELF_CLASS::ELFCLASS64) {
+          LIEF_WARN("CORE_AUXV requires a valid ELF class");
+          return nullptr;
+        }
 
-  if (!details) {
-    switch (type) {
-      case NOTE_TYPES::NT_GNU_ABI_TAG:
-        {
-          details = std::make_unique<NoteAbi>(NoteAbi::make(*this));
-          break;
+        if (arch == ARCH::EM_NONE) {
+          LIEF_WARN("CORE_AUXV requires a valid architecture");
+          return nullptr;
+        }
+        return std::unique_ptr<CoreAuxv>(new CoreAuxv(
+            arch, cls, std::move(norm_name), *int_type, std::move(description)
+        ));
+      }
+    case Note::TYPE::CORE_SIGINFO:
+      {
+        if (cls != ELF_CLASS::ELFCLASS32 && cls != ELF_CLASS::ELFCLASS64) {
+          LIEF_WARN("CORE_SIGINFO requires a valid ELF class");
+          return nullptr;
         }
 
-      default:
-        {
-          details = std::make_unique<NoteDetails>();
-          break;
+        if (arch == ARCH::EM_NONE) {
+          LIEF_WARN("CORE_SIGINFO requires a valid architecture");
+          return nullptr;
         }
-    }
+        return std::unique_ptr<CoreSigInfo>(new CoreSigInfo(
+            std::move(norm_name), ntype, *int_type, std::move(description)
+        ));
+      }
+    case Note::TYPE::ANDROID_IDENT:
+        return std::unique_ptr<AndroidIdent>(new AndroidIdent(
+            std::move(norm_name), ntype, *int_type, std::move(description)
+        ));
+    case Note::TYPE::GNU_ABI_TAG:
+        return std::unique_ptr<NoteAbi>(new NoteAbi(
+            std::move(norm_name), ntype, *int_type, std::move(description)
+        ));
+
+    default:
+        return std::unique_ptr<Note>(new Note(
+            std::move(norm_name), ntype, *int_type, std::move(description)
+        ));
   }
-
-  // update cache
-  dcache.first = type;
-  dcache.second.swap(details);
-  return *dcache.second;
+  return nullptr;
 }
 
-void Note::name(const std::string& name) {
-  name_ = name;
-}
 
-void Note::type(NOTE_TYPES type) {
-  type_ = type;
-  is_core_ = false;
-}
-
-void Note::type_core(NOTE_TYPES_CORE type) {
-  type_ = static_cast<NOTE_TYPES>(type);
-  is_core_ = true;
-  details();
+std::unique_ptr<Note>
+Note::create(const std::string& name, uint32_t type, Note::description_t description,
+             E_TYPE ftype, ARCH arch, ELF_CLASS cls)
+{
+  auto conv = Note::convert_type(ftype, type, name);
+  if (!conv) {
+    LIEF_WARN("Note type: 0x{:x} is not supported for owner: '{}'", type, name);
+    return std::unique_ptr<Note>(new Note(std::move(name), Note::TYPE::UNKNOWN, type,
+                                  std::move(description)));
+  }
+  return create(name, *conv, std::move(description), arch, cls);
 }
 
-void Note::description(const description_t& description) {
-  description_ = description;
-}
 
 uint64_t Note::size() const {
   uint64_t size = 0;
@@ -233,63 +583,137 @@ void Note::accept(Visitor& visitor) const {
   visitor.visit(*this);
 }
 
+void Note::dump(std::ostream& os) const {
+  os << fmt::format("{}(0x{:04x}) '{}' [{}]",
+                    to_string(type()), original_type(), name(),
+                    to_hex(description(), 10));
+}
 
 
+const char* to_string(Note::TYPE type) {
+  #define ENTRY(X) std::pair(Note::TYPE::X, #X)
+  STRING_MAP enums2str {
+    ENTRY(UNKNOWN),
+    ENTRY(GNU_ABI_TAG),
+    ENTRY(GNU_HWCAP),
+    ENTRY(GNU_ABI_TAG),
+    ENTRY(GNU_HWCAP),
+    ENTRY(GNU_BUILD_ID),
+    ENTRY(GNU_GOLD_VERSION),
+    ENTRY(GNU_PROPERTY_TYPE_0),
+    ENTRY(GNU_BUILD_ATTRIBUTE_OPEN),
+    ENTRY(GNU_BUILD_ATTRIBUTE_FUNC),
+    ENTRY(CRASHPAD),
+    ENTRY(CORE_PRSTATUS),
+    ENTRY(CORE_FPREGSET),
+    ENTRY(CORE_PRPSINFO),
+    ENTRY(CORE_TASKSTRUCT),
+    ENTRY(CORE_AUXV),
+    ENTRY(CORE_PSTATUS),
+    ENTRY(CORE_FPREGS),
+    ENTRY(CORE_PSINFO),
+    ENTRY(CORE_LWPSTATUS),
+    ENTRY(CORE_LWPSINFO),
+    ENTRY(CORE_WIN32PSTATUS),
+    ENTRY(CORE_FILE),
+    ENTRY(CORE_PRXFPREG),
+    ENTRY(CORE_SIGINFO),
+
+    ENTRY(CORE_ARM_VFP),
+    ENTRY(CORE_ARM_TLS),
+    ENTRY(CORE_ARM_HW_BREAK),
+    ENTRY(CORE_ARM_HW_WATCH),
+    ENTRY(CORE_ARM_SYSTEM_CALL),
+    ENTRY(CORE_ARM_SVE),
+    ENTRY(CORE_ARM_PAC_MASK),
+    ENTRY(CORE_ARM_PACA_KEYS),
+    ENTRY(CORE_ARM_PACG_KEYS),
+    ENTRY(CORE_TAGGED_ADDR_CTRL),
+    ENTRY(CORE_PAC_ENABLED_KEYS),
+    ENTRY(CORE_X86_TLS),
+    ENTRY(CORE_X86_IOPERM),
+    ENTRY(CORE_X86_XSTATE),
+    ENTRY(CORE_X86_CET),
+
+    ENTRY(ANDROID_MEMTAG),
+    ENTRY(ANDROID_KUSER),
+    ENTRY(ANDROID_IDENT),
+    ENTRY(STAPSDT),
+    ENTRY(GO_BUILDID),
+  };
+  #undef ENTRY
+
+  if (auto it = enums2str.find(type); it != enums2str.end()) {
+    return it->second;
+  }
 
+  return "UNKNOWN";
+}
 
-void Note::dump(std::ostream& os) const {
-  const description_t& desc = description();
-
-  std::string description_str = std::accumulate(
-      std::begin(desc),
-      std::begin(desc) + std::min<size_t>(16, desc.size()), std::string{},
-      [] (const std::string& a, uint8_t v) {
-        std::ostringstream hex_v;
-        hex_v << std::setw(2) << std::setfill('0') << std::hex;
-        hex_v << static_cast<uint32_t>(v);
-
-        return a.empty() ? "[" + hex_v.str() : a + " " + hex_v.str();
-      });
-  if (desc.size() > 16) {
-    description_str += " ...";
-  }
-  description_str += "]";
-  os << std::hex << std::left;
-  os << std::setw(33) << std::setfill(' ') << "Name:" << name() << std::endl;
-  const std::string type_str = is_core() ? to_string(type_core()) : to_string(type());
-  os << std::setw(33) << std::setfill(' ') << "Type:" << type_str << std::endl;
-  os << std::setw(33) << std::setfill(' ') << "Description:" << description_str << std::endl;
-
-  if (!is_core()) {
-    // GOLD VERSION
-    if (type() == NOTE_TYPES::NT_GNU_GOLD_VERSION) {
-      std::string version_str{reinterpret_cast<const char*>(desc.data()), desc.size()};
-      os << std::setw(33) << std::setfill(' ') << "Version:" << version_str << std::endl;
-    }
 
-    // BUILD ID
-    if (type() == NOTE_TYPES::NT_GNU_BUILD_ID) {
-      std::string hash = std::accumulate(std::begin(desc), std::end(desc), std::string{},
-        [] (const std::string& a, uint8_t v) {
-          std::ostringstream hex_v;
-          hex_v << std::setw(2) << std::setfill('0') << std::hex;
-          hex_v << static_cast<uint32_t>(v);
+template<class T>
+result<T> Note::read_at(size_t offset) const {
+  auto stream = SpanStream::from_vector(description_);
+  if (!stream) {
+    return make_error_code(get_error(stream));
+  }
+  return stream->peek<T>(offset);
+}
 
-          return a + hex_v.str();
-        });
 
-      os << std::setw(33) << std::setfill(' ') << "ID Hash:" << hash << std::endl;
-    }
+result<std::string> Note::read_string_at(size_t offset, size_t max_size) const {
+  auto stream = SpanStream::from_vector(description_);
+  if (!stream) {
+    return make_error_code(get_error(stream));
+  }
+  if (max_size > 0) {
+    return stream->peek_string_at(offset, max_size);
   }
 
-  details().dump(os);
+  return stream->peek_string_at(offset);
 }
 
-std::ostream& operator<<(std::ostream& os, const Note& note) {
-  note.dump(os);
-  return os;
+template<class T>
+ok_error_t Note::write_at(size_t offset, const T& value) {
+  Note::description_t original = description_;
+  vector_iostream ios;
+  ios.write(original);
+  ios.seekp(offset);
+  ios.write<T>(value);
+  ios.move(description_);
+  return ok();
+}
+
+ok_error_t Note::write_string_at(size_t offset, const std::string& value) {
+  Note::description_t original = description_;
+  const auto* data = reinterpret_cast<const uint8_t*>(value.data());
 
+  vector_iostream ios;
+  ios.write(original);
+  ios.seekp(offset);
+  ios.write(data, value.size());
+  ios.move(description_);
+  return ok();
 }
 
+IMPL_READ_AT(uint8_t);
+IMPL_READ_AT(int8_t);
+IMPL_READ_AT(uint16_t);
+IMPL_READ_AT(int16_t);
+IMPL_READ_AT(uint32_t);
+IMPL_READ_AT(int32_t);
+IMPL_READ_AT(uint64_t);
+IMPL_READ_AT(int64_t);
+
+IMPL_WRITE_AT(uint8_t);
+IMPL_WRITE_AT(int8_t);
+IMPL_WRITE_AT(uint16_t);
+IMPL_WRITE_AT(int16_t);
+IMPL_WRITE_AT(uint32_t);
+IMPL_WRITE_AT(int32_t);
+IMPL_WRITE_AT(uint64_t);
+IMPL_WRITE_AT(int64_t);
+IMPL_WRITE_AT(std::string);
+
 } // namespace ELF
 } // namespace LIEF
diff --git a/src/ELF/NoteDetails/AndroidIdent.cpp b/src/ELF/NoteDetails/AndroidIdent.cpp
new file mode 100644
index 0000000000..260ce125d7
--- /dev/null
+++ b/src/ELF/NoteDetails/AndroidIdent.cpp
@@ -0,0 +1,86 @@
+/* Copyright 2017 - 2023 R. Thomas
+ * Copyright 2017 - 2023 Quarkslab
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <iomanip>
+#include <sstream>
+#include <algorithm>
+#include "logging.hpp"
+
+#include "LIEF/BinaryStream/SpanStream.hpp"
+#include "LIEF/ELF/NoteDetails/AndroidIdent.hpp"
+#include "LIEF/ELF/hash.hpp"
+#include "LIEF/iostream.hpp"
+
+namespace LIEF {
+namespace ELF {
+
+static constexpr auto sdk_version_offset = 0;
+static constexpr auto ndk_version_offset = sizeof(uint32_t);
+static constexpr auto ndk_build_number_offset = ndk_version_offset + AndroidIdent::ndk_version_size;
+
+uint32_t AndroidIdent::sdk_version() const {
+  auto val = read_at<uint32_t>(sdk_version_offset);
+  if (!val) {
+    return 0;
+  }
+  return *val;
+}
+
+std::string AndroidIdent::ndk_version() const {
+  auto val = read_string_at(ndk_version_offset);
+  if (!val) {
+    return "";
+  }
+  return *val;
+}
+
+std::string AndroidIdent::ndk_build_number() const {
+  auto val = read_string_at(ndk_build_number_offset);
+  if (!val) {
+    return "";
+  }
+  return *val;
+}
+
+void AndroidIdent::sdk_version(uint32_t version) {
+  write_at(sdk_version_offset, version);
+}
+
+void AndroidIdent::ndk_version(const std::string& ndk_version) {
+  std::string sized_nersion = ndk_version;
+  sized_nersion.resize(ndk_version_size, 0);
+  write_string_at(ndk_version_offset, sized_nersion);
+}
+
+void AndroidIdent::ndk_build_number(const std::string& ndk_build_number) {
+  std::string sized_nersion = ndk_build_number;
+  sized_nersion.resize(ndk_build_number_size);
+  write_string_at(ndk_build_number_offset, sized_nersion);
+}
+
+void AndroidIdent::accept(Visitor& visitor) const {
+  visitor.visit(*this);
+}
+
+void AndroidIdent::dump(std::ostream& os) const {
+  Note::dump(os);
+  os << '\n';
+  os << fmt::format("SDK: {} NDK: {} NDK Build: {}",
+      sdk_version(), ndk_version(), ndk_build_number()
+  );
+}
+
+} // namespace ELF
+} // namespace LIEF
diff --git a/src/ELF/NoteDetails/AndroidNote.cpp b/src/ELF/NoteDetails/AndroidNote.cpp
deleted file mode 100644
index 6d58b2915b..0000000000
--- a/src/ELF/NoteDetails/AndroidNote.cpp
+++ /dev/null
@@ -1,149 +0,0 @@
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <iomanip>
-#include <sstream>
-#include <algorithm>
-
-#include "LIEF/ELF/hash.hpp"
-
-#include "LIEF/ELF/NoteDetails/AndroidNote.hpp"
-
-namespace LIEF {
-namespace ELF {
-
-AndroidNote AndroidNote::make(Note& note) {
-  AndroidNote android_note = note;
-  android_note.parse();
-  return android_note;
-}
-
-AndroidNote::AndroidNote(Note& note) :
-  NoteDetails::NoteDetails{note}
-{}
-
-AndroidNote* AndroidNote::clone() const {
-  return new AndroidNote(*this);
-}
-
-uint32_t AndroidNote::sdk_version() const {
-  return sdk_version_;
-}
-
-std::string AndroidNote::ndk_version() const {
-  return ndk_version_;
-}
-
-std::string AndroidNote::ndk_build_number() const {
-  return ndk_build_number_;
-}
-
-void AndroidNote::sdk_version(uint32_t version) {
-  sdk_version_ = version;
-  build();
-}
-
-void AndroidNote::ndk_version(const std::string& ndk_version) {
-  ndk_version_ = ndk_version;
-  build();
-}
-
-void AndroidNote::ndk_build_number(const std::string& ndk_build_number) {
-  ndk_build_number_ = ndk_build_number;
-  build();
-}
-
-
-void AndroidNote::parse() {
-  const description_t& desc = description();
-
-  // Parse SDK Version
-  if (desc.size() < (sdk_version_offset + sdk_version_size)) {
-    return;
-  }
-  sdk_version_ = *reinterpret_cast<const uint32_t*>(desc.data() + sdk_version_offset);
-
-  // Parse NDK Version
-  if (desc.size() < (ndk_version_offset + ndk_version_size)) {
-    return ;
-  }
-  ndk_version_ = std::string{reinterpret_cast<const char*>(desc.data()) + ndk_version_offset, ndk_version_size};
-
-  // Parse NDK Build Number
-  if (desc.size() < (ndk_build_number_offset + ndk_build_number_size)) {
-    return ;
-  }
-  ndk_build_number_ = std::string{reinterpret_cast<const char*>(desc.data()) + ndk_build_number_offset, ndk_build_number_size};
-}
-
-void AndroidNote::build() {
-  description_t& desc = description();
-
-  // Build SDK Version
-  // =================
-  if (desc.size() < (sdk_version_offset + sdk_version_size)) {
-    desc.resize(sdk_version_offset + sdk_version_size);
-  }
-
-  std::copy(
-      reinterpret_cast<const uint8_t*>(&sdk_version_),
-      reinterpret_cast<const uint8_t*>(&sdk_version_) + sdk_version_size,
-      desc.data() + sdk_version_offset);
-
-  // Build NDK Version
-  // =================
-  if (desc.size() < (ndk_version_offset + ndk_version_size)) {
-    desc.resize(ndk_version_offset + ndk_version_size);
-  }
-
-  std::copy(
-      reinterpret_cast<const uint8_t*>(ndk_version_.data()),
-      reinterpret_cast<const uint8_t*>(ndk_version_.data()) + ndk_version_.size(),
-      desc.data() + ndk_version_offset);
-
-  // Build NDK Build Number
-  // ======================
-  if (desc.size() < (ndk_build_number_offset + ndk_build_number_size)) {
-    desc.resize(ndk_build_number_offset + ndk_build_number_size);
-  }
-
-  std::copy(
-      reinterpret_cast<const uint8_t*>(ndk_build_number_.data()),
-      reinterpret_cast<const uint8_t*>(ndk_build_number_.data()) + ndk_build_number_.size(),
-      desc.data() + ndk_build_number_offset);
-}
-
-void AndroidNote::accept(Visitor& visitor) const {
-  visitor.visit(*this);
-}
-
-
-
-
-void AndroidNote::dump(std::ostream& os) const {
-  os << std::setw(33) << std::setfill(' ') << "SDK Version: " << std::dec << sdk_version() << std::endl;
-  os << std::setw(33) << std::setfill(' ') << "NDK Version: " << ndk_version() << std::endl;
-  os << std::setw(33) << std::setfill(' ') << "NDK Builder Number: " << ndk_build_number() << std::endl;
-}
-
-std::ostream& operator<<(std::ostream& os, const AndroidNote& note) {
-  note.dump(os);
-  return os;
-}
-
-AndroidNote::~AndroidNote() = default;
-
-} // namespace ELF
-} // namespace LIEF
diff --git a/src/ELF/NoteDetails/CMakeLists.txt b/src/ELF/NoteDetails/CMakeLists.txt
index 55cf9ffa2b..e54dcf9390 100644
--- a/src/ELF/NoteDetails/CMakeLists.txt
+++ b/src/ELF/NoteDetails/CMakeLists.txt
@@ -1,6 +1,6 @@
 target_sources(LIB_LIEF PRIVATE
-  AndroidNote.cpp
   NoteAbi.cpp
+  AndroidIdent.cpp
 )
 
 add_subdirectory(core)
diff --git a/src/ELF/NoteDetails/NoteAbi.cpp b/src/ELF/NoteDetails/NoteAbi.cpp
index ae46077d59..c2b1514f78 100644
--- a/src/ELF/NoteDetails/NoteAbi.cpp
+++ b/src/ELF/NoteDetails/NoteAbi.cpp
@@ -16,89 +16,86 @@
 #include <iomanip>
 #include <sstream>
 
+#include "frozen.hpp"
+#include "internal_utils.hpp"
+
 #include "LIEF/ELF/hash.hpp"
 
 #include "LIEF/ELF/EnumToString.hpp"
-
+#include "LIEF/BinaryStream/SpanStream.hpp"
+#include "LIEF/iostream.hpp"
 #include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
 
+#include "spdlog/fmt/fmt.h"
+
 namespace LIEF {
 namespace ELF {
 
-NoteAbi NoteAbi::make(Note& note) {
-  NoteAbi abi = note;
-  abi.parse();
-  return abi;
+result<NoteAbi::version_t> NoteAbi::version() const {
+  NoteAbi::version_t version;
+  for (size_t i = 0; i < version.size(); ++i) {
+    auto res = read_at<uint32_t>(version_offset + i * sizeof(uint32_t));
+    if (!res) {
+      return make_error_code(lief_errors::read_error);
+    }
+    version[i] = *res;
+  }
+  return version;
 }
 
-NoteAbi* NoteAbi::clone() const {
-  return new NoteAbi(*this);
+result<NoteAbi::ABI> NoteAbi::abi() const {
+  auto value = read_at<uint32_t>(abi_offset);
+  if (!value) {
+    return make_error_code(get_error(value));
+  }
+  return static_cast<ABI>(*value);
 }
 
-NoteAbi::NoteAbi(Note& note) :
-  NoteDetails::NoteDetails{note},
-  version_{{0, 0, 0}},
-  abi_{NOTE_ABIS::ELF_NOTE_UNKNOWN}
-{}
-
-NoteAbi::version_t NoteAbi::version() const {
-  return version_;
+void NoteAbi::version(const version_t& version) {
+  write_at<uint32_t>(version_offset + 0 * sizeof(uint32_t), version[0]);
+  write_at<uint32_t>(version_offset + 1 * sizeof(uint32_t), version[1]);
+  write_at<uint32_t>(version_offset + 2 * sizeof(uint32_t), version[2]);
 }
 
-NOTE_ABIS NoteAbi::abi() const {
-  return abi_;
+void NoteAbi::version(ABI abi) {
+  write_at<uint32_t>(abi_offset, static_cast<uint32_t>(abi));
 }
 
-void NoteAbi::parse() {
-  const description_t& desc = description();
-
-  // Parse ABI
-  if (desc.size() < (abi_offset + abi_size)) {
-    return;
-  }
-  abi_ = static_cast<NOTE_ABIS>(*reinterpret_cast<const uint32_t*>(desc.data()));
-
-  // Parse Version
-  if (desc.size() < (version_offset + version_size)) {
-    return;
-  }
-
-  const auto* version = reinterpret_cast<const uint32_t*>(desc.data() + version_offset);
-  version_ = {{version[0], version[1], version[2]}};
-}
-
-
 void NoteAbi::accept(Visitor& visitor) const {
   visitor.visit(*this);
 }
 
-
-
-
 void NoteAbi::dump(std::ostream& os) const {
-    version_t version = this->version();
-    std::string version_str;
-    // Major
-    version_str += std::to_string(std::get<0>(version));
-    version_str += ".";
+  Note::dump(os);
+  os << '\n';
+  auto version_res = version().value_or(version_t({0, 0, 0}));
+  auto abi_res = to_string_or(abi(), "???");
+  os << fmt::format("   {}.{}.{} '{}'",
+    version_res[0], version_res[1], version_res[2], abi_res
+  );
+}
 
-    // Minor
-    version_str += std::to_string(std::get<1>(version));
-    version_str += ".";
 
-    // Patch
-    version_str += std::to_string(std::get<2>(version));
+const char* to_string(NoteAbi::ABI abi) {
+  #define ENTRY(X) std::pair(NoteAbi::ABI::X, #X)
+  STRING_MAP enums2str {
+    ENTRY(LINUX),
+    ENTRY(GNU),
+    ENTRY(SOLARIS2),
+    ENTRY(FREEBSD),
+    ENTRY(NETBSD),
+    ENTRY(SYLLABLE),
+    ENTRY(NACL),
+  };
+  #undef ENTRY
+
+  if (auto it = enums2str.find(abi); it != enums2str.end()) {
+    return it->second;
+  }
 
-    os << std::setw(33) << std::setfill(' ') << "ABI:"     << to_string(abi()) << std::endl;
-    os << std::setw(33) << std::setfill(' ') << "Version:" << version_str           << std::endl;
-}
+  return "UNKNOWN";
 
-std::ostream& operator<<(std::ostream& os, const NoteAbi& note) {
-  note.dump(os);
-  return os;
 }
 
-NoteAbi::~NoteAbi() = default;
-
 } // namespace ELF
 } // namespace LIEF
diff --git a/src/ELF/NoteDetails/core/CMakeLists.txt b/src/ELF/NoteDetails/core/CMakeLists.txt
index 51064374c0..54d255133c 100644
--- a/src/ELF/NoteDetails/core/CMakeLists.txt
+++ b/src/ELF/NoteDetails/core/CMakeLists.txt
@@ -1,12 +1,8 @@
 target_sources(LIB_LIEF PRIVATE
-  CorePrPsInfo.cpp
-  CorePrPsInfo.tcc
+  CoreAuxv.cpp
   CoreFile.cpp
-  CoreFile.tcc
+  CorePrPsInfo.cpp
   CorePrStatus.cpp
-  CorePrStatus.tcc
-  CoreAuxv.cpp
-  CoreAuxv.tcc
   CoreSigInfo.cpp
 )
 
diff --git a/src/ELF/NoteDetails/core/CoreAuxv.cpp b/src/ELF/NoteDetails/core/CoreAuxv.cpp
index f8a6bf7894..163deeeeac 100644
--- a/src/ELF/NoteDetails/core/CoreAuxv.cpp
+++ b/src/ELF/NoteDetails/core/CoreAuxv.cpp
@@ -19,111 +19,177 @@
 
 #include "LIEF/ELF/hash.hpp"
 #include "LIEF/ELF/EnumToString.hpp"
-#include "LIEF/ELF/Binary.hpp"
+#include "LIEF/ELF/NoteDetails/core/CoreAuxv.hpp"
+#include "LIEF/Visitor.hpp"
+#include "LIEF/iostream.hpp"
+#include "LIEF/BinaryStream/SpanStream.hpp"
 
+#include "frozen.hpp"
+#include "spdlog/fmt/fmt.h"
 #include "ELF/Structures.hpp"
 
-#include "CoreAuxv.tcc"
-
 namespace LIEF {
 namespace ELF {
 
-CoreAuxv::CoreAuxv(Note& note):
-  NoteDetails::NoteDetails{note}
-{}
-
-CoreAuxv CoreAuxv::make(Note& note) {
-  CoreAuxv pinfo(note);
-  pinfo.parse();
-  return pinfo;
+template<class ELF_T> inline result<uint64_t>
+get_impl(CoreAuxv::TYPE type, const Note::description_t& desc) {
+  using Elf_Auxv  = typename ELF_T::Elf_Auxv;
+  auto stream = SpanStream::from_vector(desc);
+  if (!stream) {
+    return make_error_code(get_error(stream));
+  }
+  while (*stream) {
+    auto auxv = stream->read<Elf_Auxv>();
+    if (!auxv) {
+      return make_error_code(lief_errors::not_found);
+    }
+    const auto atype = static_cast<CoreAuxv::TYPE>(auxv->a_type);
+    auto value = auxv->a_un.a_val;
+    if (atype == CoreAuxv::TYPE::END) {
+      return make_error_code(lief_errors::not_found);
+    }
+    if (atype == type) {
+      return static_cast<uint64_t>(value);
+    }
+  }
+  return make_error_code(lief_errors::not_found);
 }
 
-CoreAuxv* CoreAuxv::clone() const {
-  return new CoreAuxv(*this);
-}
+template<class ELF_T> inline std::map<CoreAuxv::TYPE, uint64_t>
+get_values_impl(const Note::description_t& desc) {
+  using Elf_Auxv  = typename ELF_T::Elf_Auxv;
+  auto stream = SpanStream::from_vector(desc);
+  if (!stream) {
+    return {};
+  }
 
+  std::map<CoreAuxv::TYPE, uint64_t> values;
+  while (*stream) {
+    auto auxv = stream->read<Elf_Auxv>();
+    if (!auxv) {
+      return values;
+    }
+    const auto atype = static_cast<CoreAuxv::TYPE>(auxv->a_type);
+    auto value = auxv->a_un.a_val;
+    if (atype == CoreAuxv::TYPE::END) {
+      return values;
+    }
 
-const CoreAuxv::val_context_t& CoreAuxv::values() const {
-  return ctx_;
+    values[atype] = static_cast<uint64_t>(value);
+  }
+  return values;
 }
 
-
-uint64_t CoreAuxv::get(LIEF::ELF::AUX_TYPE atype, bool* error) const {
-  if (!has(atype)) {
-    if (error != nullptr) {
-      *error = true;
+template<class ELF_T>
+inline bool write_impl(Note::description_t& description,
+                       std::map<CoreAuxv::TYPE, uint64_t> values)
+{
+  using Elf_Auxv  = typename ELF_T::Elf_Auxv;
+  using ptr_t     = typename ELF_T::uint;
+  vector_iostream io;
+  io.reserve(values.size() * sizeof(Elf_Auxv));
+
+  for (const auto& [type, value] : values) {
+    // This will be added at the end
+    if (type == CoreAuxv::TYPE::END) {
+      continue;
     }
-    return 0;
-  }
-
-  if (error != nullptr) {
-    *error = false;
+    io.write(static_cast<ptr_t>(type))
+      .write(static_cast<ptr_t>(value));
   }
-  return ctx_.at(atype);
-}
+  io.write(static_cast<ptr_t>(CoreAuxv::TYPE::END))
+    .write(static_cast<ptr_t>(0));
 
-bool CoreAuxv::has(LIEF::ELF::AUX_TYPE reg) const {
-  return ctx_.find(reg) != std::end(ctx_);
+  io.move(description);
+  return true;
 }
 
 
-void CoreAuxv::values(const val_context_t& ctx) {
-  ctx_ = ctx;
-  build();
-}
-
-bool CoreAuxv::set(LIEF::ELF::AUX_TYPE atype, uint64_t value) {
-  ctx_[atype] = value;
-  build();
-  return true;
+result<uint64_t> CoreAuxv::get(TYPE type) const {
+  return class_ == ELF_CLASS::ELFCLASS32 ?
+                   get_impl<details::ELF32>(type, description_) :
+                   get_impl<details::ELF64>(type, description_);
 }
 
-void CoreAuxv::accept(Visitor& visitor) const {
-  visitor.visit(*this);
+std::map<CoreAuxv::TYPE, uint64_t> CoreAuxv::values() const {
+  return class_ == ELF_CLASS::ELFCLASS32 ?
+                   get_values_impl<details::ELF32>(description_) :
+                   get_values_impl<details::ELF64>(description_);
 }
 
 
+bool CoreAuxv::set(TYPE type, uint64_t value) {
+  std::map<TYPE, uint64_t> vals = values();
+  vals[type] = value;
+  return set(vals);
+}
 
-uint64_t& CoreAuxv::operator[](LIEF::ELF::AUX_TYPE atype) {
-  return ctx_[atype];
+bool CoreAuxv::set(std::map<TYPE, uint64_t> values) {
+  return class_ == ELF_CLASS::ELFCLASS32 ?
+                   write_impl<details::ELF32>(description_, values) :
+                   write_impl<details::ELF64>(description_, values);
 }
 
 void CoreAuxv::dump(std::ostream& os) const {
-  static constexpr size_t WIDTH = 16;
-  os << std::left;
+  Note::dump(os);
 
-  os << std::setw(WIDTH) << std::setfill(' ') << "Auxiliary values: "<< std::dec << std::endl;
-  for (const auto& val : ctx_) {
-    os << std::setw(14) << std::setfill(' ') << to_string(val.first) << ": " << std::hex << std::showbase << val.second << std::endl;
+  const auto& aux_vals = values();
+  if (aux_vals.empty()) {
+    return;
   }
-  os << std::endl;
-}
 
-
-void CoreAuxv::parse() {
-  if (binary()->type() == ELF_CLASS::ELFCLASS64) {
-    parse_<details::ELF64>();
-  } else if (binary()->type() == ELF_CLASS::ELFCLASS32) {
-    parse_<details::ELF32>();
+  os << '\n';
+  for (const auto& [type, val] : aux_vals) {
+    os << fmt::format("  {}: 0x{:08x}\n", to_string(type), val);
   }
 }
 
-void CoreAuxv::build() {
-  if (binary()->type() == ELF_CLASS::ELFCLASS64) {
-    build_<details::ELF64>();
-  } else if (binary()->type() == ELF_CLASS::ELFCLASS32) {
-    build_<details::ELF32>();
-  }
+void CoreAuxv::accept(Visitor& visitor) const {
+  visitor.visit(*this);
 }
 
+const char* to_string(CoreAuxv::TYPE type) {
+  #define ENTRY(X) std::pair(CoreAuxv::TYPE::X, #X)
+  STRING_MAP enums2str {
+    ENTRY(END),
+    ENTRY(IGNORE),
+    ENTRY(EXECFD),
+    ENTRY(PHDR),
+    ENTRY(PHENT),
+    ENTRY(PHNUM),
+    ENTRY(PAGESZ),
+    ENTRY(BASE),
+    ENTRY(FLAGS),
+    ENTRY(ENTRY),
+    ENTRY(NOTELF),
+    ENTRY(UID),
+    ENTRY(EUID),
+    ENTRY(GID),
+    ENTRY(EGID),
+    ENTRY(TGT_PLATFORM),
+    ENTRY(HWCAP),
+    ENTRY(CLKTCK),
+    ENTRY(FPUCW),
+    ENTRY(DCACHEBSIZE),
+    ENTRY(ICACHEBSIZE),
+    ENTRY(UCACHEBSIZE),
+    ENTRY(IGNOREPPC),
+    ENTRY(SECURE),
+    ENTRY(BASE_PLATFORM),
+    ENTRY(RANDOM),
+    ENTRY(HWCAP2),
+    ENTRY(EXECFN),
+    ENTRY(SYSINFO),
+    ENTRY(SYSINFO_EHDR),
+  };
+  #undef ENTRY
+
+  if (auto it = enums2str.find(type); it != enums2str.end()) {
+    return it->second;
+  }
 
-std::ostream& operator<<(std::ostream& os, const CoreAuxv& note) {
-  note.dump(os);
-  return os;
+  return "UNKNOWN";
 }
 
-
-CoreAuxv::~CoreAuxv() = default;
-
 } // namespace ELF
 } // namespace LIEF
diff --git a/src/ELF/NoteDetails/core/CoreAuxv.tcc b/src/ELF/NoteDetails/core/CoreAuxv.tcc
deleted file mode 100644
index 84aecb67fc..0000000000
--- a/src/ELF/NoteDetails/core/CoreAuxv.tcc
+++ /dev/null
@@ -1,84 +0,0 @@
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <algorithm>
-
-
-#include "LIEF/utils.hpp"
-#include "LIEF/BinaryStream/VectorStream.hpp"
-#include "LIEF/iostream.hpp"
-
-#include "LIEF/ELF/NoteDetails/core/CoreAuxv.hpp"
-#include "LIEF/ELF/Note.hpp"
-
-namespace LIEF {
-namespace ELF {
-
-template <typename ELF_T>
-void CoreAuxv::parse_() {
-  using Elf_Auxv  = typename ELF_T::Elf_Auxv;
-  using uint__    = typename ELF_T::uint;
-
-  const Note::description_t& desc = description();
-  if (desc.size() < sizeof(Elf_Auxv)) {
-    return;
-  }
-
-  VectorStream stream(desc);
-  while (stream.can_read<Elf_Auxv>()) {
-    auto res_auxv = stream.read<Elf_Auxv>();
-    if (!res_auxv) {
-      break;
-    }
-    auto auxv = *res_auxv;
-    auto type = static_cast<AUX_TYPE>(auxv.a_type);
-    if (type == AUX_TYPE::AT_NULL) {
-      break;
-    }
-    ctx_[type] = static_cast<uint__>(auxv.a_un.a_val);
-  }
-
-}
-
-template <typename ELF_T>
-void CoreAuxv::build_() {
-  using Elf_Auxv  = typename ELF_T::Elf_Auxv;
-  using uint__    = typename ELF_T::uint;
-
-  Note::description_t& desc = description();
-
-  vector_iostream raw_output;
-  raw_output.reserve(ctx_.size() * sizeof(Elf_Auxv));
-
-  for (const auto& val : ctx_) {
-    AUX_TYPE type = val.first;
-    // skip for now, will be added at the end
-    if (type == AUX_TYPE::AT_NULL) {
-      continue;
-    }
-    const Elf_Auxv aux = { static_cast<uint__>(val.first), {static_cast<uint__>(val.second)} };
-    raw_output.write_conv(aux);
-  }
-  // AT_NULL
-  const Elf_Auxv terminator = {0, {0}};
-  raw_output.write_conv(terminator);
-
-  std::vector<uint8_t> raw = raw_output.raw();
-  std::copy(std::begin(raw), std::end(raw),
-            std::begin(desc));
-}
-
-} // namespace ELF
-} // namespace LIEF
diff --git a/src/ELF/NoteDetails/core/CoreFile.cpp b/src/ELF/NoteDetails/core/CoreFile.cpp
index 58811acb94..ef4d5b604f 100644
--- a/src/ELF/NoteDetails/core/CoreFile.cpp
+++ b/src/ELF/NoteDetails/core/CoreFile.cpp
@@ -17,111 +17,122 @@
 #include <iomanip>
 #include <sstream>
 
+#include "logging.hpp"
 #include "LIEF/ELF/hash.hpp"
-
-#include "LIEF/ELF/Binary.hpp"
+#include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
+#include "LIEF/BinaryStream/SpanStream.hpp"
 #include "ELF/Structures.hpp"
-
-#include "CoreFile.tcc"
+#include "LIEF/iostream.hpp"
 
 namespace LIEF {
 namespace ELF {
-class Note;
 
-CoreFile::CoreFile(Note& note):
-  NoteDetails::NoteDetails{note}
-{}
+template<class ELF_T>
+void CoreFile::read_files() {
+  static constexpr auto MAX_ENTRIES = 6000;
+  using Elf_Addr      = typename ELF_T::Elf_Addr;
+  using Elf_FileEntry = typename ELF_T::Elf_FileEntry;
 
-CoreFile CoreFile::make(Note& note) {
-  CoreFile file(note);
-  file.parse();
-  return file;
-}
+  auto stream = SpanStream::from_vector(description_);
+  if (!stream) {
+    return;
+  }
 
-CoreFile* CoreFile::clone() const {
-  return new CoreFile(*this);
-}
+  auto count = stream->read<Elf_Addr>();
+  if (!count) {
+    return;
+  }
 
+  if (*count > MAX_ENTRIES) {
+    LIEF_ERR("Too many entries ({} while limited at {})", *count, MAX_ENTRIES);
+    return;
+  }
 
-uint64_t CoreFile::count() const {
-  return files_.size();
-}
+  auto page_size = stream->read<Elf_Addr>();
+  if (!page_size) {
+    return;
+  }
+
+  page_size_ = *page_size;
+  files_.resize(*count);
+  for (size_t i = 0; i < files_.size(); ++i) {
+    auto entry = stream->read<Elf_FileEntry>();
+    if (!entry) {
+      break;
+    }
+    files_[i] = {entry->start, entry->end, entry->file_ofs, ""};
+  }
 
-const CoreFile::files_t& CoreFile::files() const {
-  return files_;
+  for (size_t i = 0; i < files_.size(); ++i) {
+    auto path = stream->read_string();
+    if (!path) {
+      break;
+    }
+    files_[i].path = std::move(*path);
+  }
 }
 
+template<class ELF_T>
+void CoreFile::write_files() {
+  using Elf_Addr      = typename ELF_T::Elf_Addr;
+  using Elf_FileEntry = typename ELF_T::Elf_FileEntry;
+
+  vector_iostream ios;
+  ios.write(static_cast<Elf_Addr>(files_.size()));
+  ios.write(static_cast<Elf_Addr>(page_size_));
+  for (const entry_t& entry : files_) {
+    Elf_FileEntry raw_entry;
+    std::memset(&raw_entry, 0, sizeof(Elf_FileEntry));
+
+    raw_entry.start    = static_cast<Elf_Addr>(entry.start);
+    raw_entry.end      = static_cast<Elf_Addr>(entry.end);
+    raw_entry.file_ofs = static_cast<Elf_Addr>(entry.file_ofs);
+    ios.write(raw_entry);
+  }
 
-CoreFile::iterator CoreFile::begin() {
-  return std::begin(files_);
+  for (const entry_t& entry : files_) {
+    ios.write(entry.path);
+  }
+  ios.move(description_);
 }
 
-CoreFile::iterator CoreFile::end() {
-  return std::end(files_);
-}
+CoreFile::CoreFile(ARCH arch, ELF_CLASS cls, std::string name,
+                   uint32_t type, Note::description_t description) :
+  Note(std::move(name), Note::TYPE::CORE_FILE, type, std::move(description)),
+  arch_(arch), class_(cls)
+{
 
-CoreFile::const_iterator CoreFile::begin() const {
-  return std::begin(files_);
+  class_ == ELF_CLASS::ELFCLASS32 ? read_files<details::ELF32>() :
+                                    read_files<details::ELF64>();
 }
 
-CoreFile::const_iterator CoreFile::end() const {
-  return std::end(files_);
-}
 
 void CoreFile::files(const CoreFile::files_t& files) {
   files_ = files;
-  build();
-}
+  class_ == ELF_CLASS::ELFCLASS32 ? write_files<details::ELF32>() :
+                                    write_files<details::ELF64>();
 
+}
 
 void CoreFile::accept(Visitor& visitor) const {
   visitor.visit(*this);
 }
 
-
-
 void CoreFile::dump(std::ostream& os) const {
-  static constexpr size_t WIDTH = 16;
-  os << std::left;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Files: "<< std::dec << std::endl;
-  for (const CoreFileEntry& file : files()) {
-    os << " - ";
-    os << file.path << " ";
-    os << "[" << std::hex << std::showbase << file.start << ", " << file.end << "] ";
-    os << file.file_ofs;
-    os << std::endl;
+  Note::dump(os);
+  const files_t& files = this->files();
+  if (files.empty()) {
+    return;
   }
-  os << std::endl;
-}
-
-void CoreFile::parse() {
-  if (binary()->type() == ELF_CLASS::ELFCLASS64) {
-    parse_<details::ELF64>();
-  } else if (binary()->type() == ELF_CLASS::ELFCLASS32) {
-    parse_<details::ELF32>();
+  os << '\n';
+  for (const entry_t& entry : files) {
+    os << "  " << entry << '\n';
   }
 }
 
-void CoreFile::build() {
-  if (binary()->type() == ELF_CLASS::ELFCLASS64) {
-    build_<details::ELF64>();
-  } else if (binary()->type() == ELF_CLASS::ELFCLASS32) {
-    build_<details::ELF32>();
-  }
-}
-
-std::ostream& operator<<(std::ostream& os, const CoreFile& note) {
-  note.dump(os);
-  return os;
-}
-
-CoreFile::~CoreFile() = default;
-
-
-std::ostream& operator<<(std::ostream& os, const CoreFileEntry& entry) {
-  os << std::hex << std::showbase;
-  os << entry.path << ": [" << entry.start << ", " << entry.end << "]@" << entry.file_ofs;
+std::ostream& operator<<(std::ostream& os, const CoreFile::entry_t& entry) {
+  os << fmt::format("{}: [0x{:04x}, 0x{:04x}]@0x{:x}",
+                    entry.path, entry.start, entry.end, entry.file_ofs);
   return os;
 }
 
diff --git a/src/ELF/NoteDetails/core/CoreFile.tcc b/src/ELF/NoteDetails/core/CoreFile.tcc
deleted file mode 100644
index 0e0c2f96ba..0000000000
--- a/src/ELF/NoteDetails/core/CoreFile.tcc
+++ /dev/null
@@ -1,114 +0,0 @@
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <algorithm>
-#include <limits>
-
-#include "logging.hpp"
-
-#include "LIEF/utils.hpp"
-#include "LIEF/BinaryStream/VectorStream.hpp"
-#include "LIEF/iostream.hpp"
-
-#include "LIEF/ELF/Note.hpp"
-#include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
-
-
-namespace LIEF {
-namespace ELF {
-
-template <typename ELF_T>
-void CoreFile::parse_() {
-  static constexpr auto MAX_ENTRIES = 6000;
-  using Elf_Addr  = typename ELF_T::Elf_Addr;
-  using Elf_FileEntry  = typename ELF_T::Elf_FileEntry;
-
-  VectorStream stream{description()};
-
-  auto res_count = stream.read_conv<Elf_Addr>();
-  if (!res_count) {
-    return;
-  }
-
-  const auto count = *res_count;
-  const auto res_page_size = stream.read_conv<Elf_Addr>();
-
-  if (!res_page_size) {
-    LIEF_ERR("Can't read CoreFile.page_size");
-    return;
-  }
-
-  if (count > MAX_ENTRIES) {
-    LIEF_ERR("Too many entries ({} while limited at {})", count, MAX_ENTRIES);
-    return;
-  }
-
-  page_size_ = *res_page_size;
-  files_.resize(count);
-
-  for (uint32_t idx = 0; idx < count; idx++) {
-    auto res_entry = stream.read_conv<Elf_FileEntry>();
-    if (!res_entry) {
-      break;
-    }
-    const auto entry = *res_entry;
-    files_[idx] = {entry.start, entry.end, entry.file_ofs, {}};
-  }
-
-  for (uint32_t idx = 0; idx < count; idx++) {
-    auto res_path = stream.read_string();
-    if (!res_path) {
-      break;
-    }
-    std::string path = std::move(*res_path);
-    if (!path.empty()) {
-      files_[idx].path = std::move(path);
-    }
-  }
-}
-
-template <typename ELF_T>
-void CoreFile::build_() {
-  using Elf_Addr  = typename ELF_T::Elf_Addr;
-  using Elf_FileEntry  = typename ELF_T::Elf_FileEntry;
-
-  Note::description_t& desc = description();
-
-  auto cnt = static_cast<Elf_Addr>(count());
-  auto page_size = static_cast<Elf_Addr>(page_size_);
-
-  vector_iostream raw_output;
-  size_t desc_part_size = sizeof(Elf_Addr) * 2 + cnt * sizeof(Elf_FileEntry);
-  raw_output.reserve(desc_part_size);
-
-  raw_output.write_conv<Elf_Addr>(cnt);
-  raw_output.write_conv<Elf_Addr>(page_size);
-  for (const CoreFileEntry& entry: files_) {
-    const Elf_FileEntry raw_entry = {
-      static_cast<Elf_Addr>(entry.start),
-      static_cast<Elf_Addr>(entry.end),
-      static_cast<Elf_Addr>(entry.file_ofs)
-    };
-    raw_output.write_conv<Elf_FileEntry>(raw_entry);
-  }
-  for (const CoreFileEntry& entry: files_) {
-    raw_output.write(entry.path);
-  }
-  desc = std::move(raw_output.raw());
-}
-
-} // namespace ELF
-} // namespace LIEF
diff --git a/src/ELF/NoteDetails/core/CorePrPsInfo.cpp b/src/ELF/NoteDetails/core/CorePrPsInfo.cpp
index b2b585229b..62499133a9 100644
--- a/src/ELF/NoteDetails/core/CorePrPsInfo.cpp
+++ b/src/ELF/NoteDetails/core/CorePrPsInfo.cpp
@@ -18,146 +18,120 @@
 #include <sstream>
 
 #include "LIEF/ELF/hash.hpp"
-
 #include "ELF/Structures.hpp"
 
-#include "CorePrPsInfo.tcc"
+#include "LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp"
+#include "LIEF/BinaryStream/SpanStream.hpp"
+#include "LIEF/iostream.hpp"
+
+#include "spdlog/fmt/fmt.h"
 
 namespace LIEF {
 namespace ELF {
 
-CorePrPsInfo::CorePrPsInfo(Note& note):
-  NoteDetails::NoteDetails{note},
-  flags_(0),
-  uid_(0),
-  gid_(0),
-  pid_(0),
-  ppid_(0),
-  pgrp_(0),
-  sid_(0)
-{}
-
-CorePrPsInfo CorePrPsInfo::make(Note& note) {
-  CorePrPsInfo pinfo(note);
-  pinfo.parse();
-  return pinfo;
-}
-
-CorePrPsInfo* CorePrPsInfo::clone() const {
-  return new CorePrPsInfo(*this);
-}
-
-std::string CorePrPsInfo::file_name() const {
-  return file_name_;
-}
-
-uint64_t CorePrPsInfo::flags() const {
-  return flags_;
-}
-
-uint32_t CorePrPsInfo::uid() const {
-  return uid_;
-}
+template<class ELF_T>
+inline result<CorePrPsInfo::info_t>
+get_info_impl(const Note::description_t& description) {
+  using Elf_Prpsinfo = typename ELF_T::Elf_Prpsinfo;
+  CorePrPsInfo::info_t info;
+  auto stream = SpanStream::from_vector(description);
+  if (!stream) {
+    return make_error_code(get_error(stream));
+  }
+  auto raw = stream->read<Elf_Prpsinfo>();
+  if (!raw) {
+    return make_error_code(get_error(raw));
+  }
 
-uint32_t CorePrPsInfo::gid() const {
-  return gid_;
-}
+  info.state    = raw->pr_state;
+  info.sname    = raw->pr_sname;
+  info.zombie   = static_cast<bool>(raw->pr_zomb);
+  info.nice     = raw->pr_nice;
+  info.flag     = raw->pr_flag;
+  info.uid      = raw->pr_uid;
+  info.gid      = raw->pr_gid;
+  info.pid      = raw->pr_pid;
+  info.ppid     = raw->pr_ppid;
+  info.pgrp     = raw->pr_pgrp;
+  info.sid      = raw->pr_sid;
+  info.filename = std::string(raw->pr_fname, sizeof(Elf_Prpsinfo::pr_fname));
+  info.args     = std::string(raw->pr_psargs, sizeof(Elf_Prpsinfo::pr_psargs));
 
-int32_t CorePrPsInfo::pid() const {
-  return pid_;
+  return info;
 }
 
-int32_t CorePrPsInfo::ppid() const {
-  return ppid_;
-}
+template<class ELF_T> inline ok_error_t
+write_info_impl(Note::description_t& description,
+                const CorePrPsInfo::info_t& info)
+{
+  using Elf_Prpsinfo = typename ELF_T::Elf_Prpsinfo;
+  Elf_Prpsinfo raw;
+  std::memset(reinterpret_cast<void*>(&raw), 0, sizeof(Elf_Prpsinfo));
 
-int32_t CorePrPsInfo::pgrp() const {
-  return pgrp_;
-}
 
-int32_t CorePrPsInfo::sid() const {
-  return sid_;
-}
+  raw.pr_state = info.state;
+  raw.pr_sname = info.sname;
+  raw.pr_zomb = static_cast<char>(info.zombie);
+  raw.pr_nice = info.nice;
+  raw.pr_flag = info.flag;
+  raw.pr_uid = info.uid;
+  raw.pr_gid = info.gid;
+  raw.pr_pid = info.pid;
+  raw.pr_ppid = info.ppid;
+  raw.pr_pgrp = info.pgrp;
+  raw.pr_sid = info.sid;
 
-void CorePrPsInfo::file_name(const std::string& file_name) {
-  file_name_ = file_name;
-  build();
-}
+  std::string filename = info.filename;
+  filename.resize(sizeof(Elf_Prpsinfo::pr_fname));
 
-void CorePrPsInfo::flags(uint64_t flags) {
-  flags_ = flags;
-  build();
-}
+  std::string args = info.args;
+  args.resize(sizeof(Elf_Prpsinfo::pr_psargs));
 
-void CorePrPsInfo::uid(uint32_t uid) {
-  uid_ = uid;
-  build();
-}
+  std::move(filename.begin(), filename.end(), raw.pr_fname);
+  std::move(args.begin(), args.end(), raw.pr_psargs);
 
-void CorePrPsInfo::gid(uint32_t gid) {
-  gid_ = gid;
-  build();
+  vector_iostream ios;
+  ios.write(raw);
+  ios.move(description);
+  return ok_t();
 }
 
-void CorePrPsInfo::pid(int32_t pid) {
-  pid_ = pid;
-  build();
-}
 
-void CorePrPsInfo::ppid(int32_t ppid) {
-  ppid_ = ppid;
-  build();
+result<CorePrPsInfo::info_t> CorePrPsInfo::info() const {
+  return class_ == ELF_CLASS::ELFCLASS32 ?
+                   get_info_impl<details::ELF32>(description_) :
+                   get_info_impl<details::ELF64>(description_);
 }
 
-void CorePrPsInfo::pgrp(int32_t pgrp) {
-  pgrp_ = pgrp;
-  build();
-}
 
-void CorePrPsInfo::sid(int32_t sid) {
-  sid_ = sid;
-  build();
+void CorePrPsInfo::info(const info_t& info) {
+  class_ == ELF_CLASS::ELFCLASS32 ?
+            write_info_impl<details::ELF32>(description_, info) :
+            write_info_impl<details::ELF64>(description_, info);
 }
 
 void CorePrPsInfo::accept(Visitor& visitor) const {
   visitor.visit(*this);
 }
 
-
-
 void CorePrPsInfo::dump(std::ostream& os) const {
-  os << std::left;
-  os << std::setw(12) << std::setfill(' ') << "File name: " << std::dec << file_name() << std::endl;
-  os << std::setw(12) << std::setfill(' ') << "UID: " << std::dec << uid() << std::endl;
-  os << std::setw(12) << std::setfill(' ') << "GID: " << std::dec << gid() << std::endl;
-  os << std::setw(12) << std::setfill(' ') << "PID: " << std::dec << pid() << std::endl;
-  os << std::setw(12) << std::setfill(' ') << "PPID: " << std::dec << ppid() << std::endl;
-  os << std::setw(12) << std::setfill(' ') << "PGRP: " << std::dec << pgrp() << std::endl;
-  os << std::setw(12) << std::setfill(' ') << "SID: " << std::dec << sid() << std::endl;
-}
-
-void CorePrPsInfo::parse() {
-  if (binary()->type() == ELF_CLASS::ELFCLASS64) {
-    parse_<details::ELF64>();
-  } else if (binary()->type() == ELF_CLASS::ELFCLASS32) {
-    parse_<details::ELF32>();
-  }
-}
-
-void CorePrPsInfo::build() {
-  if (binary()->type() == ELF_CLASS::ELFCLASS64) {
-    build_<details::ELF64>();
-  } else if (binary()->type() == ELF_CLASS::ELFCLASS32) {
-    build_<details::ELF32>();
+  Note::dump(os);
+  auto info_res = info();
+  if (!info_res) {
+    return;
   }
+  os << '\n';
+  os << fmt::format("  Path: {} (args: {})\n",
+                    info_res->filename_stripped(), info_res->args_stripped())
+     << fmt::format("  UID: {:04d} GID: {:04d} PID: {:04d}\n",
+                    info_res->uid, info_res->gid, info_res->pid)
+     << fmt::format("  PPID: {:04d} PGRP: {:04d} SID: {:04d}\n",
+                    info_res->ppid, info_res->pgrp, info_res->sid)
+     << fmt::format("  Flag: 0x{:04x} Nice: {} Zombie: {}\n",
+                    info_res->flag, info_res->nice, info_res->zombie)
+     << fmt::format("  State: 0x{:x} State Name: {}\n",
+                    info_res->state, info_res->sname);
 }
 
-std::ostream& operator<<(std::ostream& os, const CorePrPsInfo& note) {
-  note.dump(os);
-  return os;
-}
-
-CorePrPsInfo::~CorePrPsInfo() = default;
-
 } // namespace ELF
 } // namespace LIEF
diff --git a/src/ELF/NoteDetails/core/CorePrPsInfo.tcc b/src/ELF/NoteDetails/core/CorePrPsInfo.tcc
deleted file mode 100644
index 569b31a07b..0000000000
--- a/src/ELF/NoteDetails/core/CorePrPsInfo.tcc
+++ /dev/null
@@ -1,82 +0,0 @@
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <algorithm>
-
-
-#include "LIEF/utils.hpp"
-
-#include "LIEF/ELF/NoteDetails/core/CorePrPsInfo.hpp"
-#include "LIEF/ELF/Note.hpp"
-#include "LIEF/ELF/Binary.hpp"
-
-namespace LIEF {
-namespace ELF {
-
-template <typename ELF_T>
-void CorePrPsInfo::parse_() {
-  using Elf_Prpsinfo  = typename ELF_T::Elf_Prpsinfo;
-
-  const Note::description_t& desc = description();
-
-  if (desc.size() < sizeof(Elf_Prpsinfo)) {
-    return;
-  }
-
-  const auto* info = reinterpret_cast<const Elf_Prpsinfo*>(desc.data());
-
-  // parse info structure
-  file_name_ = std::string(info->pr_fname, sizeof(info->pr_fname)).c_str();
-  flags_     = info->pr_flag;
-  uid_       = info->pr_uid;
-  gid_       = info->pr_gid;
-  pid_       = info->pr_pid;
-  ppid_      = info->pr_ppid;
-  pgrp_      = info->pr_pgrp;
-  sid_       = info->pr_sid;
-}
-
-template <typename ELF_T>
-void CorePrPsInfo::build_() {
-  using Elf_Prpsinfo  = typename ELF_T::Elf_Prpsinfo;
-  Note::description_t& desc = description();
-  constexpr size_t desc_size = sizeof(Elf_Prpsinfo);
-  if (desc.size() < desc_size) {
-    desc.resize(desc_size);
-  }
-
-  auto* info = reinterpret_cast<Elf_Prpsinfo*>(desc.data());
-  // update info structure
-  const size_t fname_size = sizeof(info->pr_fname) - 1;
-
-  std::string fname = file_name_;
-  fname.resize(fname_size, 0);
-
-  std::move(
-      std::begin(file_name_),
-      std::end(file_name_),
-      info->pr_fname);
-
-  info->pr_flag = flags_;
-  info->pr_uid  = uid_;
-  info->pr_gid  = gid_;
-  info->pr_pid  = pid_;
-  info->pr_ppid = ppid_;
-  info->pr_pgrp = pgrp_;
-  info->pr_sid  = sid_;
-}
-
-} // namespace ELF
-} // namespace LIEF
diff --git a/src/ELF/NoteDetails/core/CorePrStatus.cpp b/src/ELF/NoteDetails/core/CorePrStatus.cpp
index f6454ee5d6..e6aaca7cfd 100644
--- a/src/ELF/NoteDetails/core/CorePrStatus.cpp
+++ b/src/ELF/NoteDetails/core/CorePrStatus.cpp
@@ -18,358 +18,557 @@
 #include <sstream>
 
 #include "logging.hpp"
+#include "frozen.hpp"
+#include "LIEF/BinaryStream/SpanStream.hpp"
+#include "LIEF/iostream.hpp"
 
 #include "LIEF/ELF/hash.hpp"
 #include "LIEF/ELF/EnumToString.hpp"
-#include "LIEF/ELF/Binary.hpp"
+#include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
 #include "ELF/Structures.hpp"
 
-#include "CorePrStatus.tcc"
-
 namespace LIEF {
 namespace ELF {
-class Note;
 
-CorePrStatus::CorePrStatus(Note& note):
-  NoteDetails::NoteDetails{note}
-{}
+template<class ELF_T>
+inline CorePrStatus::pr_status_t
+get_status_impl(const Note::description_t& description) {
+  using Elf_Prstatus  = typename ELF_T::Elf_Prstatus;
+  CorePrStatus::pr_status_t status;
 
-CorePrStatus CorePrStatus::make(Note& note) {
-  CorePrStatus pinfo(note);
-  pinfo.parse();
-  return pinfo;
-}
+  auto stream = SpanStream::from_vector(description);
+  if (!stream) {
+    return {};
+  }
 
-CorePrStatus* CorePrStatus::clone() const {
-  return new CorePrStatus(*this);
-}
+  auto raw_pr_status = stream->read<Elf_Prstatus>();
+  if (!raw_pr_status) {
+    return {};
+  }
 
-const CorePrStatus::reg_context_t& CorePrStatus::reg_context() const {
-  return ctx_;
-}
+  status.info.signo = raw_pr_status->pr_info.si_signo;
+  status.info.code  = raw_pr_status->pr_info.si_code;
+  status.info.err = raw_pr_status->pr_info.si_errno;
 
+  status.cursig  = raw_pr_status->pr_cursig;
+  status.reserved   = raw_pr_status->reserved;
+  status.sigpend = raw_pr_status->pr_sigpend;
+  status.sighold = raw_pr_status->pr_sighold;
 
-const CorePrStatus::siginfo_t& CorePrStatus::siginfo() const {
-  return siginfo_;
-}
+  status.pid  = raw_pr_status->pr_pid;
+  status.ppid = raw_pr_status->pr_ppid;
+  status.pgrp = raw_pr_status->pr_pgrp;
+  status.sid  = raw_pr_status->pr_sid;
 
-uint16_t CorePrStatus::current_sig() const {
-  return cursig_;
-}
+  status.utime.sec = raw_pr_status->pr_utime.tv_sec;
+  status.utime.usec = raw_pr_status->pr_utime.tv_usec;
 
-uint64_t CorePrStatus::sigpend() const {
-  return sigpend_;
-}
+  status.stime.sec = raw_pr_status->pr_stime.tv_sec;
+  status.stime.usec = raw_pr_status->pr_stime.tv_usec;
 
-uint64_t CorePrStatus::sighold() const {
-  return sighold_;
-}
+  status.cutime.sec = raw_pr_status->pr_cutime.tv_sec;
+  status.cutime.usec = raw_pr_status->pr_cutime.tv_usec;
 
-int32_t CorePrStatus::pid() const {
-  return pid_;
-}
+  status.cstime.sec = raw_pr_status->pr_cstime.tv_sec;
+  status.cstime.usec = raw_pr_status->pr_cstime.tv_usec;
 
-int32_t CorePrStatus::ppid() const {
-  return ppid_;
+  return status;
 }
 
-int32_t CorePrStatus::pgrp() const {
-  return pgrp_;
-}
+template<class ELF_T>
+inline Note::description_t write_status_impl(const CorePrStatus::pr_status_t& status) {
+  using Elf_Prstatus  = typename ELF_T::Elf_Prstatus;
+  Elf_Prstatus raw_pr_status;
 
-int32_t CorePrStatus::sid() const {
-  return sid_;
-}
+  raw_pr_status.pr_info.si_signo = status.info.signo;
+  raw_pr_status.pr_info.si_code = status.info.code;
+  raw_pr_status.pr_info.si_errno = status.info.err;
 
-CorePrStatus::timeval_t CorePrStatus::utime() const {
-  return utime_;
-}
+  raw_pr_status.pr_cursig = status.cursig;
+  raw_pr_status.reserved = status.reserved;
+  raw_pr_status.pr_sigpend = static_cast<decltype(raw_pr_status.pr_sigpend)>(status.sigpend);
+  raw_pr_status.pr_sighold = static_cast<decltype(raw_pr_status.pr_sighold)>(status.sighold);
 
-CorePrStatus::timeval_t CorePrStatus::stime() const {
-  return stime_;
-}
+  raw_pr_status.pr_pid = status.pid;
+  raw_pr_status.pr_ppid = status.ppid;
+  raw_pr_status.pr_pgrp = status.pgrp;
+  raw_pr_status.pr_sid = status.sid;
 
-CorePrStatus::timeval_t CorePrStatus::cutime() const {
-  return cutime_;
-}
+  raw_pr_status.pr_utime.tv_sec = status.utime.sec;
+  raw_pr_status.pr_utime.tv_usec = status.utime.usec;
 
-CorePrStatus::timeval_t CorePrStatus::cstime() const {
-  return cstime_;
-}
+  raw_pr_status.pr_cutime.tv_sec = status.cutime.sec;
+  raw_pr_status.pr_cutime.tv_usec = status.cutime.usec;
+
+  raw_pr_status.pr_stime.tv_sec = status.stime.sec;
+  raw_pr_status.pr_stime.tv_usec = status.stime.usec;
 
+  raw_pr_status.pr_cstime.tv_sec = status.cstime.sec;
+  raw_pr_status.pr_cstime.tv_usec = status.cstime.usec;
+
+  vector_iostream ios;
+  ios.write<Elf_Prstatus>(raw_pr_status);
+  return ios.raw();
+}
 
-uint64_t CorePrStatus::get(CorePrStatus::REGISTERS reg, bool* error) const {
-  if (!has(reg)) {
-    if (error != nullptr) {
-      *error = true;
+template<class REG_T>
+inline result<uint64_t>
+get_reg_impl(REG_T reg, const Note::description_t& description,
+             ELF_CLASS cls, ARCH arch)
+{
+  if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::X86>)
+  {
+    if (arch != ARCH::EM_386) {
+      return make_error_code(lief_errors::not_found);
     }
-    return 0;
   }
-
-  if (error != nullptr) {
-    *error = false;
+  else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::X86_64>)
+  {
+    if (arch != ARCH::EM_X86_64) {
+      return make_error_code(lief_errors::not_found);
+    }
+  }
+  else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::ARM>)
+  {
+    if (arch != ARCH::EM_ARM) {
+      return make_error_code(lief_errors::not_found);
+    }
+  }
+  else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::AARCH64>)
+  {
+    if (arch != ARCH::EM_AARCH64) {
+      return make_error_code(lief_errors::not_found);
+    }
+  }
+  else {
+    LIEF_WARN("Architecture not supported");
+    return make_error_code(lief_errors::not_found);
   }
-  return ctx_.at(reg);
-}
 
-bool CorePrStatus::has(CorePrStatus::REGISTERS reg) const {
-  return ctx_.find(reg) != std::end(ctx_);
-}
+  auto pos = static_cast<int32_t>(reg);
+  if (pos < 0 || pos >= static_cast<int32_t>(REG_T::_COUNT)) {
+    return make_error_code(lief_errors::not_found);
+  }
 
+  auto stream = SpanStream::from_vector(description);
+  if (!stream) {
+    return make_error_code(lief_errors::not_found);
+  }
 
-uint64_t CorePrStatus::pc() const {
-  const ARCH arch = binary()->header().machine_type();
-  switch (arch) {
-    case ARCH::EM_386:     return get(REGISTERS::X86_EIP);
-    case ARCH::EM_X86_64:  return get(REGISTERS::X86_64_RIP);
-    case ARCH::EM_ARM:     return get(REGISTERS::ARM_R15);
-    case ARCH::EM_AARCH64: return get(REGISTERS::AARCH64_PC);
-    default:
-      {
-        LIEF_WARN("{} not supported", to_string(arch));
-        return 0;
-      }
+  if (cls == ELF_CLASS::ELFCLASS32) {
+    stream->increment_pos(sizeof(details::ELF32::Elf_Prstatus));
+    stream->increment_pos(pos * sizeof(uint32_t));
+    auto value = stream->read<uint32_t>();
+    if (!value) {
+      return make_error_code(lief_errors::corrupted);
+    }
+    return *value;
   }
-}
 
-uint64_t CorePrStatus::sp() const {
-  const ARCH arch = binary()->header().machine_type();
-  switch (arch) {
-    case ARCH::EM_386:     return get(REGISTERS::X86_ESP);
-    case ARCH::EM_X86_64:  return get(REGISTERS::X86_64_RSP);
-    case ARCH::EM_ARM:     return get(REGISTERS::ARM_R13);
-    case ARCH::EM_AARCH64: return get(REGISTERS::AARCH64_X31);
-    default:
-      {
-        LIEF_WARN("{} not supported", to_string(arch));
-        return 0;
-      }
+  if (cls == ELF_CLASS::ELFCLASS64) {
+    stream->increment_pos(sizeof(details::ELF64::Elf_Prstatus));
+    stream->increment_pos(pos * sizeof(uint64_t));
+    auto value = stream->read<uint64_t>();
+    if (!value) {
+      return make_error_code(lief_errors::corrupted);
+    }
+    return *value;
   }
 
+  return make_error_code(lief_errors::not_found);
 }
 
+template<class REG_T>
+inline ok_error_t
+set_reg_impl(REG_T reg, uint64_t value, Note::description_t& description,
+             ELF_CLASS cls, ARCH arch)
+{
+  if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::X86>)
+  {
+    if (arch != ARCH::EM_386) {
+      return make_error_code(lief_errors::not_found);
+    }
+  }
+  else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::X86_64>)
+  {
+    if (arch != ARCH::EM_X86_64) {
+      return make_error_code(lief_errors::not_found);
+    }
+  }
+  else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::ARM>)
+  {
+    if (arch != ARCH::EM_ARM) {
+      return make_error_code(lief_errors::not_found);
+    }
+  }
+  else if constexpr (std::is_same_v<REG_T, CorePrStatus::Registers::AARCH64>)
+  {
+    if (arch != ARCH::EM_AARCH64) {
+      return make_error_code(lief_errors::not_found);
+    }
+  }
+  else {
+    LIEF_WARN("Architecture not supported");
+    return make_error_code(lief_errors::not_found);
+  }
 
-void CorePrStatus::siginfo(const CorePrStatus::siginfo_t& siginfo) {
-  siginfo_ = siginfo;
-  build();
-}
+  auto pos = static_cast<int32_t>(reg);
+  if (pos < 0 || pos >= static_cast<int32_t>(REG_T::_COUNT)) {
+    return make_error_code(lief_errors::not_found);
+  }
 
-void CorePrStatus::current_sig(uint16_t current_sig) {
-  cursig_ = current_sig;
-  build();
-}
+  size_t offset = 0;
+  vector_iostream os;
+  os.write(description);
 
-void CorePrStatus::sigpend(uint64_t sigpend) {
-  sigpend_ = sigpend;
-  build();
-}
+  if (cls == ELF_CLASS::ELFCLASS32) {
+    offset += sizeof(details::ELF32::Elf_Prstatus) + pos * sizeof(uint32_t);
+    os.seekp(offset);
+    os.write<uint32_t>(value);
+    return ok();
+  }
 
-void CorePrStatus::sighold(uint64_t sighold) {
-  sighold_ = sighold;
-  build();
-}
+  if (cls == ELF_CLASS::ELFCLASS64) {
+    offset += sizeof(details::ELF64::Elf_Prstatus) + pos * sizeof(uint64_t);
+    os.seekp(offset);
+    os.write<uint64_t>(value);
+    os.move(description);
+    return ok();
+  }
 
-void CorePrStatus::pid(int32_t pid) {
-  pid_ = pid;
-  build();
+  return make_error_code(lief_errors::not_found);
 }
 
-void CorePrStatus::ppid(int32_t ppid) {
-  ppid_ = ppid;
-  build();
+std::vector<uint64_t> CorePrStatus::register_values() const {
+  std::vector<uint64_t> values;
+  switch (arch_) {
+    case ARCH::EM_X86_64:
+      {
+        using Reg = Registers::X86_64;
+        const auto count = static_cast<size_t>(Reg::_COUNT);
+        values.reserve(count);
+        for (size_t i = 0; i < count; ++i) {
+          if (auto val = get(Reg(i))) {
+            values.push_back(std::move(*val));
+          } else {
+            return {};
+          }
+        }
+        return values;
+      }
+    case ARCH::EM_386:
+      {
+        using Reg = Registers::X86;
+        const auto count = static_cast<size_t>(Reg::_COUNT);
+        values.reserve(count);
+        for (size_t i = 0; i < count; ++i) {
+          if (auto val = get(Reg(i))) {
+            values.push_back(std::move(*val));
+          } else {
+            return {};
+          }
+        }
+        return values;
+      }
+    case ARCH::EM_ARM:
+      {
+        using Reg = Registers::ARM;
+        const auto count = static_cast<size_t>(Reg::_COUNT);
+        values.reserve(count);
+        for (size_t i = 0; i < count; ++i) {
+          if (auto val = get(Reg(i))) {
+            values.push_back(std::move(*val));
+          } else {
+            return {};
+          }
+        }
+        return values;
+      }
+    case ARCH::EM_AARCH64:
+      {
+        using Reg = Registers::AARCH64;
+        const auto count = static_cast<size_t>(Reg::_COUNT);
+        values.reserve(count);
+        for (size_t i = 0; i < count; ++i) {
+          if (auto val = get(Reg(i))) {
+            values.push_back(std::move(*val));
+          } else {
+            return {};
+          }
+        }
+        return values;
+      }
+    default: return {};
+  }
 }
 
-void CorePrStatus::pgrp(int32_t pgrp) {
-  pgrp_ = pgrp;
-  build();
+CorePrStatus::pr_status_t CorePrStatus::status() const {
+  if (class_ == ELF_CLASS::ELFCLASS32) {
+    return get_status_impl<details::ELF32>(description_);
+  }
+  return get_status_impl<details::ELF64>(description_);
 }
 
-void CorePrStatus::sid(int32_t sid) {
-  sid_ = sid;
-  build();
-}
+void CorePrStatus::status(const pr_status_t& status) {
+  Note::description_t description = class_ == ELF_CLASS::ELFCLASS32 ?
+      write_status_impl<details::ELF32>(status) :
+      write_status_impl<details::ELF64>(status);
 
-void CorePrStatus::utime(CorePrStatus::timeval_t utime) {
-  utime_ = utime;
-  build();
-}
+  if (description.empty()) {
+    return;
+  }
 
-void CorePrStatus::stime(CorePrStatus::timeval_t stime) {
-  stime_ = stime;
-  build();
-}
+  if (description_.size() < description.size()) {
+    description_.resize(description.size());
+  }
 
-void CorePrStatus::cutime(CorePrStatus::timeval_t cutime) {
-  cutime_ = cutime;
-  build();
+  std::move(description.begin(), description.end(), description_.begin());
 }
 
-void CorePrStatus::cstime(CorePrStatus::timeval_t cstime) {
-  cstime_ = cstime;
-  build();
+result<uint64_t> CorePrStatus::get(Registers::X86 reg) const {
+  return get_reg_impl<Registers::X86>(reg, description_, class_, arch_);
 }
 
-void CorePrStatus::reg_context(const reg_context_t& ctx) {
-  ctx_ = ctx;
-  build();
+result<uint64_t> CorePrStatus::get(Registers::X86_64 reg) const {
+  return get_reg_impl<Registers::X86_64>(reg, description_, class_, arch_);
 }
 
-bool CorePrStatus::set(REGISTERS reg, uint64_t value) {
-  ctx_[reg] = value;
-  build();
-  return true;
+result<uint64_t> CorePrStatus::get(Registers::ARM reg) const {
+  return get_reg_impl<Registers::ARM>(reg, description_, class_, arch_);
 }
 
-void CorePrStatus::accept(Visitor& visitor) const {
-  visitor.visit(*this);
+result<uint64_t> CorePrStatus::get(Registers::AARCH64 reg) const {
+  return get_reg_impl<Registers::AARCH64>(reg, description_, class_, arch_);
 }
 
-
-
-uint64_t& CorePrStatus::operator[](REGISTERS reg) {
-  return ctx_[reg];
+ok_error_t CorePrStatus::set(Registers::X86 reg, uint64_t value) {
+  return set_reg_impl<Registers::X86>(reg, value, description_, class_, arch_);
 }
 
-void CorePrStatus::dump(std::ostream& os) const {
-  static constexpr size_t WIDTH = 16;
-  os << std::left;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Siginfo: "<< std::dec;
-    dump(os, siginfo());
-  os << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Current Signal: "<< std::dec
-     << current_sig() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Pending signal: "<< std::dec
-     << sigpend() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Signal held: "<< std::dec
-     << sighold() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "PID: "<< std::dec
-     << pid() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "PPID: "<< std::dec
-     << ppid() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "PGRP: "<< std::dec
-     << pgrp() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "SID: "<< std::dec
-     << sid() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "utime: "<< std::dec;
-    dump(os, utime());
-  os << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "stime: "<< std::dec;
-    dump(os, stime());
-  os << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "cutime: "<< std::dec;
-    dump(os, cutime());
-  os << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "cstime: "<< std::dec;
-    dump(os, cstime());
-  os << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Registers: "<< std::dec << std::endl;
-    dump(os, reg_context());
-  os << std::endl;
-
+ok_error_t CorePrStatus::set(Registers::X86_64 reg, uint64_t value) {
+  return set_reg_impl<Registers::X86_64>(reg, value, description_, class_, arch_);
 }
 
-std::ostream& CorePrStatus::dump(std::ostream& os, const CorePrStatus::timeval_t& time) {
-  os << std::dec;
-  os << time.sec << ":" << time.usec;
-  return os;
+ok_error_t CorePrStatus::set(Registers::ARM reg, uint64_t value) {
+  return set_reg_impl<Registers::ARM>(reg, value, description_, class_, arch_);
 }
 
-std::ostream& CorePrStatus::dump(std::ostream& os, const CorePrStatus::siginfo_t& siginfo) {
-  os << std::dec;
-  os << siginfo.si_signo << " - " << siginfo.si_code << " - " << siginfo.si_errno;
-  return os;
+ok_error_t CorePrStatus::set(Registers::AARCH64 reg, uint64_t value) {
+  return set_reg_impl<Registers::AARCH64>(reg, value, description_, class_, arch_);
 }
 
-std::ostream& CorePrStatus::dump(std::ostream& os, const reg_context_t& ctx) {
 
-  for (const auto& reg_val : ctx) {
-    os << std::setw(14) << std::setfill(' ') << to_string(reg_val.first) << ": " << std::hex << std::showbase << reg_val.second << std::endl;
+result<uint64_t> CorePrStatus::pc() const {
+  switch (arch_) {
+    case ARCH::EM_AARCH64: return get(Registers::AARCH64::PC);
+    case ARCH::EM_ARM: return get(Registers::ARM::R15);
+    case ARCH::EM_386: return get(Registers::X86::EIP);
+    case ARCH::EM_X86_64: return get(Registers::X86_64::RIP);
+    default: return make_error_code(lief_errors::not_supported);
   }
-  return os;
 }
 
-
-void CorePrStatus::parse() {
-  if (binary()->type() == ELF_CLASS::ELFCLASS64) {
-    parse_<details::ELF64>();
-  } else if (binary()->type() == ELF_CLASS::ELFCLASS32) {
-    parse_<details::ELF32>();
+result<uint64_t> CorePrStatus::sp() const {
+  switch (arch_) {
+    case ARCH::EM_AARCH64: return get(Registers::AARCH64::X31);
+    case ARCH::EM_ARM: return get(Registers::ARM::R13);
+    case ARCH::EM_386: return get(Registers::X86::ESP);
+    case ARCH::EM_X86_64: return get(Registers::X86_64::RSP);
+    default: return make_error_code(lief_errors::not_supported);
   }
 }
 
-void CorePrStatus::build() {
-  if (binary()->type() == ELF_CLASS::ELFCLASS64) {
-    build_<details::ELF64>();
-  } else if (binary()->type() == ELF_CLASS::ELFCLASS32) {
-    build_<details::ELF32>();
+result<uint64_t> CorePrStatus::return_value() const {
+  switch (arch_) {
+    case ARCH::EM_AARCH64: return get(Registers::AARCH64::X0);
+    case ARCH::EM_ARM: return get(Registers::ARM::R0);
+    case ARCH::EM_386: return get(Registers::X86::EAX);
+    case ARCH::EM_X86_64: return get(Registers::X86_64::RAX);
+    default: return make_error_code(lief_errors::not_supported);
   }
 }
 
+template<class REG>
+void dump_impl(std::ostream& os, const std::vector<uint64_t>& reg_vals) {
+  for (size_t i = 0; i < reg_vals.size(); ++i) {
+    if constexpr (std::is_void_v<REG>) {
+      os << fmt::format("   0x{:08x}\n", reg_vals[i]);
+    } else {
+      os << fmt::format("   {}: 0x{:08x}\n", to_string(REG(i)), reg_vals[i]);
+    }
+  }
+}
 
-std::pair<size_t, size_t> CorePrStatus::reg_enum_range() const {
-  const ARCH arch = binary()->header().machine_type();
-
-  size_t enum_start = 0;
-  size_t enum_end   = 0;
-
-  switch (arch) {
-    case ARCH::EM_386:
-      {
-        enum_start = static_cast<size_t>(REGISTERS::X86_START) + 1;
-        enum_end  = static_cast<size_t>(REGISTERS::X86_END);
-        break;
-      }
-
-    case ARCH::EM_X86_64:
-      {
-        enum_start = static_cast<size_t>(REGISTERS::X86_64_START) + 1;
-        enum_end  = static_cast<size_t>(REGISTERS::X86_64_END);
-        break;
-      }
-
+void CorePrStatus::dump(std::ostream& os) const {
+  Note::dump(os);
+  const CorePrStatus::pr_status_t& status = this->status();
+  os << '\n'
+     << fmt::format("  PID: {:04d} PPID: {:04d} PGRP: {:04d}\n",
+                    status.pid, status.ppid, status.pgrp)
+     << fmt::format("  SID: {:04d} SIGNO: {:04d} SIGCODE: {:04d}\n",
+                    status.sid, status.info.signo, status.info.code)
+     << fmt::format("  SIGERR: {:04d} SIGPEND: {:04d} SIGHOLD: {:04d}\n",
+                    status.info.err, status.sigpend, status.sighold)
+     << fmt::format("  CURRSIG: 0x{:04d} reserved: {}\n",
+                    status.cursig, status.reserved);
+  const std::vector<uint64_t>& reg_vals = register_values();
+  switch (architecture()) {
     case ARCH::EM_ARM:
-      {
-        enum_start = static_cast<size_t>(REGISTERS::ARM_START) + 1;
-        enum_end  = static_cast<size_t>(REGISTERS::ARM_END);
-        break;
-      }
-
+      dump_impl<CorePrStatus::Registers::ARM>(os, reg_vals); break;
     case ARCH::EM_AARCH64:
-      {
-        enum_start = static_cast<size_t>(REGISTERS::AARCH64_START) + 1;
-        enum_end  = static_cast<size_t>(REGISTERS::AARCH64_END);
-        break;
-      }
-
+      dump_impl<CorePrStatus::Registers::AARCH64>(os, reg_vals); break;
+    case ARCH::EM_386:
+      dump_impl<CorePrStatus::Registers::X86>(os, reg_vals); break;
+    case ARCH::EM_X86_64:
+      dump_impl<CorePrStatus::Registers::X86_64>(os, reg_vals); break;
     default:
-      {
-        LIEF_WARN("{} not supported", to_string(arch));
-      }
+      dump_impl<void>(os, reg_vals); break;
+
   }
-  return {enum_start, enum_end};
 }
 
-std::ostream& operator<<(std::ostream& os, const CorePrStatus& note) {
-  note.dump(os);
-  return os;
+void CorePrStatus::accept(Visitor& visitor) const {
+  visitor.visit(*this);
 }
 
+const char* to_string(CorePrStatus::Registers::X86 e) {
+  #define ENTRY(X) std::pair(CorePrStatus::Registers::X86::X, #X)
+  STRING_MAP enum_strings {
+    ENTRY(EBX),
+    ENTRY(ECX),
+    ENTRY(EDX),
+    ENTRY(ESI),
+    ENTRY(EDI),
+    ENTRY(EBP),
+    ENTRY(EAX),
+    ENTRY(DS),
+    ENTRY(ES),
+    ENTRY(FS),
+    ENTRY(GS),
+    ENTRY(ORIG_EAX),
+    ENTRY(EIP),
+    ENTRY(CS),
+    ENTRY(EFLAGS),
+    ENTRY(ESP),
+    ENTRY(SS),
+  };
+  #undef ENTRY
+
+  if (auto it = enum_strings.find(e); it != enum_strings.end()) {
+    return it->second;
+  }
+  return "UNKNOWN";
+}
+
+const char* to_string(CorePrStatus::Registers::X86_64 e) {
+  #define ENTRY(X) std::pair(CorePrStatus::Registers::X86_64::X, #X)
+  STRING_MAP enums2str {
+    ENTRY(R15),
+    ENTRY(R14),
+    ENTRY(R13),
+    ENTRY(R12),
+    ENTRY(RBP),
+    ENTRY(RBX),
+    ENTRY(R11),
+    ENTRY(R10),
+    ENTRY(R9),
+    ENTRY(R8),
+    ENTRY(RAX),
+    ENTRY(RCX),
+    ENTRY(RDX),
+    ENTRY(RSI),
+    ENTRY(RDI),
+    ENTRY(ORIG_RAX),
+    ENTRY(RIP),
+    ENTRY(CS),
+    ENTRY(EFLAGS),
+    ENTRY(RSP),
+    ENTRY(SS),
+  };
+  #undef ENTRY
+
+  if (auto it = enums2str.find(e); it != enums2str.end()) {
+    return it->second;
+  }
+  return "UNKNOWN";
+}
+
+const char* to_string(CorePrStatus::Registers::ARM e) {
+  #define ENTRY(X) std::pair(CorePrStatus::Registers::ARM::X, #X)
+  STRING_MAP enums2str {
+    ENTRY(R0),
+    ENTRY(R1),
+    ENTRY(R2),
+    ENTRY(R3),
+    ENTRY(R4),
+    ENTRY(R5),
+    ENTRY(R6),
+    ENTRY(R7),
+    ENTRY(R8),
+    ENTRY(R9),
+    ENTRY(R10),
+    ENTRY(R11),
+    ENTRY(R12),
+    ENTRY(R13),
+    ENTRY(R14),
+    ENTRY(R15),
+    ENTRY(CPSR),
+  };
+  #undef ENTRY
+
+  if (auto it = enums2str.find(e); it != enums2str.end()) {
+    return it->second;
+  }
 
-
-CorePrStatus::~CorePrStatus() = default;
+  return "UNKNOWN";
+}
+
+const char* to_string(CorePrStatus::Registers::AARCH64 e) {
+  #define ENTRY(X) std::pair(CorePrStatus::Registers::AARCH64::X, #X)
+  STRING_MAP enum_strings {
+    ENTRY(X0),
+    ENTRY(X1),
+    ENTRY(X2),
+    ENTRY(X3),
+    ENTRY(X4),
+    ENTRY(X5),
+    ENTRY(X6),
+    ENTRY(X7),
+    ENTRY(X8),
+    ENTRY(X9),
+    ENTRY(X10),
+    ENTRY(X11),
+    ENTRY(X12),
+    ENTRY(X13),
+    ENTRY(X14),
+    ENTRY(X15),
+    ENTRY(X15),
+    ENTRY(X16),
+    ENTRY(X17),
+    ENTRY(X18),
+    ENTRY(X19),
+    ENTRY(X20),
+    ENTRY(X21),
+    ENTRY(X22),
+    ENTRY(X23),
+    ENTRY(X24),
+    ENTRY(X25),
+    ENTRY(X26),
+    ENTRY(X27),
+    ENTRY(X28),
+    ENTRY(X29),
+    ENTRY(X30),
+    ENTRY(X31),
+    ENTRY(PC),
+    ENTRY(PSTATE),
+  };
+  #undef ENTRY
+
+  if (auto it = enum_strings.find(e); it != enum_strings.end()) {
+    return it->second;
+  }
+  return "UNKNOWN";
+}
 
 } // namespace ELF
 } // namespace LIEF
diff --git a/src/ELF/NoteDetails/core/CorePrStatus.tcc b/src/ELF/NoteDetails/core/CorePrStatus.tcc
deleted file mode 100644
index ee9bd756df..0000000000
--- a/src/ELF/NoteDetails/core/CorePrStatus.tcc
+++ /dev/null
@@ -1,139 +0,0 @@
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include <algorithm>
-
-
-#include "LIEF/utils.hpp"
-#include "LIEF/BinaryStream/VectorStream.hpp"
-#include "LIEF/iostream.hpp"
-
-#include "LIEF/ELF/Note.hpp"
-#include "LIEF/ELF/NoteDetails/core/CorePrStatus.hpp"
-
-namespace LIEF {
-namespace ELF {
-
-template <typename ELF_T>
-void CorePrStatus::parse_() {
-  using Elf_Prstatus  = typename ELF_T::Elf_Prstatus;
-  using uint__        = typename ELF_T::uint;
-
-  const Note::description_t& desc = description();
-  if (desc.size() < sizeof(Elf_Prstatus)) {
-    return;
-  }
-  const auto* status = reinterpret_cast<const Elf_Prstatus*>(desc.data());
-
-  siginfo_.si_signo = status->pr_info.si_signo;
-  siginfo_.si_errno = status->pr_info.si_errno;
-  siginfo_.si_code  = status->pr_info.si_code;
-
-  cursig_  = status->pr_cursig;
-
-  sigpend_ = status->pr_sigpend;
-  sighold_ = status->pr_sighold;
-
-  pid_  = status->pr_pid;
-  ppid_ = status->pr_ppid;
-  pgrp_ = status->pr_pgrp;
-  sid_  = status->pr_sid;
-
-  utime_.sec   = status->pr_utime.tv_sec;
-  utime_.usec  = status->pr_utime.tv_usec;
-
-  stime_.sec   = status->pr_stime.tv_sec;
-  stime_.usec  = status->pr_stime.tv_usec;
-
-  cutime_.sec  = status->pr_cutime.tv_sec;
-  cutime_.usec = status->pr_cutime.tv_usec;
-
-  cstime_.sec  = status->pr_cstime.tv_sec;
-  cstime_.usec = status->pr_cstime.tv_usec;
-
-  size_t enum_start = 0;
-  size_t enum_end   = 0;
-  std::tie(enum_start, enum_end) = reg_enum_range();
-
-  VectorStream stream(std::move(desc));
-  stream.setpos(sizeof(Elf_Prstatus));
-
-  for (size_t i = enum_start; i < enum_end; ++i) {
-    auto val = stream.read<uint__>();
-    if (!val) {
-      break;
-    }
-    ctx_[static_cast<REGISTERS>(i)] = *val;
-  }
-}
-
-template <typename ELF_T>
-void CorePrStatus::build_() {
-  using Elf_Prstatus  = typename ELF_T::Elf_Prstatus;
-  using uint__        = typename ELF_T::uint;
-
-  Note::description_t& desc = description();
-  Elf_Prstatus status;
-
-  status.pr_info.si_signo  = static_cast<int32_t>(siginfo_.si_signo);
-  status.pr_info.si_code   = static_cast<int32_t>(siginfo_.si_code);
-  status.pr_info.si_errno  = static_cast<int32_t>(siginfo_.si_errno);
-
-  status.pr_cursig         = static_cast<uint16_t>(cursig_);
-  status.reserved          = static_cast<uint16_t>(0xFE19);
-
-  status.pr_sigpend        = static_cast<uint__>(sigpend_);
-  status.pr_sighold        = static_cast<uint__>(sighold_);
-
-  status.pr_pid            = static_cast<int32_t>(pid_);
-  status.pr_ppid           = static_cast<int32_t>(ppid_);
-  status.pr_pgrp           = static_cast<int32_t>(pgrp_);
-  status.pr_sid            = static_cast<int32_t>(sid_);
-
-  status.pr_utime.tv_sec   = static_cast<uint__>(utime_.sec);
-  status.pr_utime.tv_usec  = static_cast<uint__>(utime_.usec);
-
-  status.pr_stime.tv_sec   = static_cast<uint__>(stime_.sec);
-  status.pr_stime.tv_usec  = static_cast<uint__>(stime_.usec);
-
-  status.pr_cutime.tv_sec  = static_cast<uint__>(cutime_.sec);
-  status.pr_cutime.tv_usec = static_cast<uint__>(cutime_.usec);
-
-  status.pr_cstime.tv_sec  = static_cast<uint__>(cstime_.sec);
-  status.pr_cstime.tv_usec = static_cast<uint__>(cstime_.usec);
-
-  vector_iostream raw_output;
-  size_t desc_part_size = sizeof(Elf_Prstatus);
-  raw_output.reserve(desc_part_size);
-  raw_output.write(reinterpret_cast<const uint8_t*>(&status), sizeof(Elf_Prstatus));
-
-  size_t enum_start = 0;
-  size_t enum_end = 0;
-  std::tie(enum_start, enum_end) = reg_enum_range();
-
-  for (size_t i = enum_start; i < enum_end; ++i) {
-    auto reg = static_cast<REGISTERS>(i);
-    auto val = static_cast<uint__>(get(reg));
-    raw_output.write_conv(val);
-  }
-
-  std::vector<uint8_t> raw = raw_output.raw();
-  std::copy(std::begin(raw), std::end(raw),
-            std::begin(desc));
-
-}
-
-} // namespace ELF
-} // namespace LIEF
diff --git a/src/ELF/NoteDetails/core/CoreSigInfo.cpp b/src/ELF/NoteDetails/core/CoreSigInfo.cpp
index 4c0e5faea8..6165140afb 100644
--- a/src/ELF/NoteDetails/core/CoreSigInfo.cpp
+++ b/src/ELF/NoteDetails/core/CoreSigInfo.cpp
@@ -17,108 +17,56 @@
 #include <iomanip>
 #include <sstream>
 
+#include "LIEF/BinaryStream/SpanStream.hpp"
 #include "LIEF/ELF/hash.hpp"
-
-#include "LIEF/ELF/Note.hpp"
 #include "LIEF/ELF/NoteDetails/core/CoreSigInfo.hpp"
 #include "ELF/Structures.hpp"
 
+#include "spdlog/fmt/fmt.h"
+
 namespace LIEF {
 namespace ELF {
 
-CoreSigInfo::CoreSigInfo(Note& note):
-  NoteDetails::NoteDetails{note}
-{}
-
-CoreSigInfo CoreSigInfo::make(Note& note) {
-  CoreSigInfo pinfo(note);
-  pinfo.parse();
-  return pinfo;
-}
-
-CoreSigInfo* CoreSigInfo::clone() const {
-  return new CoreSigInfo(*this);
-}
+static constexpr auto signo_offset = offsetof(details::Elf_siginfo, si_signo);
+static constexpr auto sigcode_offset = offsetof(details::Elf_siginfo, si_code);
+static constexpr auto si_errno_offset = offsetof(details::Elf_siginfo, si_errno);
 
-int32_t CoreSigInfo::signo() const {
-  return siginfo_.si_signo;
+result<int32_t> CoreSigInfo::signo() const {
+  return read_at<uint32_t>(signo_offset);
 }
 
-int32_t CoreSigInfo::sigcode() const {
-  return siginfo_.si_code;
+result<int32_t> CoreSigInfo::sigcode() const {
+  return read_at<uint32_t>(sigcode_offset);
 }
 
-int32_t CoreSigInfo::sigerrno() const {
-  return siginfo_.si_errno;
+result<int32_t> CoreSigInfo::sigerrno() const {
+  return read_at<uint32_t>(si_errno_offset);
 }
 
-void CoreSigInfo::signo(int32_t signo) {
-  siginfo_.si_signo = signo;
-  build();
+void CoreSigInfo::signo(uint32_t value) {
+  write_at(signo_offset, value);
 }
 
-void CoreSigInfo::sigcode(int32_t sigcode) {
-  siginfo_.si_code = sigcode;
-  build();
+void CoreSigInfo::sigcode(uint32_t value) {
+  write_at(sigcode_offset, value);
 }
 
-void CoreSigInfo::sigerrno(int32_t sigerrno) {
-  siginfo_.si_errno = sigerrno;
-  build();
+void CoreSigInfo::sigerrno(uint32_t value) {
+  write_at(si_errno_offset, value);
 }
 
 void CoreSigInfo::accept(Visitor& visitor) const {
   visitor.visit(*this);
 }
 
-
-
 void CoreSigInfo::dump(std::ostream& os) const {
-  static constexpr size_t WIDTH = 16;
-  os << std::left;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Signo: "<< std::dec
-     << signo() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Code: "<< std::dec
-     << sigcode() << std::endl;
-
-  os << std::setw(WIDTH) << std::setfill(' ') << "Errno: "<< std::dec
-     << sigerrno() << std::endl;
+  Note::dump(os);
+  os << '\n'
+     << fmt::format("  signo: {} code: {} errno: {}\n",
+                    signo().value_or(-1),
+                    sigcode().value_or(-1),
+                    sigerrno().value_or(-1));
 }
 
-
-void CoreSigInfo::parse() {
-  const Note::description_t& desc = description();
-  if (desc.size() < sizeof(details::Elf_siginfo)) {
-    return;
-  }
-  const auto* siginfo = reinterpret_cast<const details::Elf_siginfo*>(desc.data());
-  siginfo_.si_signo = siginfo->si_signo;
-  siginfo_.si_code  = siginfo->si_code;
-  siginfo_.si_errno = siginfo->si_errno;
-}
-
-
-void CoreSigInfo::build() {
-  Note::description_t& desc = description();
-  if (desc.size() < sizeof(details::Elf_siginfo)) {
-    desc.resize(sizeof(details::Elf_siginfo));
-  }
-  std::copy(
-    reinterpret_cast<const uint8_t*>(&siginfo_),
-    reinterpret_cast<const uint8_t*>(&siginfo_) + sizeof(details::Elf_siginfo),
-    std::begin(desc));
-}
-
-
-std::ostream& operator<<(std::ostream& os, const CoreSigInfo& note) {
-  note.dump(os);
-  return os;
-}
-
-
-CoreSigInfo::~CoreSigInfo() = default;
-
 } // namespace ELF
 } // namespace LIEF
diff --git a/src/ELF/Parser.cpp b/src/ELF/Parser.cpp
index 17749e2d7f..7f1c87ec1c 100644
--- a/src/ELF/Parser.cpp
+++ b/src/ELF/Parser.cpp
@@ -30,7 +30,6 @@
 #include "LIEF/ELF/Symbol.hpp"
 #include "LIEF/ELF/Note.hpp"
 #include "LIEF/ELF/SysvHash.hpp"
-#include "LIEF/ELF/NoteDetails/AndroidNote.hpp"
 
 #include "ELF/DataHandler/Handler.hpp"
 
@@ -502,92 +501,88 @@ ok_error_t Parser::parse_symbol_sysv_hash(uint64_t offset) {
   return ok();
 }
 
-ok_error_t Parser::parse_notes(uint64_t offset, uint64_t size) {
-  LIEF_DEBUG("== Parsing note segment ==");
+#if 0
+std::unique_ptr<Note> Parser::get_note(uint32_t type, std::string name,
+                                       std::vector<uint8_t> desc_bytes)
+{
+  const E_TYPE ftype = binary_->header().file_type();
 
-  stream_->setpos(offset);
-  uint64_t last_offset = offset + size;
+  auto conv = Note::convert_type(ftype, type, name);
+  if (!conv) {
+    LIEF_WARN("Note type: 0x{:x} is not supported for owner: '{}'", type, name);
+    return std::make_unique<Note>(std::move(name), Note::TYPE::UNKNOWN, type,
+                                  std::move(desc_bytes));
+  }
 
-  while(stream_->pos() < last_offset) {
-    const size_t pos = stream_->pos();
-    auto res_namesz = stream_->read_conv<uint32_t>();
-    if (!res_namesz) {
-      break;
-    }
+  Note::TYPE ntype = *conv;
 
-    const auto namesz = *res_namesz;
-    LIEF_DEBUG("[0x{:06x}] Name size: 0x{:x}", pos, namesz);
+  if (ntype != Note::TYPE::GNU_BUILD_ATTRIBUTE_FUNC &&
+      ntype != Note::TYPE::GNU_BUILD_ATTRIBUTE_OPEN)
+  {
+    name = name.c_str();
+  }
 
-    auto res_descz = stream_->read_conv<uint32_t>();
-    if (!res_descz) {
-      break;
-    }
+  const ARCH arch = binary_->header().machine_type();
+  const ELF_CLASS cls = binary_->header().identity_class();
 
-    uint32_t descsz = std::min(*res_descz, Parser::MAX_NOTE_DESCRIPTION);
-    LIEF_DEBUG("Description size: 0x{:x}", descsz);
+  if (cls != ELF_CLASS::ELFCLASS32 && cls != ELF_CLASS::ELFCLASS64) {
+    LIEF_WARN("Invalid ELFCLASS");
+    return nullptr;
+  }
 
-    auto res_type = stream_->read_conv<uint32_t>();
-    if (!res_type) {
-      break;
-    }
+  switch (ntype) {
+    case Note::TYPE::CORE_PRSTATUS:
+        return std::make_unique<CorePrStatus>(arch, cls, std::move(name), type,
+                                              std::move(desc_bytes));
+    case Note::TYPE::CORE_PRPSINFO:
+        return std::make_unique<CorePrPsInfo>(arch, cls, std::move(name), type,
+                                              std::move(desc_bytes));
+    case Note::TYPE::CORE_FILE:
+        return std::make_unique<CoreFile>(arch, cls, std::move(name), type,
+                                          std::move(desc_bytes));
+    case Note::TYPE::CORE_AUXV:
+        return std::make_unique<CoreAuxv>(arch, cls, std::move(name), type,
+                                          std::move(desc_bytes));
+    case Note::TYPE::CORE_SIGINFO:
+        return std::make_unique<CoreSigInfo>(std::move(name), ntype, type,
+                                             std::move(desc_bytes));
+    case Note::TYPE::ANDROID_IDENT:
+        return std::make_unique<AndroidIdent>(std::move(name), ntype, type,
+                                              std::move(desc_bytes));
+    case Note::TYPE::GNU_ABI_TAG:
+        return std::make_unique<NoteAbi>(std::move(name), ntype, type,
+                                         std::move(desc_bytes));
 
-    auto type = static_cast<NOTE_TYPES>(*res_type);
-    LIEF_DEBUG("Type: 0x{:x}", static_cast<size_t>(type));
+    default:
+        return std::make_unique<Note>(std::move(name), ntype, type,
+                                      std::move(desc_bytes));
+  }
+}
+#endif
 
-    if (namesz == 0) { // System reserves
-      break;
-    }
-    std::vector<uint8_t> name_buffer(namesz, 0);
-    if (!stream_->read_data(name_buffer, namesz)) {
-      LIEF_ERR("Can't read note name");
-      break;
-    }
+ok_error_t Parser::parse_notes(uint64_t offset, uint64_t size) {
+  LIEF_DEBUG("== Parsing note segment ==");
 
-    std::string name(reinterpret_cast<const char*>(name_buffer.data()), name_buffer.size());
-    if (type != NOTE_TYPES::NT_GNU_BUILD_ATTRIBUTE_FUNC &&
-        type != NOTE_TYPES::NT_GNU_BUILD_ATTRIBUTE_OPEN)
-    {
-      name = name.c_str();
-    }
-    LIEF_DEBUG("Name: {}", name);
-
-    stream_->align(sizeof(uint32_t));
-
-    std::vector<uint32_t> description;
-    if (descsz > 0) {
-      const size_t nb_chunks = (descsz - 1) / sizeof(uint32_t) + 1;
-      description.reserve(nb_chunks);
-      for (size_t i = 0; i < nb_chunks; ++i) {
-        if (const auto chunk = stream_->read_conv<uint32_t>()) {
-          description.push_back(*chunk);
-        } else {
-          break;
-        }
-      }
-      stream_->align(sizeof(uint32_t));
-    }
-    std::unique_ptr<Note> note;
-    std::vector<uint8_t> desc_bytes;
-    if (!description.empty()) {
-      desc_bytes = {
-          reinterpret_cast<const uint8_t*>(description.data()),
-          reinterpret_cast<const uint8_t*>(description.data()) + description.size() * sizeof(uint32_t)
-      };
-    }
+  stream_->setpos(offset);
+  uint64_t last_offset = offset + size;
 
-    if (binary_->header().file_type() == E_TYPE::ET_CORE) {
-      note = std::make_unique<Note>(name, static_cast<NOTE_TYPES_CORE>(type),
-                                    std::move(desc_bytes), binary_.get());
+  while(stream_->pos() < last_offset) {
+    std::unique_ptr<Note> note = Note::create(
+        *stream_,
+        binary_->header().file_type(), binary_->header().machine_type(),
+        binary_->header().identity_class()
+    );
+
+    if (note != nullptr) {
+      const auto it_note = std::find_if(
+          std::begin(binary_->notes_), std::end(binary_->notes_),
+          [&note] (const std::unique_ptr<Note>& n) { return *n == *note; });
+
+      if (it_note == std::end(binary_->notes_)) { // Not already present
+        binary_->notes_.push_back(std::move(note));
+      }
     } else {
-      note = std::make_unique<Note>(name, type, std::move(desc_bytes), binary_.get());
-    }
-
-    const auto it_note = std::find_if(
-        std::begin(binary_->notes_), std::end(binary_->notes_),
-        [&note] (const std::unique_ptr<Note>& n) { return *n == *note; });
-
-    if (it_note == std::end(binary_->notes_)) { // Not already present
-      binary_->notes_.push_back(std::move(note));
+      LIEF_WARN("Note not parsed!");
     }
   }
   return ok();
diff --git a/src/ELF/Structures.hpp b/src/ELF/Structures.hpp
index 5236305fee..ff73ba6539 100644
--- a/src/ELF/Structures.hpp
+++ b/src/ELF/Structures.hpp
@@ -12,9 +12,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-/* From llvm/Support/ELF.h */
-#ifndef LIEF_ELF_STRUCTURES_H_
-#define LIEF_ELF_STRUCTURES_H_
+#ifndef LIEF_ELF_STRUCTURES_H
+#define LIEF_ELF_STRUCTURES_H
 
 #include <cstring>
 
@@ -22,7 +21,6 @@
 #include "LIEF/ELF/enums.hpp"
 
 namespace LIEF {
-//! @brief Namespace related to the LIEF's ELF module
 namespace ELF {
 
 namespace details {
@@ -159,7 +157,40 @@ static const DYNAMIC_FLAGS_1 dynamic_flags_1_array[] = {
   DYNAMIC_FLAGS_1::DF_1_PIE,
 };
 
+struct Elf64_Prpsinfo
+{
+  char     pr_state;
+  char     pr_sname;
+  char     pr_zomb;
+  char     pr_nice;
+  uint32_t pr_pad;
+  uint64_t pr_flag;
+  uint32_t pr_uid;
+  uint32_t pr_gid;
+  int32_t  pr_pid;
+  int32_t  pr_ppid;
+  int32_t  pr_pgrp;
+  int32_t  pr_sid;
+  char     pr_fname[16];
+  char     pr_psargs[80];
+};
 
+struct Elf32_Prpsinfo
+{
+  char     pr_state;
+  char     pr_sname;
+  char     pr_zomb;
+  char     pr_nice;
+  uint32_t pr_flag;
+  uint16_t pr_uid;
+  uint16_t pr_gid;
+  int32_t  pr_pid;
+  int32_t  pr_ppid;
+  int32_t  pr_pgrp;
+  int32_t  pr_sid;
+  char     pr_fname[16];
+  char     pr_psargs[80];
+};
 
 class ELF32 {
   public:
diff --git a/src/ELF/hash.cpp b/src/ELF/hash.cpp
index f5d97da19e..e66b1426a3 100644
--- a/src/ELF/hash.cpp
+++ b/src/ELF/hash.cpp
@@ -26,8 +26,7 @@
 #include "LIEF/ELF/GnuHash.hpp"
 #include "LIEF/ELF/Header.hpp"
 #include "LIEF/ELF/Note.hpp"
-#include "LIEF/ELF/NoteDetails.hpp"
-#include "LIEF/ELF/NoteDetails/AndroidNote.hpp"
+#include "LIEF/ELF/NoteDetails/AndroidIdent.hpp"
 #include "LIEF/ELF/NoteDetails/NoteAbi.hpp"
 #include "LIEF/ELF/NoteDetails/core/CoreAuxv.hpp"
 #include "LIEF/ELF/NoteDetails/core/CoreFile.hpp"
@@ -233,84 +232,36 @@ void Hash::visit(const SymbolVersionAuxRequirement& svar) {
 void Hash::visit(const Note& note) {
   process(note.name());
   process(note.type());
+  process(note.original_type());
   process(note.description());
 }
 
-void Hash::visit(const NoteDetails& details) {
-  process(details.description());
+void Hash::visit(const AndroidIdent& note) {
+  visit(static_cast<const Note&>(note));
 }
-
-void Hash::visit(const AndroidNote& note) {
-  visit(static_cast<const NoteDetails&>(note));
-}
-
+//
 void Hash::visit(const NoteAbi& note) {
-  visit(static_cast<const NoteDetails&>(note));
+  visit(static_cast<const Note&>(note));
 }
 
 void Hash::visit(const CorePrPsInfo& pinfo) {
-  process(pinfo.file_name());
-  process(pinfo.flags());
-  process(pinfo.uid());
-  process(pinfo.gid());
-  process(pinfo.pid());
-  process(pinfo.ppid());
-  process(pinfo.pgrp());
-  process(pinfo.sid());
+  visit(static_cast<const Note&>(pinfo));
 }
 
 void Hash::visit(const CorePrStatus& pstatus) {
-  process(pstatus.siginfo().si_code);
-  process(pstatus.siginfo().si_errno);
-  process(pstatus.siginfo().si_signo);
-
-  process(pstatus.current_sig());
-  process(pstatus.sigpend());
-  process(pstatus.sighold());
-  process(pstatus.pid());
-  process(pstatus.ppid());
-  process(pstatus.pgrp());
-  process(pstatus.sid());
-
-  process(pstatus.utime().sec);
-  process(pstatus.utime().usec);
-
-  process(pstatus.stime().sec);
-  process(pstatus.stime().usec);
-
-  process(pstatus.cutime().sec);
-  process(pstatus.cutime().usec);
-
-  process(pstatus.cstime().sec);
-  process(pstatus.cstime().usec);
-
-  for (const CorePrStatus::reg_context_t::value_type& val : pstatus.reg_context()) {
-    process(val.first);  // Register
-    process(val.second); // Value
-  }
+  visit(static_cast<const Note&>(pstatus));
 }
 
 void Hash::visit(const CoreAuxv& auxv) {
-  for (const CoreAuxv::val_context_t::value_type& val : auxv.values()) {
-    process(val.first);  // Type
-    process(val.second); // Value
-  }
+  visit(static_cast<const Note&>(auxv));
 }
 
 void Hash::visit(const CoreSigInfo& siginfo) {
-  process(siginfo.signo());
-  process(siginfo.sigcode());
-  process(siginfo.sigerrno());
+  visit(static_cast<const Note&>(siginfo));
 }
 
 void Hash::visit(const CoreFile& file) {
-  process(file.count());
-  for (const CoreFileEntry& entry : file.files()) {
-    process(entry.start);
-    process(entry.end);
-    process(entry.file_ofs);
-    process(entry.path);
-  }
+  visit(static_cast<const Note&>(file));
 }
 
 void Hash::visit(const GnuHash& gnuhash) {
diff --git a/src/ELF/json.cpp b/src/ELF/json.cpp
index d2e00aa83d..863b275958 100644
--- a/src/ELF/json.cpp
+++ b/src/ELF/json.cpp
@@ -378,84 +378,135 @@ void JsonVisitor::visit(const SymbolVersionAuxRequirement& svar) {
 }
 
 void JsonVisitor::visit(const Note& note) {
-  node_["name"]  = note.name();
-  const std::string type_str = note.is_core() ? to_string(note.type_core()) : to_string(note.type());
-  node_["type"]  = type_str;
-  JsonVisitor visitor;
-  const NoteDetails& d = note.details();
-  d.accept(visitor);
-  node_["details"] = visitor.get();
-}
-
-void JsonVisitor::visit(const NoteDetails&) {
-  node_ = json::object();
+  node_["name"]          = note.name();
+  node_["type"]          = to_string(note.type());
+  node_["original_type"] = note.original_type();
 }
 
 void JsonVisitor::visit(const NoteAbi& note_abi) {
-  node_["abi"]     = to_string(note_abi.abi());
-  node_["version"] = note_abi.version();
+  visit(static_cast<const Note&>(note_abi));
+  if (auto abi = note_abi.abi()) {
+    node_["abi"] = to_string(*abi);
+  }
+  if (auto version = note_abi.version()) {
+    node_["version"] = *version;
+  }
 }
 
 void JsonVisitor::visit(const CorePrPsInfo& pinfo) {
-  node_["file_name"] = pinfo.file_name();
-  node_["flags"]     = pinfo.flags();
-  node_["uid"]       = pinfo.uid();
-  node_["gid"]       = pinfo.gid();
-  node_["pid"]       = pinfo.pid();
-  node_["ppid"]      = pinfo.ppid();
-  node_["pgrp"]      = pinfo.pgrp();
-  node_["sid"]       = pinfo.sid();
+  visit(static_cast<const Note&>(pinfo));
+  auto info = pinfo.info();
+  if (!info) {
+    return;
+  }
+  node_["state"]     = info->state;
+  node_["sname"]     = info->sname;
+  node_["zombie"]    = info->zombie;
+  node_["flag"]      = info->flag;
+  node_["uid"]       = info->uid;
+  node_["gid"]       = info->gid;
+  node_["pid"]       = info->pid;
+  node_["ppid"]      = info->ppid;
+  node_["pgrp"]      = info->pgrp;
+  node_["sid"]       = info->sid;
+  node_["filename"]  = info->filename_stripped();
+  node_["args"]      = info->args_stripped();
 }
 
-
 void JsonVisitor::visit(const CorePrStatus& pstatus) {
-  node_["current_sig"] = pstatus.current_sig();
-  node_["sigpend"]     = pstatus.sigpend();
-  node_["sighold"]     = pstatus.sighold();
-  node_["pid"]         = pstatus.pid();
-  node_["ppid"]        = pstatus.ppid();
-  node_["pgrp"]        = pstatus.pgrp();
-  node_["sid"]         = pstatus.sid();
-  node_["sigpend"]     = pstatus.sigpend();
+  visit(static_cast<const Note&>(pstatus));
+  const CorePrStatus::pr_status_t& status = pstatus.status();
+  node_["current_sig"] = status.cursig;
+  node_["sigpend"]     = status.sigpend;
+  node_["sighold"]     = status.sighold;
+  node_["pid"]         = status.pid;
+  node_["ppid"]        = status.ppid;
+  node_["pgrp"]        = status.pgrp;
+  node_["sid"]         = status.sid;
+  node_["sigpend"]     = status.sigpend;
 
   node_["utime"] = {
-    {"tv_sec",  pstatus.utime().sec},
-    {"tv_usec", pstatus.utime().usec}
+    {"tv_sec",  status.utime.sec},
+    {"tv_usec", status.utime.usec}
   };
 
   node_["stime"] = {
-    {"tv_sec",  pstatus.stime().sec},
-    {"tv_usec", pstatus.stime().sec}
+    {"tv_sec",  status.stime.sec},
+    {"tv_usec", status.stime.sec}
   };
 
   node_["stime"] = {
-    {"tv_sec",  pstatus.stime().sec},
-    {"tv_usec", pstatus.stime().usec}
+    {"tv_sec",  status.stime.sec},
+    {"tv_usec", status.stime.usec}
   };
 
-  json regs;
-  for (const CorePrStatus::reg_context_t::value_type& val : pstatus.reg_context()) {
-    regs[to_string(val.first)] = val.second;
-  };
-  node_["regs"] = regs;
+  std::vector<uint64_t> reg_vals = pstatus.register_values();
+  if (!reg_vals.empty()) {
+    json regs;
+    switch (pstatus.architecture()) {
+      case ARCH::EM_386:
+        {
+          for (size_t i = 0; i < reg_vals.size(); ++i) {
+            regs[to_string(CorePrStatus::Registers::X86(i))] = reg_vals[i];
+          }
+          break;
+        }
+      case ARCH::EM_X86_64:
+        {
+          for (size_t i = 0; i < reg_vals.size(); ++i) {
+            regs[to_string(CorePrStatus::Registers::X86_64(i))] = reg_vals[i];
+          }
+          break;
+        }
+      case ARCH::EM_ARM:
+        {
+          for (size_t i = 0; i < reg_vals.size(); ++i) {
+            regs[to_string(CorePrStatus::Registers::ARM(i))] = reg_vals[i];
+          }
+          break;
+        }
+      case ARCH::EM_AARCH64:
+        {
+          for (size_t i = 0; i < reg_vals.size(); ++i) {
+            regs[to_string(CorePrStatus::Registers::AARCH64(i))] = reg_vals[i];
+          }
+          break;
+        }
+      default:
+        {
+          regs = reg_vals;
+        }
+    }
+    node_["regs"] = regs;
+  }
 }
 
 void JsonVisitor::visit(const CoreAuxv& auxv) {
+  visit(static_cast<const Note&>(auxv));
   std::vector<json> values;
-  for (const CoreAuxv::val_context_t::value_type& val : auxv.values()) {
-    node_[to_string(val.first)] = val.second;
+  const std::map<CoreAuxv::TYPE, uint64_t> aux_values = auxv.values();
+  for (const auto& [k, v] : aux_values) {
+    node_[to_string(k)] = v;
   }
 }
 
 void JsonVisitor::visit(const CoreSigInfo& siginfo) {
-  node_["signo"] = siginfo.signo();
-  node_["sigcode"] = siginfo.sigcode();
-  node_["sigerrno"] = siginfo.sigerrno();
+  visit(static_cast<const Note&>(siginfo));
+  if (auto signo = siginfo.signo()) {
+    node_["signo"] = *signo;
+  }
+  if (auto sigcode = siginfo.sigcode()) {
+    node_["sigcode"] = *sigcode;
+  }
+  if (auto sigerrno = siginfo.sigerrno()) {
+    node_["sigerrno"] = *sigerrno;
+  }
 }
 
 void JsonVisitor::visit(const CoreFile& file) {
+  visit(static_cast<const Note&>(file));
   std::vector<json> files;
-  for (const CoreFileEntry& entry : file.files()) {
+  for (const CoreFile::entry_t& entry : file.files()) {
     const json file = {
       {"start",    entry.start},
       {"end",      entry.end},
@@ -468,6 +519,14 @@ void JsonVisitor::visit(const CoreFile& file) {
   node_["count"] = file.count();
 }
 
+void JsonVisitor::visit(const AndroidIdent& ident) {
+  visit(static_cast<const Note&>(ident));
+  node_["ndk_version"] = ident.ndk_version();
+  node_["sdk_verison"] = ident.sdk_version();
+  node_["ndk_build_number"] = ident.ndk_build_number();
+}
+
+
 void JsonVisitor::visit(const GnuHash& gnuhash) {
   node_["nb_buckets"]    = gnuhash.nb_buckets();
   node_["symbol_index"]  = gnuhash.symbol_index();
diff --git a/src/ELF/json_internal.hpp b/src/ELF/json_internal.hpp
index 37c563ade0..f15dec2ae5 100644
--- a/src/ELF/json_internal.hpp
+++ b/src/ELF/json_internal.hpp
@@ -23,7 +23,7 @@
 namespace LIEF {
 namespace ELF {
 
-class AndroidNote;
+class AndroidIdent;
 class Binary;
 class CoreAuxv;
 class CoreFile;
@@ -80,7 +80,7 @@ class JsonVisitor : public LIEF::JsonVisitor {
   void visit(const SymbolVersionDefinition& svd)    override;
   void visit(const Note& note)                      override;
   void visit(const NoteAbi& note)                   override;
-  void visit(const NoteDetails& details)            override;
+  void visit(const AndroidIdent& note)              override;
   void visit(const CorePrPsInfo& pinfo)             override;
   void visit(const CorePrStatus& pstatus)           override;
   void visit(const CoreAuxv& auxv)                  override;
diff --git a/src/ELF/note_utils.cpp b/src/ELF/note_utils.cpp
deleted file mode 100644
index a104cca19f..0000000000
--- a/src/ELF/note_utils.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#include "notes_utils.hpp"
-
-namespace LIEF {
-namespace ELF {
-const note_to_section_map_t& get_note_to_section() {
-  static const note_to_section_map_t note_to_section_map = {
-    { NOTE_TYPES::NT_GNU_ABI_TAG,              ".note.ABI-tag"          },
-    { NOTE_TYPES::NT_GNU_ABI_TAG,              ".note.android.ident"    },
-
-    { NOTE_TYPES::NT_GNU_HWCAP,                ".note.gnu.hwcap"        },
-    { NOTE_TYPES::NT_GNU_BUILD_ID,             ".note.gnu.build-id"     },
-    { NOTE_TYPES::NT_GNU_BUILD_ID,             ".note.stapsdt"          }, // Alternative name
-    { NOTE_TYPES::NT_GNU_GOLD_VERSION,         ".note.gnu.gold-version" },
-    { NOTE_TYPES::NT_GNU_GOLD_VERSION,         ".note.go.buildid"       },
-    { NOTE_TYPES::NT_GNU_PROPERTY_TYPE_0,      ".note.gnu.property"     },
-    { NOTE_TYPES::NT_GNU_BUILD_ATTRIBUTE_OPEN, ".gnu.build.attributes"  },
-    { NOTE_TYPES::NT_GNU_BUILD_ATTRIBUTE_FUNC, ".gnu.build.attributes"  },
-    { NOTE_TYPES::NT_CRASHPAD,                 ".note.crashpad.info"    },
-
-    { NOTE_TYPES::NT_UNKNOWN,                  ".note"                  },
-  };
-  return note_to_section_map;
-}
-}
-}
diff --git a/src/ELF/notes_utils.hpp b/src/ELF/notes_utils.hpp
deleted file mode 100644
index a0dedc17b2..0000000000
--- a/src/ELF/notes_utils.hpp
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Copyright 2017 - 2023 R. Thomas
- * Copyright 2017 - 2023 Quarkslab
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef LIEF_ELF_NOTES_UTILS_H
-#define LIEF_ELF_NOTES_UTILS_H
-#include "LIEF/ELF/enums.hpp"
-
-#include <map>
-
-namespace LIEF {
-namespace ELF {
-using note_to_section_map_t = std::multimap<NOTE_TYPES, const char*>;
-
-const note_to_section_map_t& get_note_to_section();
-
-}
-}
-#endif
diff --git a/src/ELF/structures.inc b/src/ELF/structures.inc
index 6343208bd7..5c9e503b1c 100644
--- a/src/ELF/structures.inc
+++ b/src/ELF/structures.inc
@@ -268,41 +268,6 @@ struct Elf32_Verdaux
   Elf32_Word	vda_next;		/**< Offset in bytes to next verdaux entry */
 };
 
-/** Structure for note type NT_PRPSINFO */
-struct Elf64_Prpsinfo
-{
-  char     pr_state;       /**< Numeric process state */
-  char     pr_sname;       /**< Char for pr_state */
-  char     pr_zomb;        /**< Process is a zombie */
-  char     pr_nice;        /**< Nice value */
-  uint32_t pr_pad;
-  uint64_t pr_flag;        /**< Process flags */
-  uint32_t pr_uid;         /**< Process user ID */
-  uint32_t pr_gid;         /**< Process group ID */
-  int32_t  pr_pid;         /**< Process ID */
-  int32_t  pr_ppid;        /**< Process parent ID */
-  int32_t  pr_pgrp;        /**< Process group */
-  int32_t  pr_sid;         /**< Process session ID */
-  char     pr_fname[16];   /**< Filename of executable */
-  char     pr_psargs[80];  /**< Initial part of arg list */
-};
-
-struct Elf32_Prpsinfo
-{
-  char     pr_state;      /**< Numeric process state */
-  char     pr_sname;      /**< Char for pr_state */
-  char     pr_zomb;       /**< Process is a zombie */
-  char     pr_nice;       /**< Nice value */
-  uint32_t pr_flag;       /**< Process flags */
-  uint16_t pr_uid;        /**< Process user ID */
-  uint16_t pr_gid;        /**< Process group ID */
-  int32_t  pr_pid;        /**< Process ID */
-  int32_t  pr_ppid;       /**< Process parent ID */
-  int32_t  pr_pgrp;       /**< Process group */
-  int32_t  pr_sid;        /**< Process session ID */
-  char     pr_fname[16];  /**< Filename of executable */
-  char     pr_psargs[80]; /**< Initial part of arg list */
-};
 
 struct Elf_siginfo {
   int32_t si_signo;
diff --git a/src/frozen.hpp b/src/frozen.hpp
index 9dd66596c5..d62053a65a 100644
--- a/src/frozen.hpp
+++ b/src/frozen.hpp
@@ -19,11 +19,19 @@
 #include "compiler_support.h"
 
 #ifdef LIEF_FROZEN_ENABLED
-#include <frozen/map.h>
-#define CONST_MAP(KEY, VAL, NUM) constexpr frozen::map<KEY, VAL, NUM>
-#else
-#include <unordered_map>
-#define CONST_MAP(KEY, VAL, NUM) static const std::unordered_map<KEY, VAL>
+# include <frozen/map.h>
+# define CONST_MAP(KEY, VAL, NUM) constexpr frozen::map<KEY, VAL, NUM>
+# define STRING_MAP constexpr frozen::map
+# define CONST_MAP_ALT constexpr frozen::map
+namespace frozen {
+template <class Key, class Value, class... Args>
+map(std::pair<Key, Value>, Args...) -> map<Key, Value, sizeof...(Args) + 1>;
+}
+#else // !LIEF_FROZEN_ENABLED
+# include <unordered_map>
+# define CONST_MAP(KEY, VAL, NUM) static const std::unordered_map<KEY, VAL>
+# define STRING_MAP static const std::unordered_map
+# define CONST_MAP_ALT static const std::unordered_map
 #endif
 
 #endif
diff --git a/src/internal_utils.hpp b/src/internal_utils.hpp
index e11eec17e7..aefa660c23 100644
--- a/src/internal_utils.hpp
+++ b/src/internal_utils.hpp
@@ -20,8 +20,10 @@
 #include <set>
 #include <algorithm>
 #include <unordered_map>
+#include "spdlog/fmt/fmt.h"
 
 #include "LIEF/span.hpp"
+#include "LIEF/errors.hpp"
 
 
 namespace LIEF {
@@ -37,6 +39,30 @@ inline std::vector<T> as_vector(span<const T> s) {
   return std::vector<T>(s.begin(), s.end());
 }
 
+template<class T>
+inline const char* to_string_or(result<T> res, const char* defval = "???") {
+  return res ? to_string(*res) : defval;
+}
+
+template<class T>
+inline std::string to_hex(const T& container, size_t maxsize = 0) {
+  size_t count = maxsize;
+  if (count == 0 || count > container.size()) {
+    count = container.size();
+  }
+  std::string out;
+  out.reserve(count * 2);
+  for (size_t i = 0; i < count; ++i) {
+    out += fmt::format("{:02x} ", container[i]);
+  }
+  if (count < container.size()) {
+    out += "...";
+  } else{
+    out.pop_back(); // remove trailing ' '
+  }
+  return out;
+}
+
 template<typename HANDLER>
 std::vector<std::string> optimize(const HANDLER& container,
                                   std::string(* getter)(const typename HANDLER::value_type&),
diff --git a/tests/elf/test_core.py b/tests/elf/test_core.py
index 4499c66338..2560fdf0a5 100644
--- a/tests/elf/test_core.py
+++ b/tests/elf/test_core.py
@@ -7,138 +7,127 @@
 lief.logging.set_level(lief.logging.LOGGING_LEVEL.INFO)
 
 def test_core_arm():
-    core = lief.parse(get_sample('ELF/ELF32_ARM_core_hello.core'))
+    core: lief.ELF.Binary = lief.parse(get_sample('ELF/ELF32_ARM_core_hello.core'))
 
     notes = core.notes
 
     assert len(notes) == 6
+    assert all(len(str(n).strip()) > 0 for n in notes), "\n".join(str(n) for n in notes)
 
     # Check NT_PRPSINFO
     # =================
-    prpsinfo = notes[0]
+    prpsinfo: lief.ELF.CorePrPsInfo = notes[0]
+    assert isinstance(prpsinfo, lief.ELF.CorePrPsInfo)
 
-    assert prpsinfo.is_core
-    assert prpsinfo.type_core == lief.ELF.NOTE_TYPES_CORE.PRPSINFO
+    assert prpsinfo.type == lief.ELF.Note.TYPE.CORE_PRPSINFO
 
-    # Check details
-    details = prpsinfo.details
-    assert isinstance(details, lief.ELF.CorePrPsInfo)
+    info = prpsinfo.info
 
-    assert details.file_name == "hello-exe"
-    assert details.uid  == 2000
-    assert details.gid  == 2000
-    assert details.pid  == 8166
-    assert details.ppid == 8163
-    assert details.pgrp == 8166
-    assert details.sid  == 7997
+    # Check details
+    assert info.filename_stripped == "hello-exe"
+    assert info.args_stripped == "./hello-exe "
+    assert info.uid  == 2000
+    assert info.gid  == 2000
+    assert info.pid  == 8166
+    assert info.ppid == 8163
+    assert info.pgrp == 8166
+    assert info.sid  == 7997
 
     # Check NT_PRSTATUS
     # =================
-    prstatus = notes[1]
+    prstatus: lief.ELF.CorePrStatus = notes[1]
 
-    assert prstatus.is_core
-    assert prstatus.type_core == lief.ELF.NOTE_TYPES_CORE.PRSTATUS
+    assert prstatus.type == lief.ELF.Note.TYPE.CORE_PRSTATUS
 
     # Check details
-    details = prstatus.details
-
-    assert details.current_sig == 7
-    assert details.sigpend == 0
-    assert details.sighold == 0
-    assert details.pid == 8166
-    assert details.ppid == 0
-    assert details.pgrp == 0
-    assert details.sid == 0
-
-    assert details.utime.sec == 0
-    assert details.utime.usec == 0
-
-    assert details.stime.sec == 0
-    assert details.stime.usec == 0
-
-    assert details.cutime.sec == 0
-    assert details.cutime.usec == 0
-
-    assert details.cstime.sec == 0
-    assert details.cstime.usec == 0
-
-    reg_ctx = details.register_context
-    assert len(reg_ctx) == 17
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R0] == 0xaad75074
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R1] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R2] == 0xb
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R3] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R4] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R5] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R6] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R7] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R8] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R9] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R10] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R11] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R12] == 0xA
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R13] == 1
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R14] == 0xf7728841
-    assert details[lief.ELF.CorePrStatus.REGISTERS.ARM_R15] == 0xaad7507c
-    assert details.get(lief.ELF.CorePrStatus.REGISTERS.ARM_CPSR) == 0x60010010
+
+    status = prstatus.status
+    assert status.cursig == 7
+    assert status.sigpend == 0
+    assert status.sighold == 0
+    assert status.pid == 8166
+    assert status.ppid == 0
+    assert status.pgrp == 0
+    assert status.sid == 0
+
+    assert status.utime.sec == 0
+    assert status.utime.usec == 0
+
+    assert status.stime.sec == 0
+    assert status.stime.usec == 0
+
+    assert status.cutime.sec == 0
+    assert status.cutime.usec == 0
+
+    assert status.cstime.sec == 0
+    assert status.cstime.usec == 0
+
+    reg_values = prstatus.register_values
+    assert len(reg_values) == 17
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R0.value] == 0xaad75074
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R1.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R2.value] == 0xb
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R3.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R4.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R5.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R6.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R7.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R8.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R9.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R10.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R11.value] == 0
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R12.value] == 0xA
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R13.value] == 1
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R14.value] == 0xf7728841
+    assert reg_values[lief.ELF.CorePrStatus.Registers.ARM.R15.value] == 0xaad7507c
+    assert prstatus.get(lief.ELF.CorePrStatus.Registers.ARM.CPSR) == 0x60010010
 
     arm_vfp  = notes[2]
 
-    # Check NT_NOTE
-    # =================
-    siginfo  = notes[3]
-    assert siginfo.is_core
-    assert siginfo.type_core == lief.ELF.NOTE_TYPES_CORE.SIGINFO
+    siginfo: lief.ELF.CoreSigInfo = notes[3]
+    assert siginfo.type == lief.ELF.Note.TYPE.CORE_SIGINFO
 
-    # Check details
-    details = siginfo.details
-    assert details.signo == 7
-    assert details.sigcode == 0
-    assert details.sigerrno == 1
+    assert siginfo.signo == 7
+    assert siginfo.sigcode == 0
+    assert siginfo.sigerrno == 1
 
     # Check NT_AUXV
     # =================
-    auxv = notes[4]
-
-    assert auxv.is_core
-    assert auxv.type_core == lief.ELF.NOTE_TYPES_CORE.AUXV
-
-    # Check details
-    details = auxv.details
-
-    assert len(details.values) == 18
-    assert details[lief.ELF.CoreAuxv.TYPES.PHDR] == 0xaad74034
-    assert details[lief.ELF.CoreAuxv.TYPES.PHENT] == 0x20
-    assert details[lief.ELF.CoreAuxv.TYPES.PHNUM] == 0x9
-    assert details[lief.ELF.CoreAuxv.TYPES.PAGESZ] == 4096
-    assert details[lief.ELF.CoreAuxv.TYPES.BASE] == 0xf7716000
-    assert details[lief.ELF.CoreAuxv.TYPES.FLAGS] == 0
-    assert details[lief.ELF.CoreAuxv.TYPES.ENTRY] == 0xaad75074
-    assert details[lief.ELF.CoreAuxv.TYPES.UID] == 2000
-    assert details[lief.ELF.CoreAuxv.TYPES.EUID] == 2000
-    assert details[lief.ELF.CoreAuxv.TYPES.GID] == 2000
-    assert details[lief.ELF.CoreAuxv.TYPES.EGID] == 2000
-    assert details[lief.ELF.CoreAuxv.TYPES.PLATFORM] == 0xfffefb5c
-    assert details[lief.ELF.CoreAuxv.TYPES.HWCAP] == 0x27b0d6
-    assert details[lief.ELF.CoreAuxv.TYPES.CKLTCK] == 0x64
-    assert details[lief.ELF.CoreAuxv.TYPES.SECURE] == 0
-    assert details[lief.ELF.CoreAuxv.TYPES.RANDOM] == 0xfffefb4c
-    assert details[lief.ELF.CoreAuxv.TYPES.HWCAP2] == 0x1f
-    assert details[lief.ELF.CoreAuxv.TYPES.EXECFN] == 0xfffeffec
+    auxv: lief.ELF.CoreAuxv = notes[4]
+
+    assert auxv.type == lief.ELF.Note.TYPE.CORE_AUXV
+
+    assert len(auxv.values) == 18
+    assert auxv[lief.ELF.CoreAuxv.TYPE.PHDR] == 0xaad74034
+    assert auxv[lief.ELF.CoreAuxv.TYPE.PHENT] == 0x20
+    assert auxv[lief.ELF.CoreAuxv.TYPE.PHNUM] == 0x9
+    assert auxv[lief.ELF.CoreAuxv.TYPE.PAGESZ] == 4096
+    assert auxv[lief.ELF.CoreAuxv.TYPE.BASE] == 0xf7716000
+    assert auxv[lief.ELF.CoreAuxv.TYPE.FLAGS] == 0
+    assert auxv[lief.ELF.CoreAuxv.TYPE.ENTRY] == 0xaad75074
+    assert auxv[lief.ELF.CoreAuxv.TYPE.UID] == 2000
+    assert auxv[lief.ELF.CoreAuxv.TYPE.EUID] == 2000
+    assert auxv[lief.ELF.CoreAuxv.TYPE.GID] == 2000
+    assert auxv[lief.ELF.CoreAuxv.TYPE.EGID] == 2000
+    assert auxv[lief.ELF.CoreAuxv.TYPE.TGT_PLATFORM] == 0xfffefb5c
+    assert auxv[lief.ELF.CoreAuxv.TYPE.HWCAP] == 0x27b0d6
+    assert auxv[lief.ELF.CoreAuxv.TYPE.CLKTCK] == 0x64
+    assert auxv[lief.ELF.CoreAuxv.TYPE.SECURE] == 0
+    assert auxv[lief.ELF.CoreAuxv.TYPE.RANDOM] == 0xfffefb4c
+    assert auxv[lief.ELF.CoreAuxv.TYPE.HWCAP2] == 0x1f
+    assert auxv[lief.ELF.CoreAuxv.TYPE.EXECFN] == 0xfffeffec
 
     # Check NT_FILE
     # =================
-    note = notes[5]
+    note: lief.ELF.CoreFile = notes[5]
 
-    assert note.is_core
-    assert note.type_core == lief.ELF.NOTE_TYPES_CORE.FILE
+    assert note.type == lief.ELF.Note.TYPE.CORE_FILE
 
     # Check details
-    details = note.details
-    files   = details.files
+    files = note.files
 
-    assert len(files) == len(details)
-    assert 21 == len(details)
+    assert len(files) == len(note)
+    assert 21 == len(note)
 
     assert files[0].start == 0xaad74000
     assert files[0].end == 0xaad78000
@@ -152,7 +141,7 @@ def test_core_arm():
     assert last.file_ofs == 0x8a000
     assert last.path == "/system/bin/linker"
 
-    assert all(len(c.path) > 0 for c in details)
+    assert all(len(c.path) > 0 for c in note)
 
 
 def test_core_arm64():
@@ -162,149 +151,141 @@ def test_core_arm64():
 
     assert len(notes) == 6
 
+    assert all(len(str(n).strip()) > 0 for n in notes)
+
     # Check NT_PRPSINFO
     # =================
-    prpsinfo = notes[0]
+    prpsinfo: lief.ELF.CorePrPsInfo = notes[0]
 
-    assert prpsinfo.is_core
-    assert prpsinfo.type_core == lief.ELF.NOTE_TYPES_CORE.PRPSINFO
+    assert prpsinfo.type == lief.ELF.Note.TYPE.CORE_PRPSINFO
 
     # Check details
-    details = prpsinfo.details
-    assert isinstance(details, lief.ELF.CorePrPsInfo)
-    assert details.file_name == "hello-exe"
-    assert details.uid == 2000
-    assert details.gid == 2000
-    assert details.pid == 8104
-    assert details.ppid == 8101
-    assert details.pgrp == 8104
-    assert details.sid == 7997
+    assert isinstance(prpsinfo, lief.ELF.CorePrPsInfo)
+    info = prpsinfo.info
+    assert info.filename_stripped == "hello-exe"
+    assert info.args_stripped == "./hello-exe "
+    assert info.uid == 2000
+    assert info.gid == 2000
+    assert info.pid == 8104
+    assert info.ppid == 8101
+    assert info.pgrp == 8104
+    assert info.sid == 7997
 
     # Check NT_PRSTATUS
     # =================
-    prstatus = notes[1]
+    prstatus: lief.ELF.CorePrStatus = notes[1]
 
-    assert prstatus.is_core
-    assert prstatus.type_core == lief.ELF.NOTE_TYPES_CORE.PRSTATUS
+    assert prstatus.type == lief.ELF.Note.TYPE.CORE_PRSTATUS
 
     # Check details
-    details = prstatus.details
-
-    assert details.current_sig == 5
-    assert details.sigpend == 0
-    assert details.sighold == 0
-    assert details.pid == 8104
-    assert details.ppid == 0
-    assert details.pgrp == 0
-    assert details.sid == 0
-
-    assert details.utime.sec == 0
-    assert details.utime.usec == 0
-
-    assert details.stime.sec == 0
-    assert details.stime.usec == 0
-
-    assert details.cutime.sec == 0
-    assert details.cutime.usec == 0
-
-    assert details.cstime.sec == 0
-    assert details.cstime.usec == 0
-
-
-    reg_ctx = details.register_context
-    assert len(reg_ctx) == 34
-
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X0] == 0x5580b86f50
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X1] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X2] == 0x1
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X3] == 0x7fb7e2e160
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X4] == 0x7fb7e83030
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X5] == 0x4
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X6] == 0x6f6c2f617461642f
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X7] == 0x2f706d742f6c6163
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X8] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X9] == 0xa
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X10] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X11] == 0xA
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X12] == 0x0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X13] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X14] == 0x878ca62ae01a9a5
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X15] == 0x7fb7e7a000
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X16] == 0x7fb7c132c8
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X17] == 0x7fb7bb0adc
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X18] == 0x7fb7c1e000
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X19] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X20] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X21] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X22] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X23] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X24] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X25] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X26] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X27] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X28] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X29] == 0
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X30] == 0x7fb7eb6068
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_X31] == 0x7ffffff950
-    assert details[lief.ELF.CorePrStatus.REGISTERS.AARCH64_PC] == 0x5580b86f50
-
-    arm_vfp  = notes[2]
+    status = prstatus.status
+
+    assert status.cursig == 5
+    assert status.sigpend == 0
+    assert status.sighold == 0
+    assert status.pid == 8104
+    assert status.ppid == 0
+    assert status.pgrp == 0
+    assert status.sid == 0
+
+    assert status.utime.sec == 0
+    assert status.utime.usec == 0
+
+    assert status.stime.sec == 0
+    assert status.stime.usec == 0
+
+    assert status.cutime.sec == 0
+    assert status.cutime.usec == 0
+
+    assert status.cstime.sec == 0
+    assert status.cstime.usec == 0
+
+    regs = prstatus.register_values
+    assert len(regs) == 34
+
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X0.value] == 0x5580b86f50
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X1.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X2.value] == 0x1
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X3.value] == 0x7fb7e2e160
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X4.value] == 0x7fb7e83030
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X5.value] == 0x4
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X6.value] == 0x6f6c2f617461642f
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X7.value] == 0x2f706d742f6c6163
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X8.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X9.value] == 0xa
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X10.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X11.value] == 0xA
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X12.value] == 0x0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X13.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X14.value] == 0x878ca62ae01a9a5
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X15.value] == 0x7fb7e7a000
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X16.value] == 0x7fb7c132c8
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X17.value] == 0x7fb7bb0adc
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X18.value] == 0x7fb7c1e000
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X19.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X20.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X21.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X22.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X23.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X24.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X25.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X26.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X27.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X28.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X29.value] == 0
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X30.value] == 0x7fb7eb6068
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.X31.value] == 0x7ffffff950
+    assert regs[lief.ELF.CorePrStatus.Registers.AARCH64.PC.value] == 0x5580b86f50
+
+    fpregset = notes[2]
+    assert fpregset.type == lief.ELF.Note.TYPE.CORE_FPREGSET
 
     # Check NT_NOTE
     # =================
     siginfo  = notes[3]
-    assert siginfo.is_core
-    assert siginfo.type_core == lief.ELF.NOTE_TYPES_CORE.SIGINFO
+    assert siginfo.type == lief.ELF.Note.TYPE.CORE_SIGINFO
 
-    # Check details
-    details = siginfo.details
-    assert details.signo == 5
-    assert details.sigcode == 0
-    assert details.sigerrno == 1
+    assert siginfo.signo == 5
+    assert siginfo.sigcode == 0
+    assert siginfo.sigerrno == 1
 
     # Check NT_AUXV
     # =================
-    auxv = notes[4]
+    auxv: lief.ELF.CoreAuxv = notes[4]
 
-    assert auxv.is_core
-    assert auxv.type_core == lief.ELF.NOTE_TYPES_CORE.AUXV
+    assert auxv.type == lief.ELF.Note.TYPE.CORE_AUXV
 
     # Check details
-    details = auxv.details
-
-    assert len(details.values) == 18
-    assert details[lief.ELF.CoreAuxv.TYPES.PHDR] == 0x5580b86040
-    assert details[lief.ELF.CoreAuxv.TYPES.PHENT] == 0x38
-    assert details[lief.ELF.CoreAuxv.TYPES.PHNUM] == 0x9
-    assert details[lief.ELF.CoreAuxv.TYPES.PAGESZ] == 4096
-    assert details[lief.ELF.CoreAuxv.TYPES.BASE] == 0x7fb7e93000
-    assert details[lief.ELF.CoreAuxv.TYPES.FLAGS] == 0
-    assert details[lief.ELF.CoreAuxv.TYPES.ENTRY] == 0x5580b86f50
-    assert details[lief.ELF.CoreAuxv.TYPES.UID] == 2000
-    assert details[lief.ELF.CoreAuxv.TYPES.EUID] == 2000
-    assert details[lief.ELF.CoreAuxv.TYPES.GID] == 2000
-    assert details[lief.ELF.CoreAuxv.TYPES.EGID] == 2000
-    assert details[lief.ELF.CoreAuxv.TYPES.PLATFORM] == 0x7ffffffb58
-    assert details[lief.ELF.CoreAuxv.TYPES.HWCAP] == 0xff
-    assert details[lief.ELF.CoreAuxv.TYPES.CKLTCK] == 0x64
-    assert details[lief.ELF.CoreAuxv.TYPES.SECURE] == 0
-    assert details[lief.ELF.CoreAuxv.TYPES.RANDOM] == 0x7ffffffb48
-    assert details[lief.ELF.CoreAuxv.TYPES.EXECFN] == 0x7fffffffec
-    assert details[lief.ELF.CoreAuxv.TYPES.SYSINFO_EHDR] == 0x7fb7e91000
+    values = auxv.values
+    assert len(values) == 18
+    assert values[lief.ELF.CoreAuxv.TYPE.PHDR] == 0x5580b86040
+    assert values[lief.ELF.CoreAuxv.TYPE.PHENT] == 0x38
+    assert values[lief.ELF.CoreAuxv.TYPE.PHNUM] == 0x9
+    assert values[lief.ELF.CoreAuxv.TYPE.PAGESZ] == 4096
+    assert values[lief.ELF.CoreAuxv.TYPE.BASE] == 0x7fb7e93000
+    assert values[lief.ELF.CoreAuxv.TYPE.FLAGS] == 0
+    assert values[lief.ELF.CoreAuxv.TYPE.ENTRY] == 0x5580b86f50
+    assert values[lief.ELF.CoreAuxv.TYPE.UID] == 2000
+    assert values[lief.ELF.CoreAuxv.TYPE.EUID] == 2000
+    assert values[lief.ELF.CoreAuxv.TYPE.GID] == 2000
+    assert values[lief.ELF.CoreAuxv.TYPE.EGID] == 2000
+    assert values[lief.ELF.CoreAuxv.TYPE.TGT_PLATFORM] == 0x7ffffffb58
+    assert values[lief.ELF.CoreAuxv.TYPE.HWCAP] == 0xff
+    assert values[lief.ELF.CoreAuxv.TYPE.CLKTCK] == 0x64
+    assert values[lief.ELF.CoreAuxv.TYPE.SECURE] == 0
+    assert values[lief.ELF.CoreAuxv.TYPE.RANDOM] == 0x7ffffffb48
+    assert values[lief.ELF.CoreAuxv.TYPE.EXECFN] == 0x7fffffffec
+    assert values[lief.ELF.CoreAuxv.TYPE.SYSINFO_EHDR] == 0x7fb7e91000
 
     # Check NT_FILE
     # =================
-    note = notes[5]
-
-    assert note.is_core
-    assert note.type_core == lief.ELF.NOTE_TYPES_CORE.FILE
+    note: lief.ELF.CoreFile = notes[5]
 
-    # Check details
-    details = note.details
-    files   = details.files
+    assert note.type == lief.ELF.Note.TYPE.CORE_FILE
 
-    assert len(files) == len(details)
-    assert 22 == len(details)
+    files = note.files
+    assert len(files) == len(note)
+    assert 22 == len(files)
 
     assert files[0].start == 0x5580b86000
     assert files[0].end == 0x5580b88000
@@ -320,23 +301,19 @@ def test_core_arm64():
 def test_core_write(tmp_path: Path):
     core = lief.parse(get_sample('ELF/ELF64_x86-64_core_hello.core'))
     note = core.notes[1]
-    assert note.type_core == lief.ELF.NOTE_TYPES_CORE.PRSTATUS
-    details = note.details
+    assert note.type == lief.ELF.Note.TYPE.CORE_PRSTATUS
 
-    details[lief.ELF.CorePrStatus.REGISTERS.X86_64_RIP] = 0xBADC0DE
+    note[lief.ELF.CorePrStatus.Registers.X86_64.RIP] = 0xBADC0DE
 
     note = core.notes[5]
-    assert note.type_core == lief.ELF.NOTE_TYPES_CORE.AUXV
-    details = note.details
+    assert note.type == lief.ELF.Note.TYPE.CORE_AUXV
 
-    details[lief.ELF.CoreAuxv.TYPES.ENTRY] = 0xBADC0DE
+    note[lief.ELF.CoreAuxv.TYPE.ENTRY] = 0xBADC0DE
 
     note = core.notes[4]
-    assert note.type_core == lief.ELF.NOTE_TYPES_CORE.SIGINFO
+    assert note.type == lief.ELF.Note.TYPE.CORE_SIGINFO
     orig_siginfo_len = len(note.description)
-    details = note.details
-
-    details.sigerrno = 0xCC
+    note.sigerrno = 0xCC
 
     output = tmp_path / "elf.core"
 
@@ -349,20 +326,17 @@ def test_core_write(tmp_path: Path):
     assert core_new is not None
 
     note = core_new.notes[1]
-    assert note.type_core == lief.ELF.NOTE_TYPES_CORE.PRSTATUS
-    details = note.details
+    assert note.type == lief.ELF.Note.TYPE.CORE_PRSTATUS
 
-    assert details[lief.ELF.CorePrStatus.REGISTERS.X86_64_RIP] == 0xBADC0DE
+    assert note[lief.ELF.CorePrStatus.Registers.X86_64.RIP] == 0xBADC0DE
 
     note = core_new.notes[5]
-    assert note.type_core == lief.ELF.NOTE_TYPES_CORE.AUXV
-    details = note.details
+    assert note.type == lief.ELF.Note.TYPE.CORE_AUXV
 
-    assert details[lief.ELF.CoreAuxv.TYPES.ENTRY] == 0xBADC0DE
+    assert note[lief.ELF.CoreAuxv.TYPE.ENTRY] == 0xBADC0DE
 
     note = core_new.notes[4]
-    assert note.type_core == lief.ELF.NOTE_TYPES_CORE.SIGINFO
+    assert note.type == lief.ELF.Note.TYPE.CORE_SIGINFO
     assert len(note.description) == orig_siginfo_len
-    details = note.details
 
-    assert details.sigerrno == 0xCC
+    assert note.sigerrno == 0xCC
diff --git a/tests/elf/test_elf.py b/tests/elf/test_elf.py
index 3acfc20a57..184419c88f 100644
--- a/tests/elf/test_elf.py
+++ b/tests/elf/test_elf.py
@@ -122,12 +122,14 @@ def test_notes():
     assert n2.name == "GNU"
     assert n3.name == "GNU"
 
-    assert n1.type == lief.ELF.NOTE_TYPES.ABI_TAG
-    assert n2.type == lief.ELF.NOTE_TYPES.BUILD_ID
-    assert n3.type == lief.ELF.NOTE_TYPES.GOLD_VERSION
+    assert n1.type == lief.ELF.Note.TYPE.GNU_ABI_TAG
+    assert n2.type == lief.ELF.Note.TYPE.GNU_BUILD_ID
+    assert n3.type == lief.ELF.Note.TYPE.GNU_GOLD_VERSION
 
-    assert n1.details.abi == lief.ELF.NOTE_ABIS.LINUX
-    assert n1.details.version == [2, 6, 32]
+    assert isinstance(n1, lief.ELF.NoteAbi)
+
+    assert n1.abi == lief.ELF.NoteAbi.ABI.LINUX
+    assert n1.version == [2, 6, 32]
 
     assert list(n2.description) == [0x7e, 0x68, 0x6c, 0x7d,
                                     0x79, 0x9b, 0xa4, 0xcd,
diff --git a/tests/elf/test_notes.py b/tests/elf/test_notes.py
index 790400aa4c..287a2cf6d8 100644
--- a/tests/elf/test_notes.py
+++ b/tests/elf/test_notes.py
@@ -13,7 +13,7 @@
 
 def test_change_note(tmp_path: Path):
     etterlog = lief.parse(get_sample('ELF/ELF64_x86-64_binary_etterlog.bin'))
-    build_id = etterlog[lief.ELF.NOTE_TYPES.BUILD_ID]
+    build_id = etterlog[lief.ELF.Note.TYPE.GNU_BUILD_ID]
 
     new_desc = [i & 0xFF for i in range(500)]
     build_id.description = new_desc
@@ -22,25 +22,25 @@ def test_change_note(tmp_path: Path):
 
     etterlog_updated = lief.parse(output.as_posix())
 
-    assert etterlog[lief.ELF.NOTE_TYPES.BUILD_ID] == etterlog_updated[lief.ELF.NOTE_TYPES.BUILD_ID]
+    assert etterlog[lief.ELF.Note.TYPE.GNU_BUILD_ID] == etterlog_updated[lief.ELF.Note.TYPE.GNU_BUILD_ID]
 
 def test_remove_note(tmp_path: Path):
     etterlog = lief.parse(get_sample('ELF/ELF64_x86-64_binary_etterlog.bin'))
     output = tmp_path / "etterlog"
     print(output)
 
-    build_id = etterlog[lief.ELF.NOTE_TYPES.BUILD_ID]
+    build_id = etterlog[lief.ELF.Note.TYPE.GNU_BUILD_ID]
     assert build_id is not None
     etterlog -= build_id
 
     etterlog.write(output.as_posix(), config)
     etterlog_updated = lief.parse(output.as_posix())
-    assert lief.ELF.NOTE_TYPES.BUILD_ID not in etterlog_updated
+    assert lief.ELF.Note.TYPE.GNU_BUILD_ID not in etterlog_updated
 
 def test_add_note(tmp_path: Path):
     etterlog = lief.parse(get_sample('ELF/ELF64_x86-64_binary_etterlog.bin'))
     output = tmp_path / "etterlog"
-    note = lief.ELF.Note("Foo", lief.ELF.NOTE_TYPES.GOLD_VERSION, [123])
+    note = lief.ELF.Note.create("Foo", lief.ELF.Note.TYPE.GNU_GOLD_VERSION, [1, 2])
 
     etterlog += note
 
@@ -48,10 +48,10 @@ def test_add_note(tmp_path: Path):
 
     etterlog_updated = lief.parse(output.as_posix())
 
-    assert lief.ELF.NOTE_TYPES.GOLD_VERSION in etterlog_updated
+    assert lief.ELF.Note.TYPE.GNU_GOLD_VERSION in etterlog_updated
 
-
-    # The string printed is largely irrelevant, but running print ensures no regression occurs in a previous Note::dump segfault
+    # The string printed is largely irrelevant, but running print ensures no
+    # regression occurs in a previous Note::dump segfault
     # https://github.com/lief-project/LIEF/issues/300
     with StringIO() as temp_stdout:
         with redirect_stdout(temp_stdout):
@@ -62,17 +62,16 @@ def test_android_note(tmp_path: Path):
     ndkr16 = lief.parse(get_sample('ELF/ELF64_AArch64_piebinary_ndkr16.bin'))
     output = tmp_path / "etterlog"
 
-    note = ndkr16.get(lief.ELF.NOTE_TYPES.ABI_TAG)
-    details = note.details
-    assert details.sdk_version == 21
-    assert details.ndk_version[:4] == "r16b"
-    assert details.ndk_build_number[:7] == "4479499"
+    note: lief.ELF.AndroidIdent = ndkr16.get(lief.ELF.Note.TYPE.ANDROID_IDENT)
+    assert note.sdk_version == 21
+    assert note.ndk_version[:4] == "r16b"
+    assert note.ndk_build_number[:7] == "4479499"
 
-    details.sdk_version = 15
-    details.ndk_version = "r15c"
-    details.ndk_build_number = "123456"
+    note.sdk_version = 15
+    note.ndk_version = "r15c"
+    note.ndk_build_number = "123456"
 
-    note = ndkr16.get(lief.ELF.NOTE_TYPES.ABI_TAG).details
+    note = ndkr16.get(lief.ELF.Note.TYPE.ANDROID_IDENT)
 
     assert note.sdk_version == 15
     assert note.ndk_version[:4] == "r15c"
@@ -82,7 +81,7 @@ def test_android_note(tmp_path: Path):
 
     ndkr15 = lief.parse(output.as_posix())
 
-    note = ndkr15.get(lief.ELF.NOTE_TYPES.ABI_TAG).details
+    note = ndkr15.get(lief.ELF.Note.TYPE.ANDROID_IDENT)
 
     assert note.sdk_version == 15
     assert note.ndk_version[:4] == "r15c"
@@ -98,3 +97,9 @@ def test_issue_816(tmp_path: Path):
     elf.write(output.as_posix(), config)
     new = lief.parse(output.as_posix())
     assert len(new.notes) == 40
+
+def test_crashpad():
+    RAW_CRASHPAD = "0900000008000000494e464f437261736870616400000000d85cf00300000000"
+    note = lief.ELF.Note.create(bytes.fromhex(RAW_CRASHPAD))
+    assert note.type == lief.ELF.Note.TYPE.CRASHPAD
+    assert note.name == "Crashpad"
diff --git a/tests/elf/test_parser.py b/tests/elf/test_parser.py
index e6e3c14251..bfda877a08 100644
--- a/tests/elf/test_parser.py
+++ b/tests/elf/test_parser.py
@@ -135,3 +135,9 @@ def test_path_like():
 def test_984():
     elf = lief.ELF.parse(get_sample('ELF/issue_984_ilp32.o'))
     assert len(elf.sections) > 0
+
+def test_975():
+    elf = lief.ELF.parse(get_sample('ELF/issue_975_aarch64.o'))
+    for note in elf.notes:
+        print(note)
+    #assert len(elf.sections) > 0
diff --git a/tests/elf/test_parser_simple.py b/tests/elf/test_parser_simple.py
index 911d86ecf9..bca0688cb8 100644
--- a/tests/elf/test_parser_simple.py
+++ b/tests/elf/test_parser_simple.py
@@ -105,11 +105,11 @@ def test_notes():
     notes = TARGET.notes
     assert len(notes) == 2
 
-    assert notes[0].details.abi == lief.ELF.NOTE_ABIS.LINUX
-    assert notes[0].description == [0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]
+    assert notes[0].abi == lief.ELF.NoteAbi.ABI.LINUX
+    assert list(notes[0].description) == [0, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0]
     assert notes[0].name == "GNU"
-    assert notes[0].type == lief.ELF.NOTE_TYPES.ABI_TAG
-    assert notes[0].details.version == [3, 2, 0]
+    assert notes[0].type == lief.ELF.Note.TYPE.GNU_ABI_TAG
+    assert notes[0].version == [3, 2, 0]
 
 def test_symbols_sections():
     """
diff --git a/tests/elf/test_str_hash.py b/tests/elf/test_str_hash.py
index 88a46d3137..a6baa5fab3 100644
--- a/tests/elf/test_str_hash.py
+++ b/tests/elf/test_str_hash.py
@@ -49,9 +49,7 @@ def test_hash():
 
     assert hash(resolve.header) > 0
     assert hash(etterlog.notes[0]) > 0
-    assert hash(etterlog.notes[0].details) > 0
-    #assert super(lief.ELF.NoteDetails, etterlog.notes[0].details) is None
-    #assert etterlog.notes[0].details is None
+    assert hash(etterlog.notes[0]) > 0
     assert hash(etterlog.relocations[0]) > 0
     assert hash(etterlog.sections[0]) > 0
     assert hash(etterlog.segments[0]) > 0
@@ -109,7 +107,6 @@ def test_str(capsys):
 
     print(resolve.header)
     print(etterlog.notes[0])
-    print(etterlog.notes[0].details)
     print(etterlog.relocations[0])
     print(etterlog.sections[0])
     print(etterlog.segments[0])