From 2b98571951bd01000b55891e5c50714c3277cdc0 Mon Sep 17 00:00:00 2001 From: "Magomed Kostoev (mkostoevr)" Date: Sun, 21 Feb 2021 20:36:10 +0000 Subject: [PATCH] Add unexisted dependencies git-svn-id: svn://kolibrios.org@8600 a494cfbc-eb01-0410-851d-a64ba20cac60 --- programs/develop/clink/cvec/LICENSE | 21 + programs/develop/clink/cvec/README.md | 88 +++ programs/develop/clink/cvec/cvec.h | 498 ++++++++++++++ programs/develop/clink/epep/LICENSE | 21 + programs/develop/clink/epep/README.md | 24 + programs/develop/clink/epep/epep.h | 898 ++++++++++++++++++++++++++ 6 files changed, 1550 insertions(+) create mode 100644 programs/develop/clink/cvec/LICENSE create mode 100644 programs/develop/clink/cvec/README.md create mode 100644 programs/develop/clink/cvec/cvec.h create mode 100644 programs/develop/clink/epep/LICENSE create mode 100644 programs/develop/clink/epep/README.md create mode 100644 programs/develop/clink/epep/epep.h diff --git a/programs/develop/clink/cvec/LICENSE b/programs/develop/clink/cvec/LICENSE new file mode 100644 index 0000000000..b1520f0de1 --- /dev/null +++ b/programs/develop/clink/cvec/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Magomed Kostoev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/programs/develop/clink/cvec/README.md b/programs/develop/clink/cvec/README.md new file mode 100644 index 0000000000..3bdceee731 --- /dev/null +++ b/programs/develop/clink/cvec/README.md @@ -0,0 +1,88 @@ +# cvec - partial `std::vector` implementation in C. +## Partial implementation of `std::vector` + +Member functions table: + +| Status | Name | Function or reason if not implemented | +| :---: | --- | --- | +| :heavy_check_mark: | `(constructor)` | `new` | +| :heavy_check_mark: | `(destructor)` | `free` | +| :heavy_check_mark: | `operator=` | `assign_other` | +| :heavy_check_mark: | `assign` | `assign_fill`, `assign_range` | +| :heavy_minus_sign: | `get_allocator` | No `allocator` objects in the language | +| :heavy_check_mark: | `at` | `at` | +| :heavy_check_mark: | `operator[]` | `[]` | +| :heavy_check_mark: | `front` | `front`, `front_p` | +| :heavy_check_mark: | `back` | `back`, `back_p` | +| :heavy_check_mark: | `data` | `data` | +| :heavy_check_mark: | `begin` | `begin` | +| :heavy_check_mark: | `cbegin` | `cbegin` | +| :heavy_check_mark: | `end` | `end` | +| :heavy_check_mark: | `cend` | `cend` | +| :heavy_minus_sign: | `rbegin` | No reverse iterators in the language | +| :heavy_minus_sign: | `crbegin` | No reverse iterators in the language | +| :heavy_minus_sign: | `rend` | No reverse iterators in the language | +| :heavy_minus_sign: | `crend` | No reverse iterators in the language | +| :heavy_check_mark: | `empty` | `empty` | +| :heavy_check_mark: | `size` | `size` | +| :heavy_check_mark: | `max_size` | `max_size` | +| :heavy_check_mark: | `reserve` | `reserve` | +| :heavy_check_mark: | `capacity` | `capacity` | +| :heavy_check_mark: | `shrink_to_fit` | `shrink_to_fit` | +| :heavy_check_mark: | `clear` | `clear` | +| :heavy_check_mark: | `insert` | `insert`, `insert_it` | +| :heavy_minus_sign: | `emplace` | I know no way to preserve the original signature | +| :heavy_check_mark: | `erase` | `erase` | +| :heavy_check_mark: | `push_back` | `push_back` | +| :heavy_minus_sign: | `emplace_back` | I know no way to preserve the original signature | +| :heavy_check_mark: | `pop_back` | `pop_back` | +| :heavy_check_mark: | `resize` | `resize` | +| :heavy_minus_sign: | `swap` | Would have n complexity in this implementation | + +## Easy to use + +To use the std::vector implementation for specified type they should be declared as follows: + +```C +#define CVEC_TYPE TypeOfVectorElement +#include "cvec.h" + +// ... + + TypeOfVectorElement *vec = cvec_TypeOfVectorElement_new(128); + + cvec_TypeOfVectorElement_push_back(&vec, value); +``` + +Also somewhere in the project the functinos should be instantiated as follows: + +```C +#define CVEC_TYPE TypeOfVectorElement +#define CVEC_INST +#include "cvec.h" +``` + +## Allows using of custom allocators. + +```C +#define CVEC_TYPE pchar +#define CVEC_INST +#define CVEC_MALLOC custom_malloc +#define CVEC_REALLOC custom_realloc +#define CVEC_FREE custom_free +#include "cvec.h" +``` + +## Allows handling of exceptional cases. + +```C +#define CVEC_TYPE pchar +#define CVEC_INST +// Set Out Of Bounds handler +#define CVEC_OOBH(funcname, vec, index) printf("Out of bounds in %s (vec = %p, i = %d)", funcname, vec, index); abort(); +#include "cvec.h" +``` + +## Has no fixed dependencies + +Every function it uses may be overridden. More information about dependencies in [cvec.h](cvec.h). diff --git a/programs/develop/clink/cvec/cvec.h b/programs/develop/clink/cvec/cvec.h new file mode 100644 index 0000000000..b9a5da3227 --- /dev/null +++ b/programs/develop/clink/cvec/cvec.h @@ -0,0 +1,498 @@ +// Copyright (c) 2015 Evan Teran +// Copyright (c) 2020 Magomed Kostoev +// +// You may use, distribute and modify this code under the terms of the MIT license. +// +// You should have received a copy of the MIT license with this file. If not, please visit +// https://opensource.org/licenses/MIT for full license details. + +// cvec.h - std::vector (ish) implementation in C. Based on https://github.com/eteran/c-vector/. +// +// Unlike a real std::vector this one is implemented as a fat array, so metadata is placed inside +// an allocated buffer itself. +// +// Configuration (definitions): +// CVEC_TYPE: Type of the vector's elements, after instantiation these functions will be visible +// as cvec__funcname, so no stars and subscripting marks allowed - named +// types only +// CVEC_INST: Instantiate the functions if defined +// CVEC_LOGG: Multiply capacity by CVEC_LOGG each expansion if defined (should be >= 1) +// CVEC_ASSERT: Replacement for assert from +// CVEC_MALLOC: Replacement for malloc from +// CVEC_REALLOC: Replacement for realloc from +// CVEC_FREE: Replacement for free from +// CVEC_OOBH: Out-of-bounds handler (gets __func__, vector data address and index of overflow) +// CVEC_OOBVAL: Default value to return on out of bounds access +// +// Minimal definitions for declaration: CVEC_TYPE +// Minimal definitions for instantiation: CVEC_TYPE, CVEC_INST, CVEC_OOBVAL if the type object +// can't be represented by 0 value. +// +// WARNING: All used definitions will be undefined on header exit. +// +// Dependencies: +// or another source of size_t and ptrdiff_t +// or another source of SIZE_MAX +// or another source of malloc, calloc and realloc +// or another source of assert + +// +// Input macros +// + +#ifndef CVEC_LOGG +# define CVEC_LOGG 1.5 +#endif +#ifndef CVEC_ASSERT +# define CVEC_ASSERT(x) assert(x) +#endif +#ifndef CVEC_MALLOC +# define CVEC_MALLOC(size) malloc(size) +#endif +#ifndef CVEC_REALLOC +# define CVEC_REALLOC(ptr, size) realloc(ptr, size) +#endif +#ifndef CVEC_FREE +# define CVEC_FREE(size) free(size) +#endif +#ifndef CVEC_OOBH +# define CVEC_OOBH(funcname, vec, index) +#endif +#ifndef CVEC_OOBVAL +# define CVEC_OOBVAL { 0 } +#endif + +// +// Internal macros +// + +#define CVEC_CONCAT2_IMPL(x, y) cvec_ ## x ## _ ## y +#define CVEC_CONCAT2(x, y) CVEC_CONCAT2_IMPL(x, y) + +/// Creates method name according to CVEC_TYPE +#define CVEC_FUN(name) CVEC_CONCAT2(CVEC_TYPE, name) + +#define cvec_x_new CVEC_FUN(new) +#define cvec_x_capacity CVEC_FUN(capacity) +#define cvec_x_size CVEC_FUN(size) +#define cvec_x_empty CVEC_FUN(empty) +#define cvec_x_pop_back CVEC_FUN(pop_back) +#define cvec_x_erase CVEC_FUN(erase) +#define cvec_x_free CVEC_FUN(free) +#define cvec_x_begin CVEC_FUN(begin) +#define cvec_x_cbegin CVEC_FUN(cbegin) +#define cvec_x_end CVEC_FUN(end) +#define cvec_x_cend CVEC_FUN(cend) +#define cvec_x_push_back CVEC_FUN(push_back) +#define cvec_x_at CVEC_FUN(at) +#define cvec_x_reserve CVEC_FUN(reserve) +#define cvec_x_shrink_to_fit CVEC_FUN(shrink_to_fit) +#define cvec_x_assign_fill CVEC_FUN(assign_fill) +#define cvec_x_assign_range CVEC_FUN(assign_range) +#define cvec_x_assign_other CVEC_FUN(assign_other) +#define cvec_x_data CVEC_FUN(data) +#define cvec_x_resize CVEC_FUN(resize) +#define cvec_x_resize_v CVEC_FUN(resize_v) +#define cvec_x_clear CVEC_FUN(clear) +#define cvec_x_front CVEC_FUN(front) +#define cvec_x_front_p CVEC_FUN(front_p) +#define cvec_x_back CVEC_FUN(back) +#define cvec_x_back_p CVEC_FUN(back_p) +#define cvec_x_max_size CVEC_FUN(max_size) +#define cvec_x_insert CVEC_FUN(insert) +#define cvec_x_insert_it CVEC_FUN(insert_it) + +#define cvec_x_grow CVEC_FUN(grow) +#define cvec_x_set_capacity CVEC_FUN(set_capacity) +#define cvec_x_set_size CVEC_FUN(set_size) + +// +// External declarations +// + +/// Allocates new vector of specified capacity. +CVEC_TYPE *cvec_x_new(size_t count); + +/// Gets the current capacity of the vector. +size_t cvec_x_capacity(CVEC_TYPE **vec); + +/// Gets the current size of the vector. +size_t cvec_x_size(CVEC_TYPE **vec); + +/// Returns non-zero if the vector is empty. +int cvec_x_empty(CVEC_TYPE **vec); + +/// Removes the last element from the vector. +void cvec_x_pop_back(CVEC_TYPE **vec); + +/// Removes the element at index i from the vector. +void cvec_x_erase(CVEC_TYPE **vec, size_t i); + +/// Frees all memory associated with the vector. +void cvec_x_free(CVEC_TYPE **vec); + +/// Returns an iterator to first element of the vector. +CVEC_TYPE *cvec_x_begin(CVEC_TYPE **vec); + +/// Returns a const iterator to first element of the vector +const CVEC_TYPE *cvec_x_cbegin(CVEC_TYPE **vec); + +/// Returns an iterator to one past the last element of the vector. +CVEC_TYPE *cvec_x_end(CVEC_TYPE **vec); + +/// Returns a const iterator to one past the last element of the vector. +const CVEC_TYPE *cvec_x_cend(CVEC_TYPE **vec); + +/// Adds an element to the end of the vector. +void cvec_x_push_back(CVEC_TYPE **vec, CVEC_TYPE value); + +/// Gets element with bounds checking. On out of bounds calls CVEC_OOBH and returns CVEC_OOBVAL. +CVEC_TYPE cvec_x_at(CVEC_TYPE **vec, size_t i); + +/// Increases the capacity of the vector to a value that's equal to new_cap. +void cvec_x_reserve(CVEC_TYPE **vec, size_t new_cap); + +/// Requests the removal of unused capacity. +void cvec_x_shrink_to_fit(CVEC_TYPE **vec); + +/// Replaces the contents with count copies of value value. +void cvec_x_assign_fill(CVEC_TYPE **vec, size_t count, CVEC_TYPE value); + +/// Replaces the contents with data from range [first, last). +void cvec_x_assign_range(CVEC_TYPE **vec, CVEC_TYPE *first, CVEC_TYPE *last); + +/// Replaces the contents with contetns of other. +void cvec_x_assign_other(CVEC_TYPE **vec, CVEC_TYPE **other); + +/// Gives direct access to buffer. +CVEC_TYPE *cvec_x_data(CVEC_TYPE **vec); + +/// Resizes the container to contain count elements. +void cvec_x_resize(CVEC_TYPE **vec, size_t new_size); + +/// Resizes the container to contain count elements, initializes new elements by value. +void cvec_x_resize_v(CVEC_TYPE **vec, size_t new_size, CVEC_TYPE value); + +/// Erases all elements from the container. +void cvec_x_clear(CVEC_TYPE **vec); + +/// Returns the first element of the vector. +CVEC_TYPE cvec_x_front(CVEC_TYPE **vec); + +/// Returns a pointer to the first element of the vector. +CVEC_TYPE *cvec_x_front_p(CVEC_TYPE **vec); + +/// Returns the last element of the vector. +CVEC_TYPE cvec_x_back(CVEC_TYPE **vec); + +/// Returns a pointer to the last element of the vector. +CVEC_TYPE *cvec_x_back_p(CVEC_TYPE **vec); + +/// Returns maximal size of the vector. +size_t cvec_x_max_size(CVEC_TYPE **vec); + +/// Inserts a value into vector by index. +CVEC_TYPE *cvec_x_insert(CVEC_TYPE **vec, size_t index, CVEC_TYPE value); + +/// Inserts a value into vector by iterator (pointer in vector). +CVEC_TYPE *cvec_x_insert_it(CVEC_TYPE **vec, CVEC_TYPE *it, CVEC_TYPE value); + +// +// Function definitions +// + +#ifdef CVEC_INST + +/// Ensures that the vector is at least elements big. +static void cvec_x_grow(CVEC_TYPE **vec, size_t count); + +/// Sets the capacity variable of the vector. +static void cvec_x_set_capacity(CVEC_TYPE **vec, size_t size); + +/// Sets the size variable of the vector. +static void cvec_x_set_size(CVEC_TYPE **vec, size_t size); + +// +// Public functions +// + +CVEC_TYPE *cvec_x_new(size_t count) { + const size_t cv_sz = count * sizeof(CVEC_TYPE) + sizeof(size_t) * 2; + size_t *cv_p = CVEC_MALLOC(cv_sz); + CVEC_ASSERT(cv_p); + CVEC_TYPE *vec = (void *)(&cv_p[2]); + cvec_x_set_capacity(&vec, count); + cvec_x_set_size(&vec, 0); + return vec; +} + +size_t cvec_x_capacity(CVEC_TYPE **vec) { + CVEC_ASSERT(vec); + return *vec ? ((size_t *)*vec)[-1] : (size_t)0; +} + +size_t cvec_x_size(CVEC_TYPE **vec) { + CVEC_ASSERT(vec); + return *vec ? ((size_t *)*vec)[-2] : (size_t)0; +} + +int cvec_x_empty(CVEC_TYPE **vec) { + return cvec_x_size(vec) == 0; +} + +void cvec_x_pop_back(CVEC_TYPE **vec) { + cvec_x_set_size(vec, cvec_x_size(vec) - 1); +} + +void cvec_x_erase(CVEC_TYPE **vec, size_t i) { + CVEC_ASSERT(vec); + if (*vec) { + const size_t cv_sz = cvec_x_size(vec); + if (i < cv_sz) { + cvec_x_set_size(vec, cv_sz - 1); + for (size_t cv_x = i; cv_x < (cv_sz - 1); ++cv_x) { + (*vec)[cv_x] = (*vec)[cv_x + 1]; + } + } + } +} + +void cvec_x_free(CVEC_TYPE **vec) { + CVEC_ASSERT(vec); + if (*vec) { + size_t *p1 = &((size_t *)*vec)[-2]; + CVEC_FREE(p1); + } +} + +CVEC_TYPE *cvec_x_begin(CVEC_TYPE **vec) { + CVEC_ASSERT(vec); + return *vec; +} + +const CVEC_TYPE *cvec_x_cbegin(CVEC_TYPE **vec) { + return cvec_x_begin(vec); +} + +CVEC_TYPE *cvec_x_end(CVEC_TYPE **vec) { + CVEC_ASSERT(vec); + return *vec ? &((*vec)[cvec_x_size(vec)]) : NULL; +} + +const CVEC_TYPE *cvec_x_cend(CVEC_TYPE **vec) { + return cvec_x_end(vec); +} + +void cvec_x_push_back(CVEC_TYPE **vec, CVEC_TYPE value) { + CVEC_ASSERT(vec); + size_t cv_cap = cvec_x_capacity(vec); + if (cv_cap <= cvec_x_size(vec)) { + cvec_x_grow(vec, cv_cap * CVEC_LOGG + 1); + } + (*vec)[cvec_x_size(vec)] = value; + cvec_x_set_size(vec, cvec_x_size(vec) + 1); +} + +CVEC_TYPE cvec_x_at(CVEC_TYPE **vec, size_t i) { + CVEC_ASSERT(vec); + if (i >= cvec_x_size(vec) || i < 0) { + CVEC_OOBH(__func__, vec, i); + CVEC_TYPE ret = CVEC_OOBVAL; + return ret; + } + return (*vec)[i]; +} + +void cvec_x_reserve(CVEC_TYPE **vec, size_t new_cap) { + if (new_cap <= cvec_x_capacity(vec)) { + return; + } + cvec_x_grow(vec, new_cap); +} + +void cvec_x_shrink_to_fit(CVEC_TYPE **vec) { + if (cvec_x_capacity(vec) > cvec_x_size(vec)) { + cvec_x_grow(vec, cvec_x_size(vec)); + } +} + +void cvec_x_assign_fill(CVEC_TYPE **vec, size_t count, CVEC_TYPE value) { + CVEC_ASSERT(vec); + cvec_x_reserve(vec, count); + cvec_x_set_size(vec, count); // If the buffer was bigger than new_cap, set size ourselves + for (size_t i = 0; i < count; i++) { + (*vec)[i] = value; + } +} + +void cvec_x_assign_range(CVEC_TYPE **vec, CVEC_TYPE *first, CVEC_TYPE *last) { + CVEC_ASSERT(vec); + size_t new_size = ((ptrdiff_t)(last - first)) / sizeof(*first); + cvec_x_reserve(vec, new_size); + cvec_x_set_size(vec, new_size); + size_t i = 0; + for (CVEC_TYPE *it = first; it < last; it++, i++) { + (*vec)[i] = *it; + } +} + +void cvec_x_assign_other(CVEC_TYPE **vec, CVEC_TYPE **other) { + cvec_x_assign_range(vec, cvec_x_begin(other), cvec_x_end(other)); +} + +CVEC_TYPE *cvec_x_data(CVEC_TYPE **vec) { + CVEC_ASSERT(vec); + return (*vec); +} + +void cvec_x_resize(CVEC_TYPE **vec, size_t count) { + CVEC_TYPE value = { 0 }; + cvec_x_resize_v(vec, count, value); +} + +void cvec_x_resize_v(CVEC_TYPE **vec, size_t count, CVEC_TYPE value) { + CVEC_ASSERT(vec); + size_t old_size = cvec_x_size(vec); + cvec_x_set_size(vec, count); + if (cvec_x_capacity(vec) < count) { + cvec_x_reserve(vec, count); + for (CVEC_TYPE *it = (*vec) + old_size; it < cvec_x_end(vec); it++) { + *it = value; + } + } +} + +void cvec_x_clear(CVEC_TYPE **vec) { + cvec_x_set_size(vec, 0); +} + +CVEC_TYPE cvec_x_front(CVEC_TYPE **vec) { + CVEC_ASSERT(vec); + return (*vec)[0]; +} + +CVEC_TYPE *cvec_x_front_p(CVEC_TYPE **vec) { + CVEC_ASSERT(vec); + return (*vec); +} + +CVEC_TYPE cvec_x_back(CVEC_TYPE **vec) { + return cvec_x_end(vec)[-1]; +} + +CVEC_TYPE *cvec_x_back_p(CVEC_TYPE **vec) { + return cvec_x_end(vec) - 1; +} + +size_t cvec_x_max_size(CVEC_TYPE **vec) { + return SIZE_MAX / sizeof(**vec); +} + +CVEC_TYPE *cvec_x_insert(CVEC_TYPE **vec, size_t index, CVEC_TYPE value) { + CVEC_ASSERT(vec); + if (index > cvec_x_size(vec) || index < 0) { + return NULL; // TODO: What? + } + size_t new_size = cvec_x_size(vec) + 1; + cvec_x_reserve(vec, new_size); + cvec_x_set_size(vec, new_size); + CVEC_TYPE *ret = *vec + index; + for (CVEC_TYPE *it = cvec_x_back_p(vec); it > ret; it--) { + *it = it[-1]; + } + *ret = value; + return ret; +} + +CVEC_TYPE *cvec_x_insert_it(CVEC_TYPE **vec, CVEC_TYPE *it, CVEC_TYPE value) { + CVEC_ASSERT(vec); + size_t index = (it - *vec) / sizeof(**vec); + return cvec_x_insert(vec, index, value); +} + +// +// Private functions +// + +static void cvec_x_set_capacity(CVEC_TYPE **vec, size_t size) { + CVEC_ASSERT(vec); + if (*vec) { + ((size_t *)*vec)[-1] = size; + } +} + +static void cvec_x_set_size(CVEC_TYPE **vec, size_t size) { + CVEC_ASSERT(vec); + if (*vec) { + ((size_t *)*vec)[-2] = size; + } +} + +static void cvec_x_grow(CVEC_TYPE **vec, size_t count) { + CVEC_ASSERT(vec); + const size_t cv_sz = count * sizeof(**vec) + sizeof(size_t) * 2; + size_t *cv_p1 = &((size_t *)*vec)[-2]; + size_t *cv_p2 = CVEC_REALLOC(cv_p1, (cv_sz)); + CVEC_ASSERT(cv_p2); + *vec = (void *)(&cv_p2[2]); + cvec_x_set_capacity(vec, count); +} + +#endif + +#undef CVEC_TYPE + +#ifdef CVEC_INST +# undef CVEC_INST +# ifdef CVEC_LOGG +# undef CVEC_LOGG +# endif +# ifdef CVEC_OOBH +# undef CVEC_OOBH +# endif +# ifdef CVEC_OOBVAL +# undef CVEC_OOBVAL +# endif +# undef CVEC_ASSERT +# undef CVEC_MALLOC +# undef CVEC_REALLOC +# undef CVEC_FREE +#endif + +#undef CVEC_CONCAT2_IMPL +#undef CVEC_CONCAT2 + +#undef CVEC_FUN + +#undef cvec_x_new +#undef cvec_x_capacity +#undef cvec_x_size +#undef cvec_x_empty +#undef cvec_x_pop_back +#undef cvec_x_erase +#undef cvec_x_free +#undef cvec_x_begin +#undef cvec_x_cbegin +#undef cvec_x_end +#undef cvec_x_cend +#undef cvec_x_push_back +#undef cvec_x_at +#undef cvec_x_reserve +#undef cvec_x_shrink_to_fit +#undef cvec_x_assign_fill +#undef cvec_x_assign_range +#undef cvec_x_assign_other +#undef cvec_x_data +#undef cvec_x_resize +#undef cvec_x_resize_v +#undef cvec_x_clear +#undef cvec_x_front +#undef cvec_x_front_p +#undef cvec_x_back +#undef cvec_x_back_p +#undef cvec_x_max_size +#undef cvec_x_insert +#undef cvec_x_insert_it +#undef cvec_x_grow +#undef cvec_x_set_capacity +#undef cvec_x_set_size diff --git a/programs/develop/clink/epep/LICENSE b/programs/develop/clink/epep/LICENSE new file mode 100644 index 0000000000..b1520f0de1 --- /dev/null +++ b/programs/develop/clink/epep/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Magomed Kostoev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/programs/develop/clink/epep/README.md b/programs/develop/clink/epep/README.md new file mode 100644 index 0000000000..219537f3b7 --- /dev/null +++ b/programs/develop/clink/epep/README.md @@ -0,0 +1,24 @@ +# epep - Embeddable PE Parser +## Features + +- PE header (including Data Directories as a part Optional Header) +- Section Headers +- COFF Symbols +- Imports +- Exports +- Base relocations (DLL) + +## How to use + +To declare functions from the library include it: + +```C +#include "epep.h" +``` + +The functions they shoud be instantiated somewhere in the project like so: + +```C +#define EPEP_INST +#include "epep.h" +``` diff --git a/programs/develop/clink/epep/epep.h b/programs/develop/clink/epep/epep.h new file mode 100644 index 0000000000..383e40a2c6 --- /dev/null +++ b/programs/develop/clink/epep/epep.h @@ -0,0 +1,898 @@ +// Dependencies: +// or any another source of assert() +// or any another source of uint64_t, uint32_t, uint16_t, uint8_t, size_t + +#ifndef EPEP_ASSERT +#include +#define EPEP_ASSERT(x) assert(x) +#endif + +#ifndef EPEP_READER +#include +#define EPEP_READER FILE * +#define EPEP_READER_GET(preader) getc(*preader) +#define EPEP_READER_SEEK(preader, offset) fseek(*preader, offset, SEEK_SET) +#define EPEP_READER_TELL(preader) ftell(*preader) +#define EPEP_READER_GET_BLOCK(preader, size, buf) fread(buf, 1, size, *preader); +#endif + +// +// Constants +// + +typedef enum { + EPEP_INVALID, + EPEP_IMAGE, + EPEP_OBJECT, +} EpepKind; + +typedef enum { + EPEP_ERR_SUCCESS, + EPEP_ERR_DATA_DIRECTORY_INDEX_IS_INVALID, + EPEP_ERR_SECTION_HEADER_INDEX_IS_INVALID, + EPEP_ERR_SYMBOL_INDEX_IS_INVALID, + EPEP_ERR_NOT_AN_OBJECT, + EPEP_ERR_ADDRESS_IS_OUT_OF_SECTION_RAW_DATA, + EPEP_ERR_OUTPUT_CAPACITY_IS_ZERO, + EPEP_ERR_OUTPUT_IS_NULL, + EPEP_ERR_ADDRESS_IS_OUT_OF_ANY_SECTION, + EPEP_ERR_EXPORT_ADDRESS_TABLE_ENTRY_NAME_NOT_FOUND, + EPEP_ERR_NO_BASE_RELOCATION_TABLE, + EPEP_ERR_BASE_RELOCATION_IS_ALREADY_END, +} EpepError; + +// +// Generic +// + +typedef struct { + EPEP_READER reader; + EpepKind kind; + EpepError error_code; + size_t signature_offset_offset; + size_t signature_offset; + size_t first_data_directory_offset; + size_t first_section_header_offset; + size_t export_table_offset; + size_t import_table_offset; + size_t base_relocation_table_offset; + size_t base_relocation_table_end_offset; + struct { + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; + } coffFileHeader; + struct { + // Standard fields + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint32_t BaseOfData; // PE32-only + // Windows-specific fields + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + } optionalHeader; + struct { + uint32_t ExportFlags; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t NameRva; + uint32_t OrdinalBase; + uint32_t AddressTableEntries; + uint32_t NumberOfNamePointers; + uint32_t ExportAddressTableRva; + uint32_t NamePointerRva; + uint32_t OrdinalTableRva; + } export_directory; +} Epep; + +/// Constructor of the general information container +int epep_init(Epep *epep, EPEP_READER reader); + +/// Gives file offset corresponding to RVA is any, returns 0 othervice +int epep_get_file_offset_by_rva(Epep *epep, size_t *offset, size_t addr); + +// +// Data Directories +// + +typedef struct { + uint32_t VirtualAddress; + uint32_t Size; +} EpepImageDataDirectory; + +/// Gives Data Directiry by its index +int epep_get_data_directory_by_index(Epep *epep, EpepImageDataDirectory *idd, size_t index); + +// +// Sections +// + +typedef struct { + char Name[8]; + uint32_t VirtualSize; + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; +} EpepSectionHeader; + +/// Gives Section Header by its index +int epep_get_section_header_by_index(Epep *epep, EpepSectionHeader *sh, size_t index); + +/// Gives section header by RVA +int epep_get_section_header_by_rva(Epep *epep, EpepSectionHeader *sh, size_t addr); + +/// Gives section contents by Section Header +int epep_get_section_contents(Epep *epep, EpepSectionHeader *sh, void *buf); + +// +// COFF Symbols (object file symbols) +// + +typedef union { + struct { + union { + char ShortName[8]; + struct { + uint32_t Zeroes; + uint32_t Offset; + }; + }; + uint32_t Value; + uint16_t SectionNumber; + uint16_t Type; + uint8_t StorageClass; + uint8_t NumberOfAuxSymbols; + } symbol; + struct { + uint32_t TagIndex; + uint32_t TotalSize; + uint32_t PointerToLinenumber; + uint32_t PointerToNextFunction; + uint16_t Unused; + } auxFunctionDefinition; + struct { + uint8_t Unused0[4]; + uint16_t Linenumber; + uint8_t Unused1[6]; + uint32_t PointerToNextFunction; + uint8_t Unused2[2]; + } auxBfOrEfSymbol; + struct { + uint32_t TagIndex; + uint32_t Characteristics; + uint8_t Unused[10]; + } auxWeakExternal; + struct { + char FileName[18]; + } auxFile; + struct { + uint32_t Length; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t CheckSum; + uint16_t Number; + uint8_t Selection; + uint8_t Unused[3]; + } auxSectionDefinition; +} EpepCoffSymbol; + +/// Gives COFF string table size +int epep_get_string_table_size(Epep *epep, size_t *size); + +/// Gives COFF string table +int epep_get_string_table(Epep *epep, char *string_table); + +/// Gives COFF Symbol by its index +int epep_get_symbol_by_index(Epep *epep, EpepCoffSymbol *sym, size_t index); + +// +// Imports +// + +typedef struct { + uint32_t ImportLookupTableRva; + uint32_t TimeDateStamp; + uint32_t ForwarderChain; + uint32_t NameRva; + uint32_t ImportAddressTableRva; +} EpepImportDirectory; + +/// Returns non-zero if import table exists in the file +int epep_has_import_table(Epep *epep); + +/// Places offset of import table into epep structure +int epep_read_import_table_offset(Epep *epep); + +/// Gives Import Directory by index +int epep_get_import_directory_by_index(Epep *epep, EpepImportDirectory *import_directory, size_t index); + +/// Gives name of Import Directory (library) +int epep_get_import_directory_name_s(Epep *epep, EpepImportDirectory *import_directory, char *name, size_t name_max); + +/// Gives Import Lookup (imported symbol) by import directory and index +int epep_get_import_directory_lookup_by_index(Epep *epep, EpepImportDirectory *import_directory, size_t *lookup, size_t index); + +/// Gives name of Import Directory Lookup (imported symbol) or nothing if imported by ordinal +int epep_get_lookup_name_s(Epep *epep, size_t lookup, char *name, size_t name_max); + +// +// Exports +// + +typedef union { + uint32_t ExportRva; + uint32_t ForwarderRva; +} EpepExportAddress; + +/// Returns non-zero if export table exists in the file +int epep_has_export_table(Epep *epep); + +/// Palces offset of export table into epep structrue +int epep_read_export_table_offset(Epep *epep); + +/// Palces export table into epep structrue +//! Needs to be called before next export functions +int epep_read_export_directory(Epep *epep); + +/// Gives name of the DLL +//! epep_read_export_directory needs to be called before +int epep_get_dll_name_s(Epep *epep, char *name, size_t name_max); + +/// Gives entry from Export Name Pointer Table by its index +//! epep_read_export_directory needs to be called before +int epep_get_export_name_pointer_by_index(Epep *epep, size_t *name_rva, size_t index); + +/// Gives export name by its index in Export Address Table (receives name buffer length) +//! epep_read_export_directory needs to be called before +int epep_get_export_name_s_by_index(Epep *epep, char *name, size_t name_max, size_t index); + +/// Gives export address by its index in Export Address Table +//! epep_read_export_directory needs to be called before +int epep_get_export_address_by_index(Epep *epep, EpepExportAddress *export_address, size_t index); + +/// Gives forwarder string of Export Address +//! epep_read_export_directory needs to be called before +int epep_get_export_address_forwarder_s(Epep *epep, EpepExportAddress *export_address, char *forwarder, size_t forwarder_max); + +/// Returns non-zero if the export address specifies forwarder string +//! epep_read_export_directory needs to be called before +int epep_export_address_is_forwarder(Epep *epep, EpepExportAddress *export_address); + +// +// DLL Base Relocations +// + +typedef struct { + size_t offset; + uint32_t PageRva; + uint32_t BlockSize; + uint16_t BaseRelocation[0]; +} EpepBaseRelocationBlock; + +typedef union { + struct { + uint16_t Offset: 12, + Type: 4; + }; + uint16_t u16; +} EpepBaseRelocation; + +/// Returns non-zero if the file contains Base Relocations +int epep_has_base_relocation_table(Epep *epep); + +/// Places offset to Base Relocation Table into epep structure +int epep_read_base_relocation_table_offset(Epep *epep); + +/// Gives first Base Relocation Block +int epep_get_first_base_relocation_block(Epep *epep, EpepBaseRelocationBlock *brb); + +/// Gives next Base Relocation Block (replaces contents of the given block) +int epep_get_next_base_relocation_block(Epep *epep, EpepBaseRelocationBlock *it); + +/// Gives Base Relocation by its index in Base Relocation Block +int epep_get_base_relocation_block_base_relocation_by_index(Epep *epep, EpepBaseRelocationBlock *brb, EpepBaseRelocation *br, size_t index); + +// +// COFF Relocations +// + +typedef struct { + uint32_t VirtualAddress; + uint32_t SymbolTableIndex; + uint16_t Type; +} EpepCoffRelocation; + +int epep_get_section_relocation_by_index(Epep *epep, EpepSectionHeader *sh, EpepCoffRelocation *rel, size_t index); + +// +// COFF Line Numbers +// + +typedef struct { + union { + uint32_t SymbolTableIndex; + uint32_t VirtualAddress; + } Type; + uint16_t Linenumber; +} EpepCoffLinenumber; + +int epep_get_section_line_number_by_index(Epep *epep, EpepSectionHeader *sh, EpepCoffLinenumber *ln, size_t index); + +#ifdef EPEP_INST + +// +// Private functions +// + +static int epep_seek(Epep *epep, size_t offset) { + EPEP_READER_SEEK(&epep->reader, offset); + return 1; +} + +static int epep_read_block(Epep *epep, size_t size, void *block) { + EPEP_READER_GET_BLOCK(&epep->reader, size, block); + return 1; +} + +static int is_pe32(Epep *epep) { + return epep->optionalHeader.Magic == 0x10b; +} + +static int is_pe32p(Epep *epep) { + return epep->optionalHeader.Magic == 0x20b; +} + +static uint8_t epep_read_u8(Epep *epep) { + return EPEP_READER_GET(&epep->reader); +} + +static uint16_t epep_read_u16(Epep *epep) { + unsigned l = epep_read_u8(epep); + unsigned h = epep_read_u8(epep); + return l | (h << 8); +} + +static uint32_t epep_read_u32(Epep *epep) { + unsigned b0 = epep_read_u8(epep); + unsigned b1 = epep_read_u8(epep); + unsigned b2 = epep_read_u8(epep); + unsigned b3 = epep_read_u8(epep); + return b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); +} + +static uint64_t epep_read_u64(Epep *epep) { + uint64_t res = 0; + for (unsigned i = 0; i < 64; i += 8) { + res |= epep_read_u8(epep) << i; + } + return res; +} + +static uint64_t epep_read_ptr(Epep *epep) { + return is_pe32(epep) ? epep_read_u32(epep) : epep_read_u64(epep); +} + +// +// Generic +// + +int epep_init(Epep *epep, EPEP_READER reader) { + *epep = (Epep){ 0 }; + epep->kind = EPEP_IMAGE; + epep->reader = reader; + epep->error_code = EPEP_ERR_SUCCESS; + epep->signature_offset_offset = 0x3c; + epep_seek(epep, epep->signature_offset_offset); + epep->signature_offset = 0; + epep->signature_offset |= epep_read_u8(epep); + epep->signature_offset |= epep_read_u8(epep) << 8; + epep->signature_offset |= epep_read_u8(epep) << 16; + epep->signature_offset |= epep_read_u8(epep) << 24; + epep_seek(epep, epep->signature_offset); + char signature_buf[4]; + signature_buf[0] = epep_read_u8(epep); + signature_buf[1] = epep_read_u8(epep); + signature_buf[2] = epep_read_u8(epep); + signature_buf[3] = epep_read_u8(epep); + if (signature_buf[0] != 'P' || signature_buf[1] != 'E' || + signature_buf[2] != '\0' || signature_buf[3] != '\0') { + epep->kind = EPEP_OBJECT; + epep_seek(epep, 0); + } + epep->coffFileHeader.Machine = epep_read_u16(epep); + epep->coffFileHeader.NumberOfSections = epep_read_u16(epep); + epep->coffFileHeader.TimeDateStamp = epep_read_u32(epep); + epep->coffFileHeader.PointerToSymbolTable = epep_read_u32(epep); + epep->coffFileHeader.NumberOfSymbols = epep_read_u32(epep); + epep->coffFileHeader.SizeOfOptionalHeader = epep_read_u16(epep); + epep->coffFileHeader.Characteristics = epep_read_u16(epep); + if (epep->coffFileHeader.SizeOfOptionalHeader != 0) { + // Standard fields + epep->optionalHeader.Magic = epep_read_u16(epep); + epep->optionalHeader.MajorLinkerVersion = epep_read_u8(epep); + epep->optionalHeader.MinorLinkerVersion = epep_read_u8(epep); + epep->optionalHeader.SizeOfCode = epep_read_u32(epep); + epep->optionalHeader.SizeOfInitializedData = epep_read_u32(epep); + epep->optionalHeader.SizeOfUninitializedData = epep_read_u32(epep); + epep->optionalHeader.AddressOfEntryPoint = epep_read_u32(epep); + epep->optionalHeader.BaseOfCode = epep_read_u32(epep); + if (is_pe32(epep)) { + epep->optionalHeader.BaseOfData = epep_read_u32(epep); + } + // Windows-specific fields + epep->optionalHeader.ImageBase = epep_read_ptr(epep); + epep->optionalHeader.SectionAlignment = epep_read_u32(epep); + epep->optionalHeader.FileAlignment = epep_read_u32(epep); + epep->optionalHeader.MajorOperatingSystemVersion = epep_read_u16(epep); + epep->optionalHeader.MinorOperatingSystemVersion = epep_read_u16(epep); + epep->optionalHeader.MajorImageVersion = epep_read_u16(epep); + epep->optionalHeader.MinorImageVersion = epep_read_u16(epep); + epep->optionalHeader.MajorSubsystemVersion = epep_read_u16(epep); + epep->optionalHeader.Win32VersionValue = epep_read_u32(epep); + epep->optionalHeader.MinorSubsystemVersion = epep_read_u16(epep); + epep->optionalHeader.SizeOfImage = epep_read_u32(epep); + epep->optionalHeader.SizeOfHeaders = epep_read_u32(epep); + epep->optionalHeader.CheckSum = epep_read_u32(epep); + epep->optionalHeader.Subsystem = epep_read_u16(epep); + epep->optionalHeader.DllCharacteristics = epep_read_u16(epep); + epep->optionalHeader.SizeOfStackReserve = epep_read_ptr(epep); + epep->optionalHeader.SizeOfStackCommit = epep_read_ptr(epep); + epep->optionalHeader.SizeOfHeapReserve = epep_read_ptr(epep); + epep->optionalHeader.SizeOfHeapCommit = epep_read_ptr(epep); + epep->optionalHeader.LoaderFlags = epep_read_u32(epep); + epep->optionalHeader.NumberOfRvaAndSizes = epep_read_u32(epep); + epep->first_data_directory_offset = EPEP_READER_TELL(&epep->reader); + } + epep->first_section_header_offset = EPEP_READER_TELL(&epep->reader); + if (epep->coffFileHeader.SizeOfOptionalHeader != 0) { + epep->first_section_header_offset += epep->optionalHeader.NumberOfRvaAndSizes * sizeof(EpepImageDataDirectory); + } + return 1; +} + +int epep_get_file_offset_by_rva(Epep *epep, size_t *offset, size_t addr) { + EpepSectionHeader sh = { 0 }; + if (!epep_get_section_header_by_rva(epep, &sh, addr)) { + return 0; + } + size_t diff = addr - sh.VirtualAddress; + if (diff >= sh.SizeOfRawData) { + epep->error_code = EPEP_ERR_ADDRESS_IS_OUT_OF_SECTION_RAW_DATA; + return 0; + } + *offset = sh.PointerToRawData + diff; + return 1; +} + +// +// Data Directories +// + +int epep_get_data_directory_by_index(Epep *epep, EpepImageDataDirectory *idd, size_t index) { + if (index >= epep->optionalHeader.NumberOfRvaAndSizes) { + epep->error_code = EPEP_ERR_DATA_DIRECTORY_INDEX_IS_INVALID; + return 0; + } + epep_seek(epep, epep->first_data_directory_offset + sizeof(EpepImageDataDirectory) * index); + idd->VirtualAddress = epep_read_u32(epep); + idd->Size = epep_read_u32(epep); + return 1; +} + +// +// Sections +// + +int epep_get_section_header_by_index(Epep *epep, EpepSectionHeader *sh, size_t index) { + if (index >= epep->coffFileHeader.NumberOfSections) { + epep->error_code = EPEP_ERR_SECTION_HEADER_INDEX_IS_INVALID; + return 0; + } + epep_seek(epep, epep->first_section_header_offset + sizeof(EpepSectionHeader) * index); + for (int i = 0; i < 8; i++) { + sh->Name[i] = epep_read_u8(epep); + } + sh->VirtualSize = epep_read_u32(epep); + sh->VirtualAddress = epep_read_u32(epep); + sh->SizeOfRawData = epep_read_u32(epep); + sh->PointerToRawData = epep_read_u32(epep); + sh->PointerToRelocations = epep_read_u32(epep); + sh->PointerToLinenumbers = epep_read_u32(epep); + sh->NumberOfRelocations = epep_read_u16(epep); + sh->NumberOfLinenumbers = epep_read_u16(epep); + sh->Characteristics = epep_read_u32(epep); + return 1; +} + +int epep_get_section_header_by_rva(Epep *epep, EpepSectionHeader *sh, size_t addr) { + EpepSectionHeader sh0 = { 0 }; + for (size_t i = 0; i < epep->coffFileHeader.NumberOfSections; i++) { + epep_get_section_header_by_index(epep, &sh0, i); + if (addr >= sh0.VirtualAddress && addr < (sh0.VirtualAddress + sh0.VirtualSize)) { + *sh = sh0; + return 1; + } + } + epep->error_code = EPEP_ERR_ADDRESS_IS_OUT_OF_ANY_SECTION; + return 0; +} + +int epep_get_section_contents(Epep *epep, EpepSectionHeader *sh, void *buf) { + size_t size_of_raw_data = sh->SizeOfRawData; + epep_seek(epep, sh->PointerToRawData); + epep_read_block(epep, size_of_raw_data, buf); + return 1; +} + +// +// COFF Symbols +// + +int epep_get_string_table_size(Epep *epep, size_t *size) { + epep_seek(epep, epep->coffFileHeader.PointerToSymbolTable + 18 * epep->coffFileHeader.NumberOfSymbols); + *size = epep_read_u32(epep); + return 1; +} + +int epep_get_string_table(Epep *epep, char *string_table) { + size_t size = 0; + if (!epep_get_string_table_size(epep, &size)) { + return 0; + } + // A COFF strings table starts with its size + *string_table++ = (size & 0x000000ff) >> 0; + *string_table++ = (size & 0x0000ff00) >> 8; + *string_table++ = (size & 0x00ff0000) >> 16; + *string_table++ = (size & 0xff000000) >> 24; + epep_read_block(epep, size - 4, string_table); + return 1; +} + +int epep_get_symbol_by_index(Epep *epep, EpepCoffSymbol *sym, size_t index) { + if (epep->kind != EPEP_OBJECT) { + epep->error_code = EPEP_ERR_NOT_AN_OBJECT; + return 0; + } + if (index >= epep->coffFileHeader.NumberOfSymbols) { + epep->error_code = EPEP_ERR_SYMBOL_INDEX_IS_INVALID; + return 0; + } + epep_seek(epep, epep->coffFileHeader.PointerToSymbolTable + 18 * index); + for (size_t i = 0; i < 18; i++) { + sym->auxFile.FileName[i] = epep_read_u8(epep); + } + return 1; +} + +// +// Imports +// + +int epep_has_import_table(Epep *epep) { + if (epep->kind != EPEP_IMAGE) { + return 0; + } + EpepImageDataDirectory idd = { 0 }; + if (!epep_get_data_directory_by_index(epep, &idd, 1)) { + return 0; + } + return idd.VirtualAddress; +} + +int epep_read_import_table_offset(Epep *epep) { + EpepImageDataDirectory import_table_dd = { 0 }; + if (!epep_get_data_directory_by_index(epep, &import_table_dd, 1)) { + return 0; + } + if (!epep_get_file_offset_by_rva(epep, &epep->import_table_offset, import_table_dd.VirtualAddress)) { + return 0; + } + return 1; +} + +int epep_get_import_directory_by_index(Epep *epep, EpepImportDirectory *import_directory, size_t index) { + if (epep->import_table_offset == 0) { + if (!epep_read_import_table_offset(epep)) { + return 0; + } + } + epep_seek(epep, epep->import_table_offset + index * sizeof(*import_directory)); + epep_read_block(epep, sizeof(*import_directory), import_directory); + return 1; +} + +int epep_get_import_directory_name_s(Epep *epep, EpepImportDirectory *import_directory, char *name, size_t name_max) { + size_t name_rva = import_directory->NameRva; + size_t name_offset = 0; + if (!epep_get_file_offset_by_rva(epep, &name_offset, name_rva)) { + return 0; + } + epep_seek(epep, name_offset); + epep_read_block(epep, name_max, name); + return 1; +} + +int epep_get_import_directory_lookup_by_index(Epep *epep, EpepImportDirectory *import_directory, size_t *lookup, size_t index) { + size_t first_lookup_offset = 0; + if (!epep_get_file_offset_by_rva(epep, &first_lookup_offset, import_directory->ImportLookupTableRva)) { + return 0; + } + size_t size_of_lookup = is_pe32(epep) ? 4 : 8; + size_t lookup_offset = first_lookup_offset + size_of_lookup * index; + epep_seek(epep, lookup_offset); + epep_read_block(epep, size_of_lookup, lookup); + return 1; +} + +int epep_get_lookup_name_s(Epep *epep, size_t lookup, char *name, size_t name_max) { + if (name_max == 0) { + epep->error_code = EPEP_ERR_OUTPUT_CAPACITY_IS_ZERO; + return 0; + } + if (name == NULL) { + epep->error_code = EPEP_ERR_OUTPUT_IS_NULL; + return 0; + } + uint64_t mask = is_pe32(epep) ? 0x80000000 : 0x8000000000000000; + if (lookup & mask) { + name[0] = '\0'; + return 1; + } + size_t name_rva = lookup; + size_t name_offset = 0; + if (!epep_get_file_offset_by_rva(epep, &name_offset, name_rva)) { + return 0; + } + // skip 2 bytes (Name Table :: Hint) + name_offset += 2; + epep_seek(epep, name_offset); + epep_read_block(epep, name_max, name); + return 1; +} + +// +// Exports +// + +int epep_has_export_table(Epep *epep) { + if (epep->kind != EPEP_IMAGE) { + return 0; + } + EpepImageDataDirectory idd = { 0 }; + if (!epep_get_data_directory_by_index(epep, &idd, 0)) { + return 0; + } + return idd.VirtualAddress; +} + +int epep_read_export_table_offset(Epep *epep) { + EpepImageDataDirectory export_table_dd = { 0 }; + if (!epep_get_data_directory_by_index(epep, &export_table_dd, 0)) { + return 0; + } + if (!epep_get_file_offset_by_rva(epep, &epep->export_table_offset, export_table_dd.VirtualAddress)) { + return 0; + } + return 1; +} + +int epep_read_export_directory(Epep *epep) { + if (epep->export_table_offset == 0) { + if (!epep_read_export_table_offset(epep)) { + return 0; + } + } + epep_seek(epep, epep->export_table_offset); + epep_read_block(epep, sizeof(epep->export_directory), &epep->export_directory); + return 1; +} + +int epep_get_dll_name_s(Epep *epep, char *name, size_t name_max) { + size_t offset = 0; + if (!epep_get_file_offset_by_rva(epep, &offset, epep->export_directory.NameRva)) { + return 0; + } + epep_seek(epep, offset); + epep_read_block(epep, name_max, name); + return 1; +} + +int epep_get_export_name_pointer_by_index(Epep *epep, size_t *name_rva, size_t index) { + size_t name_pointer_table_rva = epep->export_directory.NamePointerRva; + size_t name_pointer_table_offset = 0; + if (!epep_get_file_offset_by_rva(epep, &name_pointer_table_offset, name_pointer_table_rva)) { + return 0; + } + epep_seek(epep, name_pointer_table_offset + sizeof(uint32_t) * index); + *name_rva = epep_read_u32(epep); + return 1; +} + +int epep_get_export_name_s_by_index(Epep *epep, char *name, size_t name_max, size_t index) { + size_t ordinal_table_offset = 0; + if (!epep_get_file_offset_by_rva(epep, &ordinal_table_offset, epep->export_directory.OrdinalTableRva)) { + return 0; + } + epep_seek(epep, ordinal_table_offset); + for (size_t i = 0; i < epep->export_directory.NumberOfNamePointers; i++) { + uint16_t ordinal = epep_read_u16(epep); + if (ordinal == index) { // SPEC_VIOL: Why should not epep->export_directory.OrdinalBase be substracted? + size_t name_rva = 0; + if (!epep_get_export_name_pointer_by_index(epep, &name_rva, i)) { + return 0; + } + size_t name_offset = 0; + if (!epep_get_file_offset_by_rva(epep, &name_offset, name_rva)) { + return 0; + } + epep_seek(epep, name_offset); + epep_read_block(epep, name_max, name); + return 1; + } + } + epep->error_code = EPEP_ERR_EXPORT_ADDRESS_TABLE_ENTRY_NAME_NOT_FOUND; + return 0; +} + +int epep_get_export_address_by_index(Epep *epep, EpepExportAddress *export_address, size_t index) { + size_t export_address_table_offset = 0; + if (!epep_get_file_offset_by_rva(epep, &export_address_table_offset, epep->export_directory.ExportAddressTableRva)) { + return 0; + } + EPEP_ASSERT(sizeof(EpepExportAddress) == sizeof(uint32_t)); + epep_seek(epep, export_address_table_offset + sizeof(EpepExportAddress) * index); + epep_read_block(epep, sizeof(*export_address), export_address); + return 1; +} + +int epep_get_export_address_forwarder_s(Epep *epep, EpepExportAddress *export_address, char *forwarder, size_t forwarder_max) { + size_t forwarder_offset = 0; + if (!epep_get_file_offset_by_rva(epep, &forwarder_offset, export_address->ForwarderRva)) { + return 0; + } + epep_seek(epep, forwarder_offset); + epep_read_block(epep, forwarder_max, forwarder); + return 1; +} + +int epep_export_address_is_forwarder(Epep *epep, EpepExportAddress *export_address) { + EpepImageDataDirectory edd = { 0 }; + if (!epep_get_data_directory_by_index(epep, &edd, 0)) { + return 0; + } + if (export_address->ForwarderRva >= edd.VirtualAddress && export_address->ForwarderRva < edd.VirtualAddress + edd.Size) { + return 1; + } + return 0; +} + +// +// DLL Base Relocaions +// + +int epep_has_base_relocation_table(Epep *epep) { + EpepImageDataDirectory brtdd = { 0 }; + if (!epep_get_data_directory_by_index(epep, &brtdd, 5)) { + return 0; + } + if (brtdd.VirtualAddress == 0) { + return 0; + } + return 1; +} + +int epep_read_base_relocation_table_offset(Epep *epep) { + EpepImageDataDirectory brtdd = { 0 }; + if (!epep_get_data_directory_by_index(epep, &brtdd, 5)) { + return 0; + } + if (!epep_get_file_offset_by_rva(epep, &epep->base_relocation_table_offset, brtdd.VirtualAddress)) { + return 0; + } + epep->base_relocation_table_end_offset = epep->base_relocation_table_offset + brtdd.Size; + return 1; +} + +int epep_get_first_base_relocation_block(Epep *epep, EpepBaseRelocationBlock *brb) { + if (epep->base_relocation_table_offset == 0) { + if (!epep_read_base_relocation_table_offset(epep)) { + return 0; + } + } + if (epep->base_relocation_table_offset == 0) { + epep->error_code = EPEP_ERR_NO_BASE_RELOCATION_TABLE; + return 0; + } + if (!epep_seek(epep, epep->base_relocation_table_offset)) { + return 0; + } + brb->offset = epep->base_relocation_table_offset; + brb->PageRva = epep_read_u32(epep); + brb->BlockSize = epep_read_u32(epep); + return 1; +} + +int epep_get_next_base_relocation_block(Epep *epep, EpepBaseRelocationBlock *it) { + if (it->offset == 0) { + epep->error_code = EPEP_ERR_BASE_RELOCATION_IS_ALREADY_END; + return 0; + } + it->offset = it->offset + it->BlockSize; + if (it->offset >= epep->base_relocation_table_end_offset) { + *it = (EpepBaseRelocationBlock){ 0 }; + return 1; + } + if (!epep_seek(epep, it->offset)) { + return 0; + } + it->PageRva = epep_read_u32(epep); + it->BlockSize = epep_read_u32(epep); + return 1; +} + +int epep_get_base_relocation_block_base_relocation_by_index(Epep *epep, EpepBaseRelocationBlock *brb, EpepBaseRelocation *br, size_t index) { + if (!epep_seek(epep, brb->offset + 8 + sizeof(EpepBaseRelocation) * index)) { + return 0; + } + br->u16 = epep_read_u16(epep); + return 1; +} + +// +// COFF Relocations +// + +int epep_get_section_relocation_by_index(Epep *epep, EpepSectionHeader *sh, EpepCoffRelocation *rel, size_t index) { + size_t relocationsOffset = sh->PointerToRelocations; + epep_seek(epep, relocationsOffset + 10 * index); + epep_read_block(epep, 10, rel); + return 1; +} + +// +// COFF Line Numbers +// + +int epep_get_section_line_number_by_index(Epep *epep, EpepSectionHeader *sh, EpepCoffLinenumber *ln, size_t index) { + size_t LinenumbersOffset = sh->PointerToLinenumbers; + epep_seek(epep, LinenumbersOffset + 6 * index); + epep_read_block(epep, 6, ln); + return 1; +} + +#endif // EPEP_INST