summaryrefslogtreecommitdiffstats
path: root/src/kLibTweaker/kLibTweaker.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kLibTweaker/kLibTweaker.c')
-rw-r--r--src/kLibTweaker/kLibTweaker.c775
1 files changed, 775 insertions, 0 deletions
diff --git a/src/kLibTweaker/kLibTweaker.c b/src/kLibTweaker/kLibTweaker.c
new file mode 100644
index 0000000..ecfce56
--- /dev/null
+++ b/src/kLibTweaker/kLibTweaker.c
@@ -0,0 +1,775 @@
+/* $Id: kLibTweaker.c 2791 2015-09-15 22:57:44Z bird $ */
+/** @file
+ * kLibTweaker - Import library tweaker for windows.
+ */
+
+/*
+ * Copyright (c) 2007-2015 knut st. osmundsen <bird-kBuild-spamx@anduin.net>
+ *
+ * This file is part of kBuild.
+ *
+ * kBuild is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * kBuild is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with kBuild. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#if 0
+# define ELECTRIC_HEAP
+# include "../kmk/electric.h"
+# include "../kmk/electric.c"
+#endif
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "k/kLdrFmts/pe.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Microsoft import library archive header.
+ *
+ * This has the same size as a COFF header, which is probably not a coincidence.
+ */
+typedef struct COFFIMPLIBHDR
+{
+ KU16 uSig1; /* 0 */
+ KU16 uSig2; /* 0xffff */
+ KU16 uVersion; /* 0 */
+ KU16 uMachine; /* IMAGE_FILE_MACHINE_I386, ... */
+ KU32 uTimeDateStamp;
+ KU32 cbData;
+ KU16 uOrdinalOrHint;
+ KU16 uFlags;
+} COFFIMPLIBHDR;
+
+/**
+ * COFF symbol.
+ *
+ * This one has an odd size and will cause misaligned accesses on platforms
+ * which cares about such.
+ */
+#pragma pack(1)
+typedef struct COFFSYMBOL
+{
+ union
+ {
+ char e_name[8];
+ struct
+ {
+ KU32 e_zeros; /**< Zero to distinguish it from ascii name. */
+ KU32 e_offset; /**< String table offset. */
+ } e;
+ } e;
+ KU32 e_value;
+ KI16 e_scnum;
+ KU16 e_type;
+ KU8 e_sclass;
+ KU8 e_numaux;
+} COFFSYMBOL;
+#pragma pack()
+
+/**
+ * Archive file header.
+ */
+typedef struct ARCHFILEHDR
+{
+ char achName[16];
+ char achModtime[12];
+ char achOwnerId[6];
+ char achGroupId[6];
+ char achMode[8];
+ char achSize[10];
+ char achMagic[2];
+} ARCHFILEHDR;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Whether verbose output is enabled. */
+static unsigned g_cVerbosityLevel = 0;
+/** What to prefix the errors with. */
+static char g_szErrorPrefix[128];
+
+
+void FatalMsg(const char *pszFormat, ...)
+{
+ va_list va;
+
+ if (g_szErrorPrefix[0])
+ fprintf(stderr, "%s - fatal error: ", g_szErrorPrefix);
+ else
+ fprintf(stderr, "fatal error: ");
+
+ va_start(va, pszFormat);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+}
+
+
+void FatalDie(const char *pszFormat, ...)
+{
+ va_list va;
+
+ if (g_szErrorPrefix[0])
+ fprintf(stderr, "%s - fatal error: ", g_szErrorPrefix);
+ else
+ fprintf(stderr, "fatal error: ");
+
+ va_start(va, pszFormat);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+
+ exit(1);
+}
+
+
+static int ErrorMsg(const char *pszFormat, ...)
+{
+ va_list va;
+
+ if (g_szErrorPrefix[0])
+ fprintf(stderr, "%s - error: ", g_szErrorPrefix);
+ else
+ fprintf(stderr, "error: ");
+
+ va_start(va, pszFormat);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+
+ return 1;
+}
+
+
+static void InfoMsg(unsigned uLevel, const char *pszFormat, ...)
+{
+ if (uLevel <= g_cVerbosityLevel)
+ {
+ va_list va;
+
+ if (g_szErrorPrefix[0])
+ fprintf(stderr, "%s - info: ", g_szErrorPrefix);
+ else
+ fprintf(stderr, "info: ");
+
+ va_start(va, pszFormat);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+ }
+}
+
+
+static void SetErrorPrefix(const char *pszPrefix, ...)
+{
+ int cch;
+ va_list va;
+
+ va_start(va, pszPrefix);
+#if defined(_MSC_VER) || defined(__sun__)
+ cch = vsprintf(g_szErrorPrefix, pszPrefix, va);
+ if (cch >= sizeof(g_szErrorPrefix))
+ FatalDie("Buffer overflow setting error prefix!\n");
+#else
+ vsnprintf(g_szErrorPrefix, sizeof(g_szErrorPrefix), pszPrefix, va);
+#endif
+ va_end(va);
+ (void)cch;
+}
+
+#ifndef ELECTRIC_HEAP
+void *xmalloc(size_t cb)
+{
+ void *pv = malloc(cb);
+ if (!pv)
+ FatalDie("out of memory (%d)\n", (int)cb);
+ return pv;
+}
+
+
+void *xrealloc(void *pvOld, size_t cb)
+{
+ void *pv = realloc(pvOld, cb);
+ if (!pv)
+ FatalDie("out of memory (%d)\n", (int)cb);
+ return pv;
+}
+
+
+char *xstrdup(const char *pszIn)
+{
+ char *psz;
+ if (pszIn)
+ {
+ psz = strdup(pszIn);
+ if (!psz)
+ FatalDie("out of memory (%d)\n", (int)strlen(pszIn));
+ }
+ else
+ psz = NULL;
+ return psz;
+}
+#endif
+
+
+void *xmallocz(size_t cb)
+{
+ void *pv = xmalloc(cb);
+ memset(pv, 0, cb);
+ return pv;
+}
+
+
+/**
+ * Adds the arguments found in the pszCmdLine string to argument vector.
+ *
+ * The parsing of the pszCmdLine string isn't very sophisticated, no
+ * escaping or quotes.
+ *
+ * @param pcArgs Pointer to the argument counter.
+ * @param ppapszArgs Pointer to the argument vector pointer.
+ * @param pszCmdLine The command line to parse and append.
+ * @param pszWedgeArg Argument to put infront of anything found in pszCmdLine.
+ */
+static void AppendArgs(int *pcArgs, char ***ppapszArgs, const char *pszCmdLine, const char *pszWedgeArg)
+{
+ int i;
+ int cExtraArgs;
+ const char *psz;
+ char **papszArgs;
+
+ /*
+ * Count the new arguments.
+ */
+ cExtraArgs = 0;
+ psz = pszCmdLine;
+ while (*psz)
+ {
+ while (isspace(*psz))
+ psz++;
+ if (!psz)
+ break;
+ cExtraArgs++;
+ while (!isspace(*psz) && *psz)
+ psz++;
+ }
+ if (!cExtraArgs)
+ return;
+
+ /*
+ * Allocate a new vector that can hold the arguments.
+ * (Reallocating might not work since the argv might not be allocated
+ * from the heap but off the stack or somewhere... )
+ */
+ i = *pcArgs;
+ *pcArgs = i + cExtraArgs + !!pszWedgeArg;
+ papszArgs = xmalloc((*pcArgs + 1) * sizeof(char *));
+ *ppapszArgs = memcpy(papszArgs, *ppapszArgs, i * sizeof(char *));
+
+ if (pszWedgeArg)
+ papszArgs[i++] = xstrdup(pszWedgeArg);
+
+ psz = pszCmdLine;
+ while (*psz)
+ {
+ size_t cch;
+ const char *pszEnd;
+ while (isspace(*psz))
+ psz++;
+ if (!psz)
+ break;
+ pszEnd = psz;
+ while (!isspace(*pszEnd) && *pszEnd)
+ pszEnd++;
+
+ cch = pszEnd - psz;
+ papszArgs[i] = xmalloc(cch + 1);
+ memcpy(papszArgs[i], psz, cch);
+ papszArgs[i][cch] = '\0';
+
+ i++;
+ psz = pszEnd;
+ }
+
+ papszArgs[i] = NULL;
+}
+
+
+
+
+static fpos_t kLibTweakerAsciiToSize(const char *pch, size_t cch)
+{
+ fpos_t cb = 0;
+
+ /* strip leading spaces. */
+ while (cch > 0 && (*pch == ' ' || *pch == '\t'))
+ cch--, pch++;
+
+ /* Convert decimal to binary. */
+ while (cch-- > 0)
+ {
+ char ch = *pch++;
+ if (ch >= '0' && ch <= '9')
+ {
+ cb *= 10;
+ cb += ch - '0';
+ }
+ else
+ break;
+ }
+
+ return cb;
+}
+
+
+static int kLibMyReadAt(FILE *pFile, void *pv, size_t cb, fpos_t off, int fEofOk)
+{
+ if (fsetpos(pFile, &off) == 0)
+ {
+ size_t cbActual = fread(pv, 1, cb, pFile);
+ if (cbActual == cb)
+ return 0;
+ if (!fEofOk || !feof(pFile))
+ ErrorMsg("fread returned %#lx, expected %#lx!\n", (unsigned long)cbActual, (unsigned long)cb);
+ }
+ else
+ ErrorMsg("seek error!\n");
+ return 1;
+}
+
+
+static int kLibMyWriteAt(FILE *pFile, const void *pv, size_t cb, fpos_t off)
+{
+ if (fsetpos(pFile, &off) == 0)
+ {
+ size_t cbActual = fwrite(pv, 1, cb, pFile);
+ if (cbActual == cb)
+ return 0;
+ ErrorMsg("fwrite returned %#lx, expected %#lx!\n", (unsigned long)cbActual, (unsigned long)cb);
+ }
+ else
+ ErrorMsg("seek error!\n");
+ return 1;
+}
+
+
+static int kLibFillNullThunkData(FILE *pFile, fpos_t cbFile, fpos_t offFileBytes)
+{
+ size_t off;
+ IMAGE_FILE_HEADER CoffHdr;
+ IMAGE_SECTION_HEADER SecHdr;
+ unsigned iSecHdr;
+ fpos_t offCur;
+ unsigned cbMachineWord;
+ KU32 cbStrTab;
+ COFFSYMBOL *paSymbols;
+ int rcRet = 0;
+
+ /*
+ * Read the COFF file header and filter out unlikly files based on
+ * section and symbol counts.
+ */
+ if (cbFile <= sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_SECTION_HEADER) * 2 + 4)
+ return 0;
+ if (kLibMyReadAt(pFile, &CoffHdr, sizeof(CoffHdr), offFileBytes, 0) != 0)
+ return 1;
+ if ( CoffHdr.Machine != IMAGE_FILE_MACHINE_I386
+ && CoffHdr.Machine != IMAGE_FILE_MACHINE_AMD64)
+ return 0;
+ cbMachineWord = CoffHdr.Machine == IMAGE_FILE_MACHINE_I386 ? 4 : 8;
+ if ( CoffHdr.NumberOfSections == 0
+ || CoffHdr.NumberOfSymbols == 0)
+ return 0;
+ off = sizeof(IMAGE_FILE_HEADER) + CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+ if ((fpos_t)off >= cbFile)
+ return 0;
+ if ( CoffHdr.PointerToSymbolTable >= (KU64)cbFile
+ || CoffHdr.PointerToSymbolTable < off)
+ return 0;
+
+ /*
+ * Search for the .idata$5 section which the thunk data is usually found in.
+ */
+ offCur = offFileBytes + sizeof(CoffHdr);
+ for (iSecHdr = 0; iSecHdr < CoffHdr.NumberOfSections; iSecHdr++)
+ {
+ if (kLibMyReadAt(pFile, &SecHdr, sizeof(SecHdr), offCur, 0) != 0)
+ return 1;
+ InfoMsg(2, "#2: %.8s VirtualSize=%#lx\n", SecHdr.Name, SecHdr.SizeOfRawData);
+ if ( !memcmp(SecHdr.Name, ".idata$5", 8)
+ && SecHdr.SizeOfRawData == cbMachineWord)
+ break;
+ offCur += sizeof(SecHdr);
+ }
+ if (iSecHdr == CoffHdr.NumberOfSections)
+ return 0;
+
+ /*
+ * Read in the symbo and string tables.
+ */
+ off = CoffHdr.PointerToSymbolTable + CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL);
+ if (kLibMyReadAt(pFile, &cbStrTab, sizeof(cbStrTab), offFileBytes + off, 0) != 0)
+ return 1;
+ InfoMsg(2, "#2: Found COFF file header, cbStrTab=%#x; off=%#lx NumberOfSymbols=%#x PointerToSymbolTable=%#x\n",
+ cbStrTab, (long)off, CoffHdr.NumberOfSymbols, CoffHdr.PointerToSymbolTable);
+ if ( cbStrTab <= 4U
+ || cbStrTab >= 16*1024*1024U /* 16MB */
+ || (fpos_t)off + cbStrTab > cbFile)
+ return 0;
+ paSymbols = xmalloc(CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL) + cbStrTab + 1);
+ if (kLibMyReadAt(pFile, paSymbols, CoffHdr.NumberOfSymbols * sizeof(COFFSYMBOL) + cbStrTab,
+ offFileBytes + CoffHdr.PointerToSymbolTable, 0) == 0)
+ {
+ char *pchStrTab = (char *)&paSymbols[CoffHdr.NumberOfSymbols];
+ unsigned iSym;
+
+ pchStrTab[cbStrTab] = '\0';
+ pchStrTab[0] = '\0';
+ pchStrTab[1] = '\0';
+ pchStrTab[2] = '\0';
+ pchStrTab[3] = '\0';
+
+ for (iSym = 0; iSym < CoffHdr.NumberOfSymbols; iSym++)
+ {
+ static char const s_szSuffix[] = "NULL_THUNK_DATA";
+ const char *pchName;
+ size_t cchName;
+ if (paSymbols[iSym].e.e.e_zeros != 0)
+ {
+ pchName = &paSymbols[iSym].e.e_name[0];
+ cchName = (char *)memchr(pchName, '\0', sizeof(paSymbols[iSym].e.e_name)) - pchName;
+ if (cchName > sizeof(paSymbols[iSym].e.e_name))
+ cchName = sizeof(paSymbols[iSym].e.e_name);
+ }
+ else if ( paSymbols[iSym].e.e.e_offset == 0
+ || paSymbols[iSym].e.e.e_offset >= cbStrTab)
+ continue;
+ else
+ {
+ pchName = &pchStrTab[paSymbols[iSym].e.e.e_offset];
+ cchName = strlen(pchName);
+ }
+
+ if ( *pchName == 0x7f
+ && cchName >= sizeof(s_szSuffix)
+ && memcmp(&pchName[cchName - sizeof(s_szSuffix) + 1], s_szSuffix, sizeof(s_szSuffix) - 1) == 0)
+ {
+ if (pchName[cchName] == '\0')
+ InfoMsg(1, "#2: Found '%s': value=%#lx\n", pchName, paSymbols[iSym].e_value);
+ else
+ InfoMsg(1, "#2: Found '%.8s': value=%#lx\n", pchName, paSymbols[iSym].e_value);
+ if ( paSymbols[iSym].e_scnum > 0
+ && paSymbols[iSym].e_scnum <= CoffHdr.NumberOfSections)
+ {
+ if (paSymbols[iSym].e_scnum != iSecHdr + 1)
+ InfoMsg(0, "#2: '%s' in section %u, expected %u\n", pchName, paSymbols[iSym].e_scnum, iSecHdr);
+ else if (paSymbols[iSym].e_value != 0)
+ InfoMsg(0, "#2: '%s' in value %#xu, expected 0x0\n", pchName, paSymbols[iSym].e_value);
+ else if ( SecHdr.PointerToRawData < sizeof(CoffHdr) + CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
+ || (fpos_t)SecHdr.PointerToRawData + cbMachineWord > cbFile)
+ InfoMsg(0, "#2: Unexpected PointerToRawData value: %#x\n", SecHdr.PointerToRawData);
+ else
+ {
+ union
+ {
+ KU8 ab[8];
+ KU32 u32;
+ KU64 u64;
+ } uBuf;
+ uBuf.u64 = 0;
+ off = offFileBytes + SecHdr.PointerToRawData;
+ if (kLibMyReadAt(pFile, &uBuf, cbMachineWord, off, 0) == 0)
+ {
+ static const KU8 s_abGarbage[8] = { 0xaa, 0x99, 0x88, 0xbb, 0xbb, 0xaa, 0x88, 0x99 };
+ static const KU8 s_abZero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+ if (memcmp(&uBuf, s_abZero,cbMachineWord) == 0)
+ {
+ rcRet = kLibMyWriteAt(pFile, s_abGarbage, cbMachineWord, off);
+ if (!rcRet)
+ InfoMsg(1, "#2: Updated '%s'\n", pchName);
+ }
+ else if (memcmp(&uBuf, s_abGarbage, cbMachineWord) == 0)
+ {
+ InfoMsg(1, "#2: Already modified '%s'\n", pchName);
+ rcRet = 0;
+ }
+ else
+ rcRet = ErrorMsg(0, "#2: Unexpected '%s' data: %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ pchName,
+ uBuf.ab[0], uBuf.ab[1], uBuf.ab[2], uBuf.ab[3],
+ uBuf.ab[4], uBuf.ab[5], uBuf.ab[6], uBuf.ab[7]);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ }
+ else
+ rcRet = 1;
+ free(paSymbols);
+ return rcRet;
+}
+
+
+/**
+ * Clears timestamps to avoid rebuilding stuff just because the internal
+ * timestamps changed in an import library.
+ */
+static int kLibClearTimestamps(FILE *pFile, fpos_t offFileHdr, ARCHFILEHDR *pFileHdr, fpos_t cbFile, fpos_t offFileBytes)
+{
+ union
+ {
+ IMAGE_FILE_HEADER CoffHdr;
+ COFFIMPLIBHDR ImpLibHdr;
+ } u;
+ if (sizeof(u.CoffHdr) != sizeof(u.ImpLibHdr))
+ FatalDie("Oops!");
+
+ /*
+ * Clear the timestamp in the library file header.
+ */
+ memset(pFileHdr->achModtime, '0', sizeof(pFileHdr->achModtime));
+ if (kLibMyWriteAt(pFile, pFileHdr, sizeof(*pFileHdr), offFileHdr) != 0)
+ return 1;
+
+ /*
+ * Clear the timestamp in the COFF header, if we find one.
+ */
+ if (cbFile <= sizeof(IMAGE_FILE_HEADER))
+ return 0;
+ if (kLibMyReadAt(pFile, &u.CoffHdr, sizeof(u.CoffHdr), offFileBytes, 0) != 0)
+ return 1;
+
+ if ( ( u.CoffHdr.Machine == IMAGE_FILE_MACHINE_I386
+ || u.CoffHdr.Machine == IMAGE_FILE_MACHINE_AMD64)
+ && sizeof(IMAGE_FILE_HEADER)
+ + u.CoffHdr.NumberOfSections * sizeof(IMAGE_SECTION_HEADER)
+ <= (KU64)cbFile
+ && u.CoffHdr.PointerToSymbolTable <= (KU64)cbFile)
+ {
+ InfoMsg(1, "Found COFF file header\n");
+ if (u.CoffHdr.TimeDateStamp != 0)
+ {
+ u.CoffHdr.TimeDateStamp = 0;
+ return kLibMyWriteAt(pFile, &u.CoffHdr, sizeof(u.CoffHdr), offFileBytes);
+ }
+ }
+ else if ( u.ImpLibHdr.uSig1 == 0
+ && u.ImpLibHdr.uSig2 == 0xffff
+ && u.ImpLibHdr.uVersion == 0
+ && ( u.ImpLibHdr.uMachine == IMAGE_FILE_MACHINE_I386
+ || u.ImpLibHdr.uMachine == IMAGE_FILE_MACHINE_AMD64)
+ && u.ImpLibHdr.cbData <= cbFile)
+ {
+ InfoMsg(1, "Found COFF import library header\n");
+ if (u.ImpLibHdr.uTimeDateStamp)
+ {
+ u.ImpLibHdr.uTimeDateStamp = 0;
+ return kLibMyWriteAt(pFile, &u.ImpLibHdr, sizeof(u.ImpLibHdr), offFileBytes);
+ }
+ }
+ else
+ InfoMsg(1, "CoffHdr.Machine=%#x ImpLibHdr.Machine=%#x\n", u.CoffHdr.Machine, u.ImpLibHdr.uMachine);
+
+ return 0;
+}
+
+
+static int kLibTweakerDoIt(const char *pszLib, int fClearTimestamps, int fFillNullThunkData)
+{
+ int rcRet = 0;
+ FILE *pFile = fopen(pszLib, "r+b");
+ if (pFile)
+ {
+ /*
+ * Read the header.
+ */
+ static char s_szMagic[] = "!<arch>\n";
+ union
+ {
+ char ab[1024];
+ IMAGE_FILE_HEADER CoffHdr;
+ } uBuf;
+ if ( fread(uBuf.ab, 1, sizeof(s_szMagic) - 1, pFile) == sizeof(s_szMagic) - 1
+ && memcmp(uBuf.ab, s_szMagic, sizeof(s_szMagic) - 1) == 0)
+ {
+ fpos_t offFileHdr = sizeof(s_szMagic) - 1;
+ while (!feof(pFile))
+ {
+ ARCHFILEHDR FileHdr;
+ if (kLibMyReadAt(pFile, &FileHdr, sizeof(FileHdr), offFileHdr, 1) != 0)
+ {
+ if (feof(pFile))
+ break;
+ rcRet = ErrorMsg("failed reading the file header (offset %ld)\n", (long)offFileHdr);
+ break;
+ }
+ if ( FileHdr.achMagic[0] == 0x60
+ && FileHdr.achMagic[1] == 0x0a)
+ {
+ fpos_t const offFileBytes = offFileHdr + sizeof(FileHdr);
+
+ /*
+ * Convert the size from decimal to binary as we need it to skip to
+ * the next file header.
+ */
+ fpos_t const cb = kLibTweakerAsciiToSize(FileHdr.achSize, sizeof(FileHdr.achSize));
+ InfoMsg(1, "Found header at %#lx: cbFile=%#lx, bytes at %#lx\n",
+ (unsigned long)offFileHdr, (unsigned long)cb, (unsigned long)offFileBytes);
+
+ /*
+ * Make the requested changes.
+ */
+ if (fClearTimestamps)
+ rcRet |= kLibClearTimestamps(pFile, offFileHdr, &FileHdr, cb, offFileBytes);
+
+ if (fFillNullThunkData)
+ rcRet |= kLibFillNullThunkData(pFile, cb, offFileBytes);
+
+ /*
+ * Skip to the next header.
+ */
+ offFileHdr = offFileBytes + ((cb + 1) & ~(fpos_t)1);
+ }
+ else
+ rcRet = ErrorMsg("invalid file header magic (offset %ld)\n", (long)offFileHdr);
+ }
+ }
+ else
+ rcRet = ErrorMsg("Didn't find '!<arch>\\n' magic in '%s' (or read error)\n", pszLib);
+
+ if (fclose(pFile) != 0)
+ rcRet = ErrorMsg("Error closing '%s'\n");
+ }
+ else
+ rcRet = ErrorMsg("Failed to open '%s' for read+write\n", pszLib);
+ return rcRet;
+}
+
+
+/**
+ * Prints a syntax error and returns the appropriate exit code
+ *
+ * @returns approriate exit code.
+ * @param pszFormat The syntax error message.
+ * @param ... Message args.
+ */
+static int SyntaxError(const char *pszFormat, ...)
+{
+ va_list va;
+ fprintf(stderr, "kObjCache: syntax error: ");
+ va_start(va, pszFormat);
+ vfprintf(stderr, pszFormat, va);
+ va_end(va);
+ return 1;
+}
+
+
+/**
+ * Prints the usage.
+ * @returns 0.
+ */
+static int usage(FILE *pOut)
+{
+ fprintf(pOut,
+ "syntax: kLibTweaker [-v|--verbose] [--clear-timestamps] <lib>\n"
+ "\n");
+ return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+ char *psz;
+ int i;
+
+ int fClearTimestamps = 0;
+ int fFillNullThunkData = 0;
+ const char *pszLib = NULL;
+
+ SetErrorPrefix("kLibTweaker");
+
+ /*
+ * Arguments passed in the environmnet?
+ */
+ psz = getenv("KLIBTWEAKER_OPTS");
+ if (psz)
+ AppendArgs(&argc, &argv, psz, NULL);
+
+/** @todo Add the capability to produce import/stub libraries from ELF shared
+ * objects that we can use while linking and break up linking dependencies
+ * (i.e. not relink everything just because something in VBoxRT change that
+ * didn't make any difference to the symbols it exports). */
+
+ /*
+ * Parse the arguments.
+ */
+ if (argc <= 1)
+ return usage(stderr) + 1;
+ for (i = 1; i < argc; i++)
+ {
+ if (!strcmp(argv[i], "--clear-timestamps"))
+ fClearTimestamps = 1;
+ else if (!strcmp(argv[i], "--fill-null_thunk_data"))
+ fFillNullThunkData = 1;
+ /* Standard stuff: */
+ else if (!strcmp(argv[i], "--help"))
+ return usage(stderr);
+ else if (!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose"))
+ g_cVerbosityLevel++;
+ else if (!strcmp(argv[i], "-q") || !strcmp(argv[i], "--quiet"))
+ g_cVerbosityLevel = 0;
+ else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "-?")
+ || !strcmp(argv[i], "/h") || !strcmp(argv[i], "/?") || !strcmp(argv[i], "/help"))
+ return usage(stdout);
+ else if (!strcmp(argv[i], "-V") || !strcmp(argv[i], "--version"))
+ {
+ printf("kLibTweaker - kBuild version %d.%d.%d ($Revision: 2791 $)\n"
+ "Copyright (c) 2007-2015 knut st. osmundsen\n",
+ KBUILD_VERSION_MAJOR, KBUILD_VERSION_MINOR, KBUILD_VERSION_PATCH);
+ return 0;
+ }
+ else if (!strcmp(argv[i], "--"))
+ {
+ i++;
+ if (i == argc)
+ return SyntaxError("No library given!\n");
+ if (i + 1 != argc || pszLib)
+ return SyntaxError("Only one library can be tweaked at a time!\n");
+ pszLib = argv[i];
+ break;
+ }
+ else if (argv[i][0] == '-')
+ return SyntaxError("Doesn't grok '%s'!\n", argv[i]);
+ else if (!pszLib)
+ pszLib = argv[i];
+ else
+ return SyntaxError("Only one library can be tweaked at a time!\n");
+ }
+ if (!pszLib)
+ return SyntaxError("No library given!\n");
+
+ return kLibTweakerDoIt(pszLib, fClearTimestamps, fFillNullThunkData);
+}
+