diff --git a/.github/workflows/builds.yaml b/.github/workflows/builds.yaml index 53a4efe..8f27f59 100644 --- a/.github/workflows/builds.yaml +++ b/.github/workflows/builds.yaml @@ -38,15 +38,17 @@ concurrency: jobs: normal: runs-on: ubuntu-latest + container: quay.io/fedora/fedora:38 strategy: fail-fast: true matrix: buildtype: [debug, release] + plain: [enabled, disabled] steps: - name: Install dependencies run: | - sudo apt-get install -y meson + dnf install --assumeyes gcc meson - name: Checkout libmerr uses: actions/checkout@v3 @@ -54,7 +56,7 @@ jobs: - name: Setup run: | meson setup build --fatal-meson-warnings --werror \ - --buildtype=${{ matrix.buildtype }} + --buildtype=${{ matrix.buildtype }} -Dplain=${{ matrix.plain }} - name: Build run: | @@ -66,15 +68,17 @@ jobs: asan-ubsan: runs-on: ubuntu-latest + container: quay.io/fedora/fedora:38 strategy: fail-fast: true matrix: buildtype: [debug, release] + plain: [enabled, disabled] steps: - name: Install dependencies run: | - sudo apt-get install -y meson + dnf install --assumeyes gcc meson libasan libubsan - name: Checkout libmerr uses: actions/checkout@v3 @@ -82,7 +86,8 @@ jobs: - name: Setup run: | meson setup build --fatal-meson-warnings --werror \ - --buildtype=${{ matrix.buildtype }} -Db_sanitize=address,undefined + --buildtype=${{ matrix.buildtype }} -Db_sanitize=address,undefined \ + -Dplain=${{ matrix.plain }} - name: Build run: | diff --git a/.github/workflows/codeql.yaml b/.github/workflows/codeql.yaml index 293a2f5..b2ba3e9 100644 --- a/.github/workflows/codeql.yaml +++ b/.github/workflows/codeql.yaml @@ -33,11 +33,12 @@ permissions: jobs: codeql: runs-on: ubuntu-latest + container: quay.io/fedora/fedora:38 steps: - name: Install dependencies run: | - sudo apt-get install -y meson + sudo apt-get install -y gcc meson - name: Checkout libmerr uses: actions/checkout@v3 diff --git a/include/merr.h b/include/merr.h index fe219a1..600c5d8 100644 --- a/include/merr.h +++ b/include/merr.h @@ -15,15 +15,7 @@ extern "C" { #include #ifndef __has_attribute -#error "__has_attribute must be provided by the compiler" -#endif - -#if !__has_attribute(aligned) -#error "Copmiler must support the aligned attribute" -#endif - -#if !__has_attribute(section) -#error "Compiler must support the section attribute" +#define __has_attribute(_attr) 0 #endif #if __has_attribute(always_inline) @@ -50,23 +42,39 @@ extern "C" { #define MERR_WARN_UNUSED_RESULT #endif +#ifndef MERR_PLAIN // Alignment of merr_curr_file in section "merr" #define MERR_MAX_PATH_LENGTH (1 << 6) - #define merr_attributes __attribute__((section("merr"))) __attribute__((aligned(MERR_MAX_PATH_LENGTH))) static char merr_curr_file[MERR_MAX_PATH_LENGTH] merr_attributes MERR_USED = __BASE_FILE__; +#endif + + /* Layout of merr_t: * - * Field #bits Description - * ------ ----- ---------- - * 63..48 16 signed offset of (merr_curr_file - merr_base) / MERR_MAX_PATH_LENGTH - * 47..32 16 line number - * 31..16 16 context - * 15..0 16 error value + * If the compiler supports both the section and aligned attributes and + * MERR_PLAIN was not requested: + * + * Field #bits Description + * ------ ----- ---------- + * 63..48 16 signed offset of (merr_curr_file - merr_base) / MERR_MAX_PATH_LENGTH + * 47..32 16 line number + * 31..16 16 context + * 15..0 16 error value + * + * If the compiler does not support either of the section or aligned + * attributes, or MERR_PLAIN was requested: + * + * Field #bits Description + * ------ ----- ---------- + * 63..32 32 context + * 31..0 32 error value */ +#ifndef MERR_PLAIN + #define MERR_FILE_SHIFT 48 #define MERR_LINE_SHIFT 32 #define MERR_CTX_SHIFT 16 @@ -76,6 +84,18 @@ static char merr_curr_file[MERR_MAX_PATH_LENGTH] merr_attributes MERR_USED = __B #define MERR_CTX_MASK 0x00000000ffff0000LL #define MERR_ERRNO_MASK 0x000000000000ffffLL +#define MERR_CTX_TYPE int16_t + +#else + +#define MERR_CTX_SHIFT 32 + +#define MERR_ERRNO_MASK 0x00000000ffffffffLL + +#define MERR_CTX_TYPE int32_t + +#endif + /** * @brief The error value type. */ @@ -108,6 +128,8 @@ merr_stringify(int num); */ #define merr(_errnum) merrx((_errnum), 0) +#ifndef MERR_PLAIN + /** * @brief Pack given error number, call-site info, and context into an merr_t. * @@ -117,16 +139,33 @@ merr_stringify(int num); */ #define merrx(_errnum, _ctx) merr_pack((_errnum), (_ctx), merr_curr_file, __LINE__) +#else + +/** + * @brief Pack given error number and context into an merr_t. + * + * @param _errnum Error number. + * @param _ctx Context. + * @returns An merr_t. + */ +#define merrx(_errnum, _ctx) merr_pack((_errnum), (_ctx)) + +#endif + /** * @brief Get the context. * * @param err Error. * @returns Error context. */ -static MERR_ALWAYS_INLINE int16_t MERR_CONST MERR_USED MERR_WARN_UNUSED_RESULT +static MERR_ALWAYS_INLINE MERR_CTX_TYPE MERR_CONST MERR_USED MERR_WARN_UNUSED_RESULT merr_ctx(const merr_t err) { - return (int16_t)((err & MERR_CTX_MASK) >> MERR_CTX_SHIFT); +#ifndef MERR_PLAIN + return (MERR_CTX_TYPE)((err & MERR_CTX_MASK) >> MERR_CTX_SHIFT); +#else + return (MERR_CTX_TYPE)(err >> MERR_CTX_SHIFT); +#endif } /** @@ -138,9 +177,11 @@ merr_ctx(const merr_t err) static MERR_ALWAYS_INLINE int MERR_CONST MERR_USED MERR_WARN_UNUSED_RESULT merr_errno(const merr_t err) { - return err & MERR_ERRNO_MASK; + return (int)(err & MERR_ERRNO_MASK); } +#ifndef MERR_PLAIN + /** * @brief Get the file name the error was generated in. * @@ -165,6 +206,8 @@ merr_lineno(const merr_t err) return (uint16_t)((err & MERR_LINE_MASK) >> MERR_LINE_SHIFT); } +#endif + /** * @brief Format file, line, ctx, and errno from an merr_t into a buffer. * @@ -187,10 +230,20 @@ merr_strerrorx(merr_t err, char *buf, size_t buf_sz, merr_stringify ctx_stringif */ #define merr_strerror(_err, _buf, _buf_sz) merr_strerrorx((_err), (_buf), (_buf_sz), NULL) +#ifndef MERR_PLAIN + // This is not public API. DO NOT USE. merr_t merr_pack(int errnum, int ctx, const char *file, uint16_t line) MERR_CONST MERR_WARN_UNUSED_RESULT; +#else + +// This is not public API. DO NOT USE. +merr_t +merr_pack(int errnum, int ctx) MERR_CONST MERR_WARN_UNUSED_RESULT; + +#endif + #ifdef __cplusplus } #endif diff --git a/lib/merr.c b/lib/merr.c index d1d1834..d236f49 100644 --- a/lib/merr.c +++ b/lib/merr.c @@ -20,6 +20,7 @@ #define MERR_UNUSED #endif +#ifndef MERR_PLAIN char merr_base[MERR_MAX_PATH_LENGTH] merr_attributes = "merr_base"; char merr_bug0[MERR_MAX_PATH_LENGTH] merr_attributes = "merr_bug0"; char merr_bug1[MERR_MAX_PATH_LENGTH] merr_attributes = "merr_bug1"; @@ -27,6 +28,7 @@ char merr_bug2[MERR_MAX_PATH_LENGTH] merr_attributes = "merr_bug2"; extern uint8_t __start_merr; extern uint8_t __stop_merr; +#endif #ifndef HAVE_STRLCPY @@ -54,6 +56,8 @@ strlcpy(char * const dst, const char * const src, const size_t sz) #endif +#ifndef MERR_PLAIN + const char * merr_file(const merr_t err) { @@ -114,6 +118,27 @@ merr_pack(const int errnum, const int ctx, const char *file, const uint16_t line return err; } +#else + +merr_t +merr_pack(const int errnum, const int ctx) +{ + merr_t err = 0; + + if (errnum == 0) + return 0; + + if (errnum < INT32_MIN || errnum > INT32_MAX || ctx < INT32_MIN || ctx > INT32_MAX) + return merr(EINVAL); + + err |= ((int64_t)ctx << MERR_CTX_SHIFT); + err |= (int64_t)errnum & MERR_ERRNO_MASK; + + return err; +} + +#endif + static size_t strerror_safe(const merr_t err, char * const buf, const size_t buf_sz) { @@ -138,9 +163,8 @@ size_t merr_strerrorx(const merr_t err, char * const buf, size_t buf_sz, merr_stringify ctx_stringify) { int ret; - int16_t ctx; size_t sz = 0; - const char *file; + MERR_CTX_TYPE ctx; if (!buf && buf_sz > 0) buf_sz = 0; @@ -148,23 +172,29 @@ merr_strerrorx(const merr_t err, char * const buf, size_t buf_sz, merr_stringify if (!err) return strlcpy(buf, "Success", buf_sz); - file = merr_file(err); - if (file) { - const char *ptr; +#ifndef MERR_PLAIN + { + const char *file; - // Protect against files that may be too long. - ptr = memchr(file, '\0', MERR_MAX_PATH_LENGTH); - ret = snprintf( - buf, buf_sz, "%*s:%d: ", (int)(ptr ? ptr - file : MERR_MAX_PATH_LENGTH), file, - merr_lineno(err)); + file = merr_file(err); + if (file) { + const char *ptr; - if (ret < 0) { - sz = strlcpy(buf, "", buf_sz); - goto out; - } + // Protect against files that may be too long. + ptr = memchr(file, '\0', MERR_MAX_PATH_LENGTH); + ret = snprintf( + buf, buf_sz, "%*s:%d: ", (int)(ptr ? ptr - file : MERR_MAX_PATH_LENGTH), file, + merr_lineno(err)); - sz += (size_t)ret; + if (ret < 0) { + sz = strlcpy(buf, "", buf_sz); + goto out; + } + + sz += (size_t)ret; + } } +#endif if (sz >= buf_sz) { sz += strerror_safe(err, NULL, 0); diff --git a/lib/meson.build b/lib/meson.build index f897a39..4c5d325 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -11,9 +11,22 @@ configure_file( } ) +cc_has_aligned = cc.has_function_attribute('aligned') +cc_has_section = cc.has_function_attribute('section') +if get_option('plain').disabled() and (not cc_has_aligned or not cc_has_section) + error('Compiler must support both aligned' + + ' and section attributes if -Dplain=disabled') +endif + +c_args = [] +if get_option('plain').enabled() or not cc_has_aligned or not cc_has_section + c_args += '-DMERR_PLAIN' +endif + libmerr = static_library( 'merr', 'merr.c', + c_args: c_args, include_directories: libmerr_includes, install: true, gnu_symbol_visibility: 'hidden' @@ -25,6 +38,7 @@ variables = { } libmerr_dep = declare_dependency( + compile_args: c_args, link_with: libmerr, include_directories: libmerr_includes, variables: variables @@ -38,6 +52,7 @@ if pkg.found() name: meson.project_name(), description: 'C99+ library for error information', url: 'https://github.com/tristan957/libmerr', + extra_cflags: c_args, variables: variables ) endif diff --git a/meson.build b/meson.build index ae072aa..4038188 100644 --- a/meson.build +++ b/meson.build @@ -13,7 +13,7 @@ project( 'c_std=c99', 'warning_level=3', ], - meson_version: '>= 0.59.0' + meson_version: '>= 0.63.0' ) fs = import('fs', required: get_option('tests')) diff --git a/meson_options.txt b/meson_options.txt index 6899d20..0504924 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -2,5 +2,7 @@ # # SPDX-FileCopyrightText: 2023 Tristan Partin +option('plain', type: 'feature', value: 'auto', + description: 'Include file and line number with error number') option('tests', type: 'boolean', value: true, description: 'Build tests') diff --git a/tests/merr-test.c b/tests/merr-test.c index 0f4ae88..2b48fb3 100644 --- a/tests/merr-test.c +++ b/tests/merr-test.c @@ -14,6 +14,12 @@ #include +#if __has_attribute(unused) && defined(MERR_PLAIN) +#define MERR_PLAIN_UNUSED __attribute__((unused)) +#else +#define MERR_PLAIN_UNUSED +#endif + extern char merr_base[]; extern char merr_bug0[]; extern char merr_bug1[]; @@ -30,6 +36,7 @@ ctx_stringify(const int ctx) return "My context"; } +#ifndef MERR_PLAIN static void test_merr_bad_file(void) { @@ -63,21 +70,24 @@ test_merr_long_path(void) g_assert_cmpuint(found_sz, ==, expected_sz); g_assert_cmpstr(found, ==, expected); } +#endif static void test_merr_with_context(void) { - int line; merr_t err; - const char *file = __FILE__; + int line MERR_PLAIN_UNUSED; size_t found_sz, expected_sz; char found[512], expected[512]; + const char *file MERR_PLAIN_UNUSED = __FILE__; +#ifndef MERR_PLAIN err = merrx(ENOENT, INT16_MAX + 1); g_assert_cmpint(merr_errno(err), ==, EINVAL); err = merrx(ENOENT, INT16_MIN - 1); g_assert_cmpint(merr_errno(err), ==, EINVAL); +#endif #ifdef MERR_REL_SRC_DIR /* Point the file pointer past the prefix in order to retrieve the file @@ -88,14 +98,23 @@ test_merr_with_context(void) // clang-format off err = merrx(ENOENT, 2); line = __LINE__; - // clang-format on +#ifndef MERR_PLAIN expected_sz = (size_t)snprintf( expected, sizeof(expected), "%s:%d: %s (%d): %s (%u)", file, merr_lineno(err), strerror(merr_errno(err)), merr_errno(err), ctx_stringify(merr_ctx(err)), merr_ctx(err)); +#else + expected_sz = (size_t)snprintf( + expected, sizeof(expected), "%s (%d): %s (%u)", + strerror(merr_errno(err)), merr_errno(err), ctx_stringify(merr_ctx(err)), merr_ctx(err)); +#endif + + // clang-format on found_sz = merr_strerrorx(err, found, sizeof(found), ctx_stringify); g_assert_cmpint(merr_ctx(err), ==, 2); +#ifndef MERR_PLAIN g_assert_cmpint(merr_lineno(err), ==, line); g_assert_cmpstr(merr_file(err), ==, file); +#endif g_assert_cmpuint(found_sz, ==, expected_sz); g_assert_cmpstr(found, ==, expected); found_sz = merr_strerrorx(err, NULL, 0, ctx_stringify); @@ -116,8 +135,10 @@ test_merr_none(void) g_assert_cmpint(err, ==, 0); g_assert_cmpint(merr_errno(err), ==, 0); g_assert_cmpint(merr_ctx(err), ==, 0); +#ifndef MERR_PLAIN g_assert_cmpint(merr_lineno(err), ==, 0); g_assert_null(merr_file(err)); +#endif g_assert_cmpuint(found_sz, ==, 7); g_assert_cmpstr(found, ==, "Success"); } @@ -125,11 +146,11 @@ test_merr_none(void) static void test_merr_without_context(void) { - int line; merr_t err; - const char *file = __FILE__; + int line MERR_PLAIN_UNUSED; size_t found_sz, expected_sz; char found[512], expected[512]; + const char *file MERR_PLAIN_UNUSED = __FILE__; #ifdef MERR_REL_SRC_DIR /* Point the file pointer past the prefix in order to retrieve the file @@ -140,14 +161,25 @@ test_merr_without_context(void) // clang-format off err = merr(ENOENT); line = __LINE__; - // clang-format on + +#ifndef MERR_PLAIN expected_sz = (size_t)snprintf( expected, sizeof(expected), "%s:%d: %s (%d)", file, merr_lineno(err), strerror(merr_errno(err)), merr_errno(err)); +#else + expected_sz = (size_t)snprintf( + expected, sizeof(expected), "%s (%d)", + strerror(merr_errno(err)), merr_errno(err)); +#endif + + // clang-format on found_sz = merr_strerror(err, found, sizeof(found)); g_assert_cmpint(merr_errno(err), ==, 2); + g_assert_cmpint(merr_ctx(err), ==, 0); +#ifndef MERR_PLAIN g_assert_cmpint(merr_lineno(err), ==, line); g_assert_cmpstr(merr_file(err), ==, file); +#endif g_assert_cmpuint(found_sz, ==, expected_sz); g_assert_cmpstr(found, ==, expected); found_sz = merr_strerror(err, NULL, 0); @@ -161,8 +193,10 @@ main(int argc, char *argv[]) { g_test_init(&argc, &argv, NULL); +#ifndef MERR_PLAIN g_test_add_func("/merr/bad-file", test_merr_bad_file); g_test_add_func("/merr/long-path", test_merr_long_path); +#endif g_test_add_func("/merr/none", test_merr_none); g_test_add_func("/merr/with-context", test_merr_with_context); g_test_add_func("/merr/without-context", test_merr_without_context);