Я пытаюсь работать с юнит-тестами на мезон, и получаю:
ϰ ninja
[4/4] Linking target test/crypto/crypto_tests.
FAILED: test/crypto/crypto_tests
clang++-6.0 -o test/crypto/crypto_tests 'test/crypto/test@crypto@@crypto_tests@exe/.._.._src_platform_encoding_Endian.c.o' 'test/crypto/test@crypto@@crypto_tests@exe/.._.._src_crypto_Sha1.c.o' 'test/crypto/test@crypto@@crypto_tests@exe/Sha1Tests.cpp.o' 'test/crypto/test@crypto@@crypto_tests@exe/.._.._subprojects_googletest-release-1.8.0_googletest_src_gtest-all.cc.o' 'test/crypto/test@crypto@@crypto_tests@exe/.._.._subprojects_googletest-release-1.8.0_googletest_src_gtest_main.cc.o' -Wl,--no-undefined -Wl,--as-needed -pthread
test/crypto/test@crypto@@crypto_tests@exe/Sha1Tests.cpp.o: In function `sha_one_hash_simple_Test::TestBody()':
/home/kfc/molten/magma/builddir/../test/crypto/Sha1Tests.cpp:46: undefined reference to `br_sha1_init(br_sha1_context*)'
/home/kfc/molten/magma/builddir/../test/crypto/Sha1Tests.cpp:47: undefined reference to `br_sha1_update(br_sha1_context*, void const*, unsigned long)'
/home/kfc/ecoan/molten/magma/builddir/../test/crypto/Sha1Tests.cpp:48: undefined reference to `br_sha1_out(br_sha1_context const*, void*)'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.
Однако я определил эти функции в Sha1.h:
#ifndef _CRYPTO_SHA1_H
#define _CRYPTO_SHA1_H
#include <stddef.h>
#include <string.h>
#include "../BuildSwitches.h"#include "../types/BaseTypes.h"
/**
* Symbolic identifier for SHA-1.
*/
#define br_sha1_ID 2
/**
* SHA-1 output size (in bytes).
*/
#define br_sha1_SIZE 20
/**
* SHA-1 context.
*
* Fields are not supposed to be accessed by user code.
*/
typedef struct {
unsigned char buf[64];
UINT64 count;
UINT32 val[5];
} br_sha1_context;
/**
* SHA-1 context initialisation.
*
* This function initialises or resets a context for a new SHA-1
* computation.
*
* ctx: pointer to the context structure.
*/
void br_sha1_init(br_sha1_context *ctx);
/**
* Inject some data bytes in a running SHA-1 computation.
*
* The provided context is updated with some data bytes. If the number
* of bytes (`len`) is zero, then the data pointer (`data`) is ignored
* and may be `NULL`, and this function does nothing.
*
* ctx: pointer to the context structure.
* data: pointer to the injected data.
* len: injected data length (in bytes).
*/
void br_sha1_update(br_sha1_context *ctx, const void *data, size_t len);
/**
* Compute SHA-1 output.
*
* The SHA-1 output for the concatenation of all bytes injected in the
* provided context since the last initialisation or reset call, is
* computed and written in the buffer pointed to by `out`. The context
* itself is not modified, so extra bytes may be injected afterwards
* to continue that computation.
*
* ctx: pointer to the context structure.
* out: destination buffer for the hash output.
*/
void br_sha1_out(const br_sha1_context *ctx, void *out);
/**
* Save SHA-1 running state.
*
* The running state for SHA-1 (output of the last internal block
* processing) is written in the buffer pointed to by `out`. The
* number of bytes injected since the last initialisation or reset
* call is returned. The context is not modified.
*
* ctx: pointer to the context structure.
* out: destination buffer for the running state.
*
* returns: the injected total byte length.
*/
UINT64 br_sha1_state(const br_sha1_context *ctx, void *out);
/**
* Restore SHA-1 running state.
*
* The running state for SHA-1 is set to the provided values.
*
* ctx: pointer to the context structure.
* stb: source buffer for the running state.
* count: the injected total byte length.
*/
void br_sha1_set_state(br_sha1_context *ctx, const void *stb, UINT64 count);
void br_sha1_round(const unsigned char *buf, UINT32 *val);
extern const UINT32 br_sha1_IV[];
#endif // _CRYPTO_SHA1_H
Они имеют те же определения в Sha1.c:
#include "../platform/encoding/Endian.h"
#include "./Sha1.h"
#define F(B, C, D) ((((C) ^ (D)) & (B)) ^ (D))
#define G(B, C, D) ((B) ^ (C) ^ (D))
#define H(B, C, D) (((D) & (C)) | (((D) | (C)) & (B)))
#define I(B, C, D) G(B, C, D)
#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
#define K1 ((UINT32)0x5A827999)
#define K2 ((UINT32)0x6ED9EBA1)
#define K3 ((UINT32)0x8F1BBCDC)
#define K4 ((UINT32)0xCA62C1D6)
const UINT32 br_sha1_IV[5] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 };
void br_sha1_round(const unsigned char *buf, UINT32 *val) {
UINT32 m[80];
UINT32 a, b, c, d, e;
int i;
a = val[0];
b = val[1];
c = val[2];
d = val[3];
e = val[4];
br_range_dec32be(m, 16, buf);
for (i = 16; i < 80; i ++) {
UINT32 x = m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16];
m[i] = ROTL(x, 1);
}
for (i = 0; i < 20; i += 5) {
e += ROTL(a, 5) + F(b, c, d) + K1 + m[i + 0]; b = ROTL(b, 30);
d += ROTL(e, 5) + F(a, b, c) + K1 + m[i + 1]; a = ROTL(a, 30);
c += ROTL(d, 5) + F(e, a, b) + K1 + m[i + 2]; e = ROTL(e, 30);
b += ROTL(c, 5) + F(d, e, a) + K1 + m[i + 3]; d = ROTL(d, 30);
a += ROTL(b, 5) + F(c, d, e) + K1 + m[i + 4]; c = ROTL(c, 30);
}
for (i = 20; i < 40; i += 5) {
e += ROTL(a, 5) + G(b, c, d) + K2 + m[i + 0]; b = ROTL(b, 30);
d += ROTL(e, 5) + G(a, b, c) + K2 + m[i + 1]; a = ROTL(a, 30);
c += ROTL(d, 5) + G(e, a, b) + K2 + m[i + 2]; e = ROTL(e, 30);
b += ROTL(c, 5) + G(d, e, a) + K2 + m[i + 3]; d = ROTL(d, 30);
a += ROTL(b, 5) + G(c, d, e) + K2 + m[i + 4]; c = ROTL(c, 30);
}
for (i = 40; i < 60; i += 5) {
e += ROTL(a, 5) + H(b, c, d) + K3 + m[i + 0]; b = ROTL(b, 30);
d += ROTL(e, 5) + H(a, b, c) + K3 + m[i + 1]; a = ROTL(a, 30);
c += ROTL(d, 5) + H(e, a, b) + K3 + m[i + 2]; e = ROTL(e, 30);
b += ROTL(c, 5) + H(d, e, a) + K3 + m[i + 3]; d = ROTL(d, 30);
a += ROTL(b, 5) + H(c, d, e) + K3 + m[i + 4]; c = ROTL(c, 30);
}
for (i = 60; i < 80; i += 5) {
e += ROTL(a, 5) + I(b, c, d) + K4 + m[i + 0]; b = ROTL(b, 30);
d += ROTL(e, 5) + I(a, b, c) + K4 + m[i + 1]; a = ROTL(a, 30);
c += ROTL(d, 5) + I(e, a, b) + K4 + m[i + 2]; e = ROTL(e, 30);
b += ROTL(c, 5) + I(d, e, a) + K4 + m[i + 3]; d = ROTL(d, 30);
a += ROTL(b, 5) + I(c, d, e) + K4 + m[i + 4]; c = ROTL(c, 30);
}
val[0] += a;
val[1] += b;
val[2] += c;
val[3] += d;
val[4] += e;
}
void br_sha1_init(br_sha1_context *ctx) {
memcpy(ctx->val, br_sha1_IV, sizeof ctx->val);
ctx->count = 0;
}
void br_sha1_update(br_sha1_context *cc, const void *data, size_t len) {
const unsigned char *buf;
size_t ptr;
buf = (const unsigned char *) data;
ptr = (size_t)cc->count & 63;
while (len > 0) {
size_t clen;
clen = 64 - ptr;
if (clen > len) {
clen = len;
}
memcpy(cc->buf + ptr, buf, clen);
ptr += clen;
buf += clen;
len -= clen;
cc->count += (UINT64)clen;
if (ptr == 64) {
br_sha1_round(cc->buf, cc->val);
ptr = 0;
}
}
}
void br_sha1_out(const br_sha1_context *cc, void *dst) {
unsigned char buf[64];
UINT32 val[5];
size_t ptr;
ptr = (size_t)cc->count & 63;
memcpy(buf, cc->buf, ptr);
memcpy(val, cc->val, sizeof val);
buf[ptr ++] = 0x80;
if (ptr > 56) {
memset(buf + ptr, 0, 64 - ptr);
br_sha1_round(buf, val);
memset(buf, 0, 56);
} else {
memset(buf + ptr, 0, 56 - ptr);
}
br_enc64be(buf + 56, cc->count << 3);
br_sha1_round(buf, val);
br_range_enc32be(dst, val, 5);
}
UINT64 br_sha1_state(const br_sha1_context *cc, void *dst) {
br_range_enc32be(dst, cc->val, 5);
return cc->count;
}
void br_sha1_set_state(br_sha1_context *cc, const void *stb, UINT64 count) {
br_range_dec32be(cc->val, 5, stb);
cc->count = count;
}
Затем у меня есть тестовый файл, который затем пытается проверить функции SHA1:
#include <gtest/gtest.h>
#include "../../src/crypto/Sha1.h"
static size_t hextobin(unsigned char *dst, const char *src) {
size_t num;
unsigned acc;
int z;
num = 0;
z = 0;
acc = 0;
while (*src != 0) {
int c = *src ++;
if (c >= '0' && c <= '9') {
c -= '0';
} else if (c >= 'A' && c <= 'F') {
c -= ('A' - 10);
} else if (c >= 'a' && c <= 'f') {
c -= ('a' - 10);
} else {
continue;
}
if (z) {
*dst ++ = (acc << 4) + c;
num ++;
} else {
acc = c;
}
z = !z;
}
return num;
}
TEST(sha_one, hash_simple) {
unsigned char ref[br_sha1_SIZE];
hextobin(ref, (const char *) "a9993e364706816aba3e25717850c26c9cd0d89d");
unsigned char res[br_sha1_SIZE];
const char *data = (const char *)"abc";
br_sha1_context mc;
size_t n;
n = strlen(data);
br_sha1_init(&mc);
br_sha1_update(&mc, data, n);
br_sha1_out(&mc, res);
ASSERT_EQ(res, ref);
}
Однако при использовании файла meson.build, например:
crypto_srcs = [
'../../src/platform/encoding/Endian.c',
'../../src/crypto/Sha1.c',
'Sha1Tests.cpp',
]
e = executable('crypto_tests', sources : crypto_srcs, dependencies : gtest_dep)
test('crypto tests', e)
Я получаю вышеупомянутую ошибку, утверждая, что не может найти ссылку на эти функции (но, похоже, она прекрасно разрешает фактическую структуру, определенную в этом заголовочном файле? Что меня смущает. Поэтому я попытался просмотреть символы в самом объектном файле, чтобы увидеть, были ли на самом деле определены функции:
ϰ nm ./builddir/src/src@@magmatpm@sta/crypto_Sha1.c.o
0000000000000dc0 t br_enc32be
0000000000000ce0 t br_enc64be
U br_range_dec32be
U br_range_enc32be
0000000000000a80 T br_sha1_init
0000000000000000 R br_sha1_IV
0000000000000ba0 T br_sha1_out
0000000000000000 T br_sha1_round
0000000000000d70 T br_sha1_set_state
0000000000000d30 T br_sha1_state
0000000000000ac0 T br_sha1_update
U memcpy
U memset
Который показывает функции, определенные внутри объектного файла. Так что я немного запутался в том, как я получаю эту ошибку.
Как я заметил в комментарий:
Вы скомпилировали код в
Sha1.c
с компилятором C; имена, которые вы показываете, не являются искаженными именами, которые хочет компилятор C ++. Вам необходимо скомпилировать код с помощью компилятора C ++ (при необходимости переименовать исходный код), или вам нужно сообщить коду C ++, что функции определеныextern "C"
,
Один из способов сделать это — использовать:
extern "C" {
#include "Sha1.h"}
в коде C ++, который использует код C.
Других решений пока нет …