From 5cd42cd55b0dbb0ee4550abf6d334c3ce166057a Mon Sep 17 00:00:00 2001 From: Yongkui Han Date: Mon, 24 Oct 2022 15:56:33 +0000 Subject: [PATCH] gitBOM support via environment variable GITBOM_BUILD_MODE Signed-off-by: Yongkui Han --- binutils-2.39/binutils/ar.c | 328 +++++++++++++++++++++++++++++ binutils-2.39/binutils/objcopy.c | 274 ++++++++++++++++++++++++ binutils-2.39/binutils/readelf.c | 72 +++++++ binutils-2.39/include/elf/common.h | 5 + 4 files changed, 679 insertions(+) diff --git a/binutils-2.39/binutils/ar.c b/binutils-2.39/binutils/ar.c index 0fdf0679..35e2c0f3 100644 --- a/binutils-2.39/binutils/ar.c +++ b/binutils-2.39/binutils/ar.c @@ -178,6 +178,272 @@ static struct option long_options[] = int interactive = 0; +/* Only creating new archive and ranlib are supported by gitBOM */ +/* 0 means no hash, 1 means SHA1 only, 2 means SHA256 only, and 3 means SHA1+SHA256 */ +static int gitbom_hash_mode = 0; +/* process ID, used to construct unique file name, etc. */ +static pid_t gitbom_pid = 0; +/* a buffer to construct shell command, file name, etc. */ +static char *gitbom_buf = NULL; +/* to record the build_cmd for record_hash feature */ +static int gitbom_argc = 0; +static char ** gitbom_argv = NULL; +/* the default directory to save gitbom ADG and symlink files */ +static const char *gitbom_adgdir = ".gitbom"; +/* the default directory to save gitbom log files */ +static char *gitbom_logdir = ".gitbom/metadata/binutils"; +/* do we create gitBOM ADG docs? we always create symlink from artifact-id to bom-id if creating gitBOM docs */ +static int gitbom_create_adg_flag = 0; +/* do we want to record hashes of output and input files? */ +static int gitbom_record_hash_flag = 0; +/* whether the inarch file exists before invoking this ar command */ +static int gitbom_inarch_exist = 1; +/* number of input files and the files array */ +static int gitbom_file_count = 0; +static char **gitbom_files = NULL; + +#if 1 +#define gitbom_printf(...) do { } while (0) +#else +#define gitbom_printf printf +#endif + +#define GITBOM_SHA1 1 +#define GITBOM_SHA256 2 + +/* 5 bytes "blob " + 64 bytes sha256 + 5 bytes " bom " + 64 bytes sha256 + 1 byte newline */ +#define GITBOM_MAX_DEPLINE_SIZE (5 + 64 + 5 + 64 + 1) + +/* 8192 bytes for shell command, should be big enough */ +#define GITBOM_BUF_SIZE 8192 + +/* Get the SHA1/SHA256 hash of a file */ +static char * +gitbom_get_file_hash(char *afile, int hash_alg) +{ + char tmpfile[64]; + sprintf(tmpfile, "/tmp/gitbom_ar_pid%d_hash", gitbom_pid); + sprintf(gitbom_buf, "printf \"blob %ld\\0\" | cat - %s 2>/dev/null | %s | head --bytes=-4 > %s", + get_file_size(afile), afile, hash_alg == GITBOM_SHA256 ? "sha256sum" : "sha1sum", tmpfile); + if ( -1 == system(gitbom_buf) ) { + fatal("Failed to run the get file hash command\n"); + } + off_t size = get_file_size (tmpfile); + char *buffer = (char *) xmalloc (size + 2); + FILE *f = fopen (tmpfile, FOPEN_RT); + if (f == NULL) + fatal (_("cannot open '%s': %s"), tmpfile, strerror (errno)); + if (fread (buffer, 1, size, f) == 0 || ferror (f)) + fatal (_("%s: fread failed"), tmpfile); + fclose (f); + buffer [size] = '\0'; + remove(tmpfile); + return buffer; +} + +/* Get the SHA1/SHA256 hash of a list of files */ +static char ** +gitbom_get_file_hashes(char **infiles, int infile_num, int hash_alg) +{ + char **infile_hashes = (char **) xmalloc( infile_num * sizeof(char *) ); + for(int i=0; i0; j--) { + if (gitbom_buf[j] == '/') { + bomptr = &gitbom_buf[j-2]; + break; + } + } + } + if (bomptr) { + deplines[i+1] = deplines[i] + sprintf(deplines[i], "blob %s bom %c%c%s\n", inhash, bomptr[0], bomptr[1], &bomptr[3]); + deplines[i+1][-1] = 0; + } else { + deplines[i+1] = deplines[i] + sprintf(deplines[i], "blob %s\n", inhash); + deplines[i+1][-1] = 0; + } + } + // it is required to sort the array + if (infile_num > 1) { + qsort(deplines, infile_num, sizeof(char *), strcmp_comparator); + } + // first write to a temporary file to get the hash, then figure out the file name to save + sprintf(gitbom_buf, "/tmp/gitbom_ar_pid%d_adg", gitbom_pid); + char *tmpfile = xstrdup(gitbom_buf); + FILE *f = fopen (tmpfile, FOPEN_WT); + if (f == NULL) + fatal (_("cannot open '%s': %s"), tmpfile, strerror (errno)); + fprintf(f, "gitoid:blob:sha%s\n", hash_alg == 2 ? "256" : "1"); + for (i=0; i 1 && strcmp(outhash, infile_hashes[0]) == 0) { + // this is creating the same empty thin archive from empty thin archives + fprintf(f, "ignore_this_record: information only"); + } + fprintf(f, "build_cmd:"); + for(i=0; i 0) ? argv + arg_index : NULL; + // record the list of input files for gitBOM build mode + gitbom_files = files; + gitbom_file_count = file_count; arch = open_inarch (inarch_filename, files == NULL ? (char *) NULL : files[0]); @@ -990,6 +1292,10 @@ open_inarch (const char *archive_filename, const char *file) bfd_fatal (archive_filename); #endif + // Set the flag for the inarchive file, which is used by gitBOM later + gitbom_inarch_exist = 0; + gitbom_printf("set the gitbom_inarch_exist flag to 0\n"); + if (!operation_alters_arch) { fprintf (stderr, "%s: ", program_name); @@ -1309,6 +1615,28 @@ write_archive (bfd *iarch) /* We don't care if this fails; we might be creating the archive. */ bfd_close (iarch); + // GITBOM extra work for gitBOM build mode + if (gitbom_hash_mode && (gitbom_create_adg_flag || gitbom_record_hash_flag)) { + gitbom_printf("inarch_exist: %d is_ranlib: %d write_armap: %d operation: %d\n", gitbom_inarch_exist, is_ranlib, write_armap, operation); + gitbom_printf("old_name: %s new_name: %s\n", old_name, new_name); + if (!gitbom_inarch_exist && gitbom_file_count > 0 && gitbom_files && (operation == replace || operation == quick_append)) { // this is create new archive + char *outfile_path = realpath(old_name, NULL); + char **infiles = (char **) xmalloc( gitbom_file_count * sizeof(char *) ); + for (int i=0; i/dev/null | %s | head --bytes=-4 > %s", + get_file_size(afile), afile, hash_alg == GITBOM_SHA256 ? "sha256sum" : "sha1sum", tmpfile); + if ( -1 == system(gitbom_buf) ) { + fatal("Failed to run the get file hash command\n"); + } + off_t size = get_file_size (tmpfile); + char *buffer = (char *) xmalloc (size + 2); + FILE *f = fopen (tmpfile, FOPEN_RT); + if (f == NULL) + fatal (_("cannot open '%s': %s"), tmpfile, strerror (errno)); + if (fread (buffer, 1, size, f) == 0 || ferror (f)) + fatal (_("%s: fread failed"), tmpfile); + fclose (f); + buffer [size] = '\0'; + remove(tmpfile); + return buffer; +} + +/* Get the symlink file path in the symlink farm */ +static char * +gitbom_get_symlink(char *ahash) +{ + sprintf(gitbom_buf, "%s/symlinks/%s", gitbom_adgdir, ahash); + return strdup(gitbom_buf); +} + +static bool +symlink_exists(const char* path) +{ + struct stat buf; + return ( lstat(path, &buf) == 0 ); +} + +static int strcmp_comparator(const void *p, const void *q) +{ + return strcmp(* (char * const *) p, * (char * const *) q); +} + +/* Create ADG doc for a list of infile hashes, for a specific hashing algorithm */ +static char * +gitbom_create_adg_doc(char **infile_hashes, int infile_num, int hash_alg) +{ + int i; + char ** deplines = (char **)xmalloc( (infile_num + 1) * sizeof(char *) ); + char *deplines_buf = (char *)xmalloc( infile_num * (GITBOM_MAX_DEPLINE_SIZE) + 1 ); + deplines[0] = deplines_buf; + for (i=0; i0; j--) { + if (gitbom_buf[j] == '/') { + bomptr = &gitbom_buf[j-2]; + break; + } + } + } + // now create the i-th line of dependency + if (bomptr) { + deplines[i+1] = deplines[i] + sprintf(deplines[i], "blob %s bom %c%c%s\n", inhash, bomptr[0], bomptr[1], &bomptr[3]); + deplines[i+1][-1] = 0; + } else { + deplines[i+1] = deplines[i] + sprintf(deplines[i], "blob %s\n", inhash); + deplines[i+1][-1] = 0; + } + } + // it is required to sort the array + if (infile_num > 1) { + qsort(deplines, infile_num, sizeof(char *), strcmp_comparator); + } + // first write to a temporary file to get the hash, then figure out the file name to save + sprintf(gitbom_buf, "/tmp/gitbom_objcopy_pid%d_adg", gitbom_pid); + char *tmpfile = strdup(gitbom_buf); + FILE *f = fopen (tmpfile, FOPEN_WT); + if (f == NULL) + fatal (_("cannot open '%s': %s"), tmpfile, strerror (errno)); + fprintf(f, "gitoid:blob:sha%s\n", hash_alg == 2 ? "256" : "1"); + for (i=0; itype) + { + case NT_GITBOM_SHA1: + { + unsigned long i; + + printf (_(" SHA1 GitOID: ")); + for (i = 0; i < pnote->descsz; ++i) + printf ("%02x", pnote->descdata[i] & 0xff); + printf ("\n"); + } + break; + + case NT_GITBOM_SHA256: + { + unsigned long i; + + printf (_(" SHA256 GitOID: ")); + for (i = 0; i < pnote->descsz; ++i) + printf ("%02x", pnote->descdata[i] & 0xff); + printf ("\n"); + } + break; + + default: + /* Handle unrecognised types. An error message should have already been + created by get_gitbom_elf_note_type(), so all that we need to do is to + display the data. */ + { + unsigned long i; + + printf (_(" Description data: ")); + for (i = 0; i < pnote->descsz; ++i) + printf ("%02x ", pnote->descdata[i] & 0xff); + printf ("\n"); + } + break; + } + + return true; +} + static bool print_gnu_note (Filedata * filedata, Elf_Internal_Note *pnote) { @@ -21540,6 +21606,10 @@ process_note (Elf_Internal_Note * pnote, else if (startswith (pnote->namedata, "stapsdt")) nt = get_stapsdt_note_type (pnote->type); + else if (startswith (pnote->namedata, "GITBOM")) + /* GNU-specific object file notes. */ + nt = get_gitbom_elf_note_type (pnote->type); + else /* Don't recognize this note name; just use the default set of note type strings. */ @@ -21580,6 +21650,8 @@ process_note (Elf_Internal_Note * pnote, else if (startswith (pnote->namedata, "AMDGPU") && pnote->type == NT_AMDGPU_METADATA) return print_amdgpu_note (pnote); + else if (startswith (pnote->namedata, "GITBOM")) + return print_gitbom_note (pnote); print_note_contents_hex (pnote); return true; diff --git a/binutils-2.39/include/elf/common.h b/binutils-2.39/include/elf/common.h index e4bc53e3..1f7f5d88 100644 --- a/binutils-2.39/include/elf/common.h +++ b/binutils-2.39/include/elf/common.h @@ -1006,6 +1006,11 @@ #define NT_FREEBSD_ABI_TAG 1 +/* Values for notes in non-core files using name "GITBOM". */ + +#define NT_GITBOM_SHA1 1 /* the hashing algorithm is SHA1. */ +#define NT_GITBOM_SHA256 2 /* the hashing algorithm is SHA256. */ + /* Values for FDO .note.package notes as defined on https://systemd.io/COREDUMP_PACKAGE_METADATA/ */ #define FDO_PACKAGING_METADATA 0xcafe1a7e