diff --git a/examples/elf/main/CMakeLists.txt b/examples/elf/main/CMakeLists.txt index 3ae5a347e50..49b86cdf748 100644 --- a/examples/elf/main/CMakeLists.txt +++ b/examples/elf/main/CMakeLists.txt @@ -19,7 +19,98 @@ # ############################################################################## if(CONFIG_EXAMPLES_ELF) - nuttx_add_application(NAME elf SRCS elf_main.c) + set(ELF_SYMTAB ${CMAKE_CURRENT_BINARY_DIR}/test_symtab.c) + set(ELF_ROMFS_IMG ${CMAKE_CURRENT_BINARY_DIR}/elf_romfs.img) + set(ELF_ROMFS_SRC ${CMAKE_CURRENT_BINARY_DIR}/elf_romfs.c) + + set(ELFNAME_BASE hello) + + set(ELF_BINARY_FILES) + set(ELF_BINARY_TARGETS) + foreach(ELFNAME IN LISTS ELFNAME_BASE) + list(APPEND ELF_BINARY_FILES ${CMAKE_BINARY_DIR}/bin/${ELFNAME}) + + if(CMAKE_C_ELF_COMPILER) + list(APPEND ELF_BINARY_TARGETS ${ELFNAME}) + else() + list(APPEND ELF_BINARY_TARGETS ELF_${ELFNAME}) + endif() + endforeach() + + add_custom_command( + OUTPUT ${ELF_SYMTAB} + COMMAND ${NUTTX_APPS_DIR}/tools/mksymtab.sh ${ELF_BINARY_FILES} g_elf > + ${ELF_SYMTAB} + DEPENDS ${ELF_BINARY_TARGETS}) + + set_source_files_properties(${ELF_SYMTAB} PROPERTIES GENERATED TRUE) + + set(ELF_GENERATED_OUTPUTS ${ELF_SYMTAB}) + set(ELF_SRCS elf_main.c ${ELF_SYMTAB}) + + if(CONFIG_EXAMPLES_ELF_ROMFS) + set(ELF_ROMFS_DEPS ${ELF_BINARY_TARGETS}) + + if(CONFIG_SYSTEM_NXPKG) + set(ELF_INDEX_JSON ${CMAKE_BINARY_DIR}/bin/index.json) + set(ELF_BAD_INDEX_JSON ${CMAKE_BINARY_DIR}/bin/bad-index.json) + set(ELF_PKGTEST ${CMAKE_BINARY_DIR}/bin/pkgtest.nsh) + set(ELF_PKGFAIL ${CMAKE_BINARY_DIR}/bin/pkgfail.nsh) + set(ELF_HELLO_BIN ${CMAKE_BINARY_DIR}/bin/hello) + set(ELF_FIXTURE_HOST_DIR ${CMAKE_CURRENT_BINARY_DIR}/host) + set(ELF_FIXTURE_HOST_SRC ${CMAKE_CURRENT_SOURCE_DIR}/host/CMakeLists.txt) + set(ELF_FIXTURE_TOOL + ${ELF_FIXTURE_HOST_DIR}/mk_pkg_fixture${CMAKE_EXECUTABLE_SUFFIX}) + set(ELF_HELLO_TARGET hello) + + if(NOT CMAKE_C_ELF_COMPILER) + set(ELF_HELLO_TARGET ELF_hello) + endif() + + add_custom_command( + OUTPUT ${ELF_FIXTURE_TOOL} + COMMAND ${CMAKE_COMMAND} -S ${CMAKE_CURRENT_SOURCE_DIR}/host -B + ${ELF_FIXTURE_HOST_DIR} + COMMAND ${CMAKE_COMMAND} --build ${ELF_FIXTURE_HOST_DIR} --target + mk_pkg_fixture + DEPENDS ${ELF_FIXTURE_HOST_SRC} + ${CMAKE_CURRENT_SOURCE_DIR}/mk_pkg_fixture.c + VERBATIM) + + add_custom_command( + OUTPUT ${ELF_INDEX_JSON} ${ELF_BAD_INDEX_JSON} ${ELF_PKGTEST} + ${ELF_PKGFAIL} + COMMAND + ${ELF_FIXTURE_TOOL} ${ELF_HELLO_BIN} ${ELF_INDEX_JSON} + ${ELF_BAD_INDEX_JSON} ${ELF_PKGTEST} ${ELF_PKGFAIL} ${CONFIG_ARCH} + ${CONFIG_ARCH_BOARD} + DEPENDS ${ELF_HELLO_TARGET} ${ELF_FIXTURE_TOOL} + VERBATIM) + + list(APPEND ELF_ROMFS_DEPS ${ELF_INDEX_JSON} ${ELF_BAD_INDEX_JSON} + ${ELF_PKGTEST} ${ELF_PKGFAIL}) + endif() + + add_custom_command( + OUTPUT ${ELF_ROMFS_IMG} + COMMAND genromfs -f ${ELF_ROMFS_IMG} -d ${CMAKE_BINARY_DIR}/bin + DEPENDS ${ELF_ROMFS_DEPS} + VERBATIM) + + add_custom_command( + OUTPUT ${ELF_ROMFS_SRC} + COMMAND xxd -i ${ELF_ROMFS_IMG} > ${ELF_ROMFS_SRC} + DEPENDS ${ELF_ROMFS_IMG}) + + set_source_files_properties(${ELF_ROMFS_SRC} PROPERTIES GENERATED TRUE) + + list(APPEND ELF_GENERATED_OUTPUTS ${ELF_ROMFS_SRC}) + list(APPEND ELF_SRCS ${ELF_ROMFS_SRC}) + endif() + + add_custom_target(elf_artifacts DEPENDS ${ELF_GENERATED_OUTPUTS}) + + nuttx_add_application(NAME elf SRCS ${ELF_SRCS} DEPENDS elf_artifacts) # TODO: tests diff --git a/examples/elf/main/Makefile b/examples/elf/main/Makefile index 76093ea5f88..a147d165cda 100644 --- a/examples/elf/main/Makefile +++ b/examples/elf/main/Makefile @@ -69,8 +69,34 @@ ifeq ($(CONFIG_EXAMPLES_ELF_ROMFS),y) ROMFSIMG = elf_romfs.img ROMFSSRC = elf_romfs.c ROMFSOBJ = $(ROMFSSRC:.c=$(OBJEXT)) +ifdef CONFIG_SYSTEM_NXPKG +HOSTOBJSEXT ?= hobj +PKGINDEX = $(BINDIR)/index.json +PKGBADINDEX = $(BINDIR)/bad-index.json +PKGTEST = $(BINDIR)/pkgtest.nsh +PKGFAIL = $(BINDIR)/pkgfail.nsh +PKG_FIXTURE_GEN = mk_pkg_fixture$(HOSTEXEEXT) +PKG_FIXTURE_SRCS = mk_pkg_fixture.c +PKG_FIXTURE_OBJS = $(PKG_FIXTURE_SRCS:.c=.$(HOSTOBJSEXT)) +PKG_FIXTURE_OUTPUTS = $(PKGINDEX) $(PKGBADINDEX) $(PKGTEST) $(PKGFAIL) +PKG_FIXTURE_INPUTS = $(BINDIR)/hello +PKG_FIXTURE_ARGS = \ + $(BINDIR)/hello $(PKGINDEX) $(PKGBADINDEX) $(PKGTEST) $(PKGFAIL) \ + $(CONFIG_ARCH) $(CONFIG_ARCH_BOARD) + +$(PKG_FIXTURE_OBJS): %.$(HOSTOBJSEXT): %.c + @echo "CC: $<" + $(Q) $(HOSTCC) -c $(HOSTCFLAGS) $< -o $@ + +$(PKG_FIXTURE_GEN): $(PKG_FIXTURE_OBJS) + $(Q) $(HOSTCC) $(HOSTLDFLAGS) $(PKG_FIXTURE_OBJS) -o $@ + +$(PKG_FIXTURE_OUTPUTS) &: $(PKG_FIXTURE_INPUTS) $(PKG_FIXTURE_GEN) + $(Q) ./$(PKG_FIXTURE_GEN) \ + $(PKG_FIXTURE_ARGS) +endif -$(ROMFSIMG): +$(ROMFSIMG): $(PKG_FIXTURE_OUTPUTS) $(Q) genromfs -d $(BINDIR) -f $@ $(ROMFSSRC): $(ROMFSIMG) @@ -106,7 +132,7 @@ postinstall:: $(ROMFSOBJ) $(SYMTABOBJ) $(FSIMG_OBJ) $(call ARLOCK, $(call CONVERT_PATH,$(BIN)), $^) distclean:: - $(Q) $(call DELFILE, $(SYMTABSRC) $(SYMTABOBJ) $(ROMFSSRC) $(ROMFSIMG) $(ROMFSOBJ) $(MODLUE_NAME)) + $(Q) $(call DELFILE, $(SYMTABSRC) $(SYMTABOBJ) $(ROMFSSRC) $(ROMFSIMG) $(ROMFSOBJ) $(MODLUE_NAME) $(PKG_FIXTURE_GEN) $(PKG_FIXTURE_OBJS)) include $(APPDIR)/Application.mk diff --git a/examples/elf/main/host/CMakeLists.txt b/examples/elf/main/host/CMakeLists.txt new file mode 100644 index 00000000000..a2474cb7a83 --- /dev/null +++ b/examples/elf/main/host/CMakeLists.txt @@ -0,0 +1,26 @@ +# ############################################################################## +# apps/examples/elf/main/host/CMakeLists.txt +# +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed to the Apache Software Foundation (ASF) under one or more contributor +# license agreements. See the NOTICE file distributed with this work for +# additional information regarding copyright ownership. The ASF licenses this +# file to you 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. +# +# ############################################################################## + +cmake_minimum_required(VERSION 3.16) +project(elf_fixture_host LANGUAGES C) + +add_executable(mk_pkg_fixture ../mk_pkg_fixture.c) diff --git a/examples/elf/main/mk_pkg_fixture.c b/examples/elf/main/mk_pkg_fixture.c new file mode 100644 index 00000000000..0e69b7ee998 --- /dev/null +++ b/examples/elf/main/mk_pkg_fixture.c @@ -0,0 +1,454 @@ +/**************************************************************************** + * apps/examples/elf/main/mk_pkg_fixture.c + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SHA256_BLOCK_SIZE 64 +#define SHA256_DIGEST_SIZE 32 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sha256_state_s +{ + uint32_t state[8]; + uint64_t bitcount; + uint8_t buffer[SHA256_BLOCK_SIZE]; + size_t buffer_len; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const uint32_t g_sha256_init[8] = +{ + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +static const uint32_t g_sha256_k[64] = +{ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static uint32_t sha256_rotr(uint32_t value, unsigned int bits) +{ + return (value >> bits) | (value << (32 - bits)); +} + +static uint32_t sha256_ch(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) ^ (~x & z); +} + +static uint32_t sha256_maj(uint32_t x, uint32_t y, uint32_t z) +{ + return (x & y) ^ (x & z) ^ (y & z); +} + +static uint32_t sha256_bs0(uint32_t x) +{ + return sha256_rotr(x, 2) ^ sha256_rotr(x, 13) ^ sha256_rotr(x, 22); +} + +static uint32_t sha256_bs1(uint32_t x) +{ + return sha256_rotr(x, 6) ^ sha256_rotr(x, 11) ^ sha256_rotr(x, 25); +} + +static uint32_t sha256_ss0(uint32_t x) +{ + return sha256_rotr(x, 7) ^ sha256_rotr(x, 18) ^ (x >> 3); +} + +static uint32_t sha256_ss1(uint32_t x) +{ + return sha256_rotr(x, 17) ^ sha256_rotr(x, 19) ^ (x >> 10); +} + +static uint32_t sha256_load32(const uint8_t *src) +{ + return ((uint32_t)src[0] << 24) | ((uint32_t)src[1] << 16) | + ((uint32_t)src[2] << 8) | (uint32_t)src[3]; +} + +static void sha256_store32(uint8_t *dst, uint32_t value) +{ + dst[0] = (uint8_t)(value >> 24); + dst[1] = (uint8_t)(value >> 16); + dst[2] = (uint8_t)(value >> 8); + dst[3] = (uint8_t)value; +} + +static void sha256_transform(struct sha256_state_s *ctx, + const uint8_t block[SHA256_BLOCK_SIZE]) +{ + uint32_t schedule[64]; + uint32_t a; + uint32_t b; + uint32_t c; + uint32_t d; + uint32_t e; + uint32_t f; + uint32_t g; + uint32_t h; + int i; + + for (i = 0; i < 16; i++) + { + schedule[i] = sha256_load32(block + i * 4); + } + + for (; i < 64; i++) + { + schedule[i] = sha256_ss1(schedule[i - 2]) + schedule[i - 7] + + sha256_ss0(schedule[i - 15]) + schedule[i - 16]; + } + + a = ctx->state[0]; + b = ctx->state[1]; + c = ctx->state[2]; + d = ctx->state[3]; + e = ctx->state[4]; + f = ctx->state[5]; + g = ctx->state[6]; + h = ctx->state[7]; + + for (i = 0; i < 64; i++) + { + uint32_t t1 = h + sha256_bs1(e) + sha256_ch(e, f, g) + g_sha256_k[i] + + schedule[i]; + uint32_t t2 = sha256_bs0(a) + sha256_maj(a, b, c); + + h = g; + g = f; + f = e; + e = d + t1; + d = c; + c = b; + b = a; + a = t1 + t2; + } + + ctx->state[0] += a; + ctx->state[1] += b; + ctx->state[2] += c; + ctx->state[3] += d; + ctx->state[4] += e; + ctx->state[5] += f; + ctx->state[6] += g; + ctx->state[7] += h; +} + +static void sha256_init(struct sha256_state_s *ctx) +{ + memcpy(ctx->state, g_sha256_init, sizeof(g_sha256_init)); + ctx->bitcount = 0; + ctx->buffer_len = 0; +} + +static void sha256_update(struct sha256_state_s *ctx, const uint8_t *data, + size_t len) +{ + ctx->bitcount += (uint64_t)len * 8; + + while (len > 0) + { + size_t space = SHA256_BLOCK_SIZE - ctx->buffer_len; + size_t chunk = len < space ? len : space; + + memcpy(ctx->buffer + ctx->buffer_len, data, chunk); + ctx->buffer_len += chunk; + data += chunk; + len -= chunk; + + if (ctx->buffer_len == SHA256_BLOCK_SIZE) + { + sha256_transform(ctx, ctx->buffer); + ctx->buffer_len = 0; + } + } +} + +static void sha256_final(struct sha256_state_s *ctx, + uint8_t digest[SHA256_DIGEST_SIZE]) +{ + uint8_t length_bytes[8]; + uint8_t padding[SHA256_BLOCK_SIZE] = + { + 0x80 + }; + + int i; + + for (i = 0; i < 8; i++) + { + length_bytes[7 - i] = (uint8_t)(ctx->bitcount >> (i * 8)); + } + + if (ctx->buffer_len < 56) + { + sha256_update(ctx, padding, 56 - ctx->buffer_len); + } + else + { + sha256_update(ctx, padding, SHA256_BLOCK_SIZE - ctx->buffer_len); + sha256_update(ctx, padding + 1, 56); + } + + sha256_update(ctx, length_bytes, sizeof(length_bytes)); + + for (i = 0; i < 8; i++) + { + sha256_store32(digest + i * 4, ctx->state[i]); + } +} + +static int sha256_file(const char *path, char *digest_hex, size_t digest_len) +{ + struct sha256_state_s ctx; + uint8_t digest[SHA256_DIGEST_SIZE]; + uint8_t buffer[4096]; + static const char g_hex[] = "0123456789abcdef"; + FILE *stream; + size_t nread; + int i; + + if (digest_len < SHA256_DIGEST_SIZE * 2 + 1) + { + fprintf(stderr, "digest buffer is too small\n"); + return -1; + } + + stream = fopen(path, "rb"); + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + sha256_init(&ctx); + + while ((nread = fread(buffer, 1, sizeof(buffer), stream)) > 0) + { + sha256_update(&ctx, buffer, nread); + } + + if (ferror(stream) != 0) + { + fprintf(stderr, "failed to read %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + fclose(stream); + sha256_final(&ctx, digest); + + for (i = 0; i < SHA256_DIGEST_SIZE; i++) + { + digest_hex[i * 2] = g_hex[digest[i] >> 4]; + digest_hex[i * 2 + 1] = g_hex[digest[i] & 0x0f]; + } + + digest_hex[SHA256_DIGEST_SIZE * 2] = '\0'; + return 0; +} + +static int write_text_file(const char *path, const char *content) +{ + FILE *stream = fopen(path, "w"); + + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + if (fputs(content, stream) == EOF) + { + fprintf(stderr, "failed to write %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + if (fclose(stream) != 0) + { + fprintf(stderr, "failed to close %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +static int write_index_file(const char *path, const char *arch, + const char *compat, const char *digest) +{ + const char *artifact = "/mnt/elf/romfs/hello"; + FILE *stream = fopen(path, "w"); + + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + if (fprintf(stream, + "{\"packages\":[" + "{\"name\":\"hello\",\"version\":\"1.0.0\",\"arch\":\"%s\"," + "\"compat\":\"%s\",\"artifact\":\"%s\",\"sha256\":\"%s\"," + "\"type\":\"elf\"}," + "{\"name\":\"hello\",\"version\":\"9.9.9\",\"arch\":\"arm\"," + "\"compat\":\"stm32f4discovery\",\"artifact\":\"%s\"," + "\"sha256\":\"%s\",\"type\":\"elf\"}" + "]}\n", + arch, compat, artifact, digest, artifact, digest) < 0) + { + fprintf(stderr, "failed to write %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + if (fclose(stream) != 0) + { + fprintf(stderr, "failed to close %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +static int write_bad_index_file(const char *path, const char *arch, + const char *compat) +{ + FILE *stream = fopen(path, "w"); + + if (stream == NULL) + { + fprintf(stderr, "failed to open %s: %s\n", path, strerror(errno)); + return -1; + } + + if (fprintf(stream, + "{\"packages\":[" + "{\"name\":\"hello-missing\",\"version\":\"1.0.0\"," + "\"arch\":\"%s\",\"compat\":\"%s\"," + "\"artifact\":\"/mnt/elf/romfs/hello-missing\"," + "\"sha256\":\"" + "00000000000000000000000000000000" + "00000000000000000000000000000000" + "\",\"type\":\"elf\"}" + "]}\n", + arch, compat) < 0) + { + fprintf(stderr, "failed to write %s: %s\n", path, strerror(errno)); + fclose(stream); + return -1; + } + + if (fclose(stream) != 0) + { + fprintf(stderr, "failed to close %s: %s\n", path, strerror(errno)); + return -1; + } + + return 0; +} + +static void usage(const char *progname) +{ + fprintf(stderr, + "usage: %s " + " \n", + progname); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, char *argv[]) +{ + char digest[SHA256_DIGEST_SIZE * 2 + 1]; + static const char g_pkgtest[] = + "mount -t tmpfs /etc\n" + "mount -t tmpfs /var\n" + "mkdir /etc/nxpkg\n" + "cp /mnt/elf/romfs/index.json /etc/nxpkg/index.json\n" + "nxpkg install hello\n" + "nxpkg list\n"; + static const char g_pkgfail[] = + "mount -t tmpfs /etc\n" + "mount -t tmpfs /var\n" + "mkdir /etc/nxpkg\n" + "cp /mnt/elf/romfs/bad-index.json /etc/nxpkg/index.json\n" + "nxpkg install hello-missing\n"; + + if (argc != 8) + { + usage(argv[0]); + return EXIT_FAILURE; + } + + if (sha256_file(argv[1], digest, sizeof(digest)) < 0 || + write_index_file(argv[2], argv[6], argv[7], digest) < 0 || + write_bad_index_file(argv[3], argv[6], argv[7]) < 0 || + write_text_file(argv[4], g_pkgtest) < 0 || + write_text_file(argv[5], g_pkgfail) < 0) + { + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +}