blob: c7997e761b5548f438ee359fe2d26b39e95de10a [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "target_archive/target_archive.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//
//
//
// clang-format off
#define TARGET_ARCHIVE_BUFLEN 8192
#define TARGET_ARCHIVE_BINARY_ALIGN 8
// clang-format on
//
// Target Archive
//
static void
target_archive_usage(char const * argv[])
{
fprintf(
stderr,
"Usage: %s <target archive name> <rebased target archive filename prefix> [ <binary file> ]+\n"
"\n"
"Concatenates one or more binaries prefixed by a table containing \n"
"the number of binaries and the offset and size of each binary.\n"
"\n"
" - Offsets are relative to the end of the entries[] table.\n"
" - Offsets and sizes are in bytes.\n"
" - Offsets and sizes are 64-bit.\n"
" - Binaries and their offsets are 8-byte aligned.\n"
"\n"
"Three files are output:\n"
" - <output_name>.ar : Target archive\n"
" - <output_name>.S\n : Linkable source file for including target archive\n"
" - <output_name>.h\n : Include file containing linkable symbol name of target archive\n",
argv[0]);
}
//
// Copy files_in[] to file_out.
//
// NOTE: Use of `sendfile()` probably isn't worth loss of portability.
//
static void
target_archive_copy(FILE * file_out,
FILE ** files_in,
struct target_archive_entry * entries,
uint32_t count,
int * const exit_code)
{
if (count == 0)
{
return;
}
char * buf = malloc(TARGET_ARCHIVE_BUFLEN);
if (buf == NULL)
{
exit(EXIT_FAILURE);
}
while (true)
{
// copy until error
while (true)
{
size_t const bytes = fread(buf, 1, TARGET_ARCHIVE_BUFLEN, *files_in);
if (fwrite(buf, 1, bytes, file_out) != bytes)
{
*exit_code = EXIT_FAILURE;
}
entries->size += bytes;
if (bytes < TARGET_ARCHIVE_BUFLEN)
{
if (ferror(*files_in))
{
*exit_code = EXIT_FAILURE;
}
break;
}
}
// pad with zeroes to next 8-byte offset
uint32_t const rem = (entries->size & (TARGET_ARCHIVE_BINARY_ALIGN - 1));
bool const is_unaligned = (rem != 0);
uint32_t const skip = is_unaligned ? TARGET_ARCHIVE_BINARY_ALIGN - rem : 0;
if (is_unaligned)
{
// store up to 7 zero bytes
uint8_t const zeroes[TARGET_ARCHIVE_BINARY_ALIGN - 1] = { 0 };
if (fwrite(zeroes, 1, skip, file_out) != skip)
{
*exit_code = EXIT_FAILURE;
}
}
if (--count == 0)
{
break;
}
uint64_t const offset_ru = entries->offset + entries->size + skip;
++entries;
++files_in;
entries->offset = offset_ru;
}
free(buf);
}
//
//
//
static bool
target_archive_verify(FILE ** files, char const * argv[], uint32_t count)
{
bool is_verified = true;
for (uint32_t ii = 0; ii < count; ii++)
{
if (files[ii] == NULL)
{
is_verified = false;
fprintf(stderr, "File not found: %s\n", argv[ii]);
}
}
return is_verified;
}
//
//
//
static void
target_archive_str_replace(char file_name[], size_t file_name_len, char from, char to)
{
for (size_t ii = 0; ii < file_name_len; ii++)
{
if (file_name[ii] == from)
{
file_name[ii] = to;
}
}
}
//
//
//
static void
target_archive_write_dot_h(char const * generator, char const * name, char const * rebased_name)
{
//
// What is the name of the include file?
//
size_t const name_len = strlen(rebased_name);
size_t const file_name_len = name_len + strlen(".h") + 1;
char file_name[file_name_len];
strcpy(file_name, rebased_name);
strcpy(file_name + name_len, ".h");
FILE * file = fopen(file_name, "w");
if (file == NULL)
{
exit(EXIT_FAILURE);
}
//
// Symbol name can be long but that's OK
//
target_archive_str_replace(file_name, name_len, '/', '_');
file_name[name_len] = '\0';
fprintf(file,
"// Copyright 2021 The Fuchsia Authors.All rights reserved.\n"
"// Use of this source code is governed by a BSD - style license that can be\n"
"// found in the LICENSE file.\n"
"//\n"
"// WARNING: File generated by %s\n"
"\n",
generator);
fprintf(file,
"#pragma once\n"
"\n"
"#include \"target_archive/target_archive.h\"\n"
"\n"
"#ifdef __cplusplus\n"
"extern \"C\" {\n"
"#endif\n"
"\n"
"extern struct target_archive_header const %s[];\n"
"\n"
"#ifdef __cplusplus\n"
"}\n"
"#endif\n",
name);
fclose(file);
}
//
//
//
static void
target_archive_write_dot_S(char const * generator, char const * name, char const * rebased_name)
{
//
// What is the name of the include file?
//
size_t const name_len = strlen(rebased_name);
size_t const file_name_len = name_len + strlen(".S") + 1;
char file_name[file_name_len];
strcpy(file_name, rebased_name);
strcpy(file_name + name_len, ".S");
FILE * file = fopen(file_name, "w");
if (file == NULL)
{
exit(EXIT_FAILURE);
}
//
// Symbol name can be long but that's OK
//
fprintf(file,
"# Copyright 2021 The Fuchsia Authors.All rights reserved.\n"
"# Use of this source code is governed by a BSD - style license that can be\n"
"# found in the LICENSE file.\n"
"#\n"
"# WARNING: File generated by %s\n"
"\n",
generator);
fprintf(file,
".section .rodata\n"
"\n"
".global %s\n"
".type %s, @object\n"
".align 8\n"
"\n"
"%s: .incbin \"%s.ar\"\n"
"\n",
name,
name,
name,
rebased_name);
fclose(file);
}
//
//
//
static void
target_archive_dump_linkables(char const * argv[])
{
target_archive_write_dot_h(argv[0], argv[1], argv[2]);
target_archive_write_dot_S(argv[0], argv[1], argv[2]);
}
//
//
//
int
main(int argc, char const * argv[])
{
if (argc < 3)
{
target_archive_usage(argv);
exit(EXIT_FAILURE);
}
//
// Dump linkable files first
//
target_archive_dump_linkables(argv);
//
// How many files to concatenate?
//
uint32_t const file_count = argc - 2;
uint32_t const read_count = argc - 3;
//
// Create header
//
size_t const entries_size = sizeof(struct target_archive_entry) * read_count;
size_t const header_size = sizeof(struct target_archive_header) + entries_size;
struct target_archive_header * const header = calloc(1, header_size);
if (header == NULL)
{
exit(EXIT_FAILURE);
}
struct target_archive_entry * entries = header->entries;
header->magic = TARGET_ARCHIVE_MAGIC;
header->count = read_count;
//
// What is the file name of the target archive?
//
size_t const name_len = strlen(argv[2]);
size_t const file_name_len = name_len + strlen(".ar") + 1;
char file_name[file_name_len];
strcpy(file_name, argv[2]);
strcpy(file_name + name_len, ".ar");
//
// Blindly open all files
//
FILE ** files = calloc(file_count, sizeof(*files));
if (files == NULL)
{
exit(EXIT_FAILURE);
}
files[0] = fopen(file_name, "wb");
for (uint32_t ii = 1; ii < file_count; ii++)
{
files[ii] = fopen(argv[2 + ii], "rb");
}
//
// Verify all files were opened
//
int exit_code = EXIT_FAILURE;
if (target_archive_verify(files, argv + 2, file_count))
{
exit_code = EXIT_SUCCESS;
//
// Copy all files
//
if (fseek(files[0], (long)header_size, SEEK_SET) != 0)
{
exit_code = EXIT_FAILURE;
}
target_archive_copy(files[0], files + 1, entries, read_count, &exit_code);
//
// Write header
//
if (fseek(files[0], 0, SEEK_SET) != 0)
{
exit_code = EXIT_FAILURE;
}
if (fwrite(header, 1, header_size, files[0]) != header_size)
{
exit_code = EXIT_FAILURE;
}
}
//
// Cleanup
//
for (uint32_t ii = 0; ii < file_count; ii++)
{
if (files[ii] != NULL)
{
fclose(files[ii]);
}
}
free(files);
free(header);
exit(exit_code);
}
//
//
//