1

I am learning how to create libraries with just a terminal. Is it possible (and I suspect it might not be) to place resources, such as images, text, API-keys, etc. into a statically linked archive file, like in an NSBundle? How do I reference these resources?

4
  • This is not a standard linker function and there is no standard method to do it. There are, however, quite some linkers that support embedment of static resources, even into libraries. As you have not specified OS and development environment, it is not possible to answer your question. Commented Mar 19, 2016 at 8:45
  • @tofro Any OS i'll be using will be POSIX, but I was hoping for something portable if possible. Are there any linkers you could recommend? Commented Mar 19, 2016 at 8:47
  • If you are looking for something really portable: Write your own converter that creates C code from your binary data like const unsigned char blob[] = {0x0,022,.....} . Then compile and link. shouldn't be too hard to do. Everything else is non-portable. Commented Mar 19, 2016 at 9:30
  • @tofro I was thinking about that but I wanted to make sure there wasn't a better way. I'd feel silly if I started doing that and it turns out I'm supposed to just be including the file, you know? Thanks for your input. Commented Mar 19, 2016 at 9:31

2 Answers 2

1

On POSIX systems, you have a pretty much standard shell sh, four very useful shell utilities called rm, printf, od, and sed.

Let's say you wish to create file foo.c with a const unsigned char blob[] array containing the contents of binary file foo.bar:

export LANG=C LC_ALL=C
rm -f foo.c
printf 'const unsigned char blob[] = {\n' > foo.c
od -A n -t x1 foo.bar | sed -e 's| *\([0-9A-Fa-f][0-9A-Fa-f]\)| 0x\1U,|g; s|^|   |' >> foo.c
printf '};\n' >> foo.c

The first line sets the locale to C. This ensures the utilities use the format we expect, instead of some localized variant.

The second line removes the possibly existing foo.c. (Some shells complain if you direct output to an existing file without appending to it.) The -f quiets any complaints, if the file does not even exist yet.

The third and fifth lines append the array definition and closing to the foo.c file.

The fourth line uses the od utility to output the contents of foo.bar as hexadecimal bytes. We use sed to prepend 0x and append U, to each hexadecimal byte to make it nice and pretty C, and then we prepend an additional three spaces to the line (making it indented by four spaces).

If you don't like the approach, you can always write your own utility to do exactly this. For example: #include #include #include #include

#ifndef  DEFAULT_VARNAME
#define  DEFAULT_VARNAME "blob"
#endif

#ifndef  DEFAULT_STORAGE
#define  DEFAULT_STORAGE "const"
#endif

#ifndef  DEFAULT_ATTRIBUTES
#define  DEFAULT_ATTRIBUTES ""
#endif

#ifndef  DEFAULT_INDENT
#define  DEFAULT_INDENT "    "
#endif

#ifndef  DEFAULT_COLUMNS
#define  DEFAULT_COLUMNS 16
#endif

int usage(const char *argv0)
{
    fprintf(stderr, "\n");
    fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv0);
    fprintf(stderr, "       %s [ OPTIONS ] file [ [ OPTIONS ] file .. ]\n", argv0);
    fprintf(stderr, "\n");
    fprintf(stderr, "Options:\n");
    fprintf(stderr, "       -n %-10s Variable name\n", "'" DEFAULT_VARNAME "'");
    fprintf(stderr, "       -s %-10s Storage type\n", "'" DEFAULT_STORAGE "'");
    fprintf(stderr, "       -a %-10s Attributes\n", "'" DEFAULT_ATTRIBUTES "'");
    fprintf(stderr, "       -i %-10s Indentation\n", "'" DEFAULT_INDENT "'");
    fprintf(stderr, "       -c %-10d Bytes per line\n", (int)DEFAULT_COLUMNS);
    fprintf(stderr, "\n");
    fprintf(stderr, "This program will output the contents of the specified file(s)\n");
    fprintf(stderr, "as C source code.\n");
    fprintf(stderr, "\n");
    return EXIT_SUCCESS;
}


int main(int argc, char *argv[])
{
    const char *storage = DEFAULT_STORAGE;
    const char *varname = DEFAULT_VARNAME;
    const char *attributes = DEFAULT_ATTRIBUTES;
    const char *indent = DEFAULT_INDENT;
    size_t      columns = DEFAULT_COLUMNS;

    FILE   *in;
    size_t  bytes;
    int     ch, arg;
    char    dummy;

    if (argc < 2)
        return usage(argv[0]);

    arg = 1;
    while (arg < argc) {

        if (argv[arg][0] == '-') {
            if (argv[arg][1] == 'n') {
                if (argv[arg][2] != '\0') {
                    varname = argv[arg] + 2;
                    arg++;
                    continue;
                } else
                if (arg + 1 < argc) {
                    varname = argv[arg + 1];
                    arg += 2;
                    continue;
                }
            } else
            if (argv[arg][1] == 's') {
                if (argv[arg][2] != '\0') {
                    storage = argv[arg] + 2;
                    arg++;
                    continue;
                } else
                if (arg + 1 < argc) {
                    storage = argv[arg + 1];
                    arg += 2;
                    continue;
                }
            } else
            if (argv[arg][1] == 'a') {
                if (argv[arg][2] != '\0') {
                    attributes = argv[arg] + 2;
                    arg++;
                    continue;
                } else
                if (arg + 1 < argc) {
                    attributes = argv[arg + 1];
                    arg += 2;
                    continue;
                }
            } else
            if (argv[arg][1] == 'i') {
                if (argv[arg][2] != '\0') {
                    indent = argv[arg] + 2;
                    arg++;
                    continue;
                } else
                if (arg + 1 < argc) {
                    indent = argv[arg + 1];
                    arg += 2;
                    continue;
                }
            } else
            if (argv[arg][1] == 'c') {
                if (argv[arg][2] != '\0') {
                    if (sscanf(argv[arg] + 2, " %zu %c", &columns, &dummy) != 1 || columns < 1) {
                        fprintf(stderr, "%s: Invalid number of bytes per line.\n", argv[arg] + 2);
                        return EXIT_FAILURE;
                    }
                    arg++;
                    continue;
                } else
                if (arg + 1 < argc) {
                    if (sscanf(argv[arg+1], " %zu %c", &columns, &dummy) != 1 || columns < 1) {
                        fprintf(stderr, "%s: Invalid number of bytes per line.\n", argv[arg + 1]);
                        return EXIT_FAILURE;
                    }
                    arg += 2;
                    continue;
                }
            } else
            if (!strcmp(argv[arg], "-h") || !strcmp(argv[arg], "--help"))
                return usage(argv[0]);
        }

        in = fopen(argv[arg], "r");
        if (!in) {
            fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
            return EXIT_FAILURE;
        }

        printf("%s unsigned char %s[] %s= {", storage, varname, attributes);

        bytes = 0;
        while ((ch = getc(in)) != EOF)
            if (bytes++ % columns)
                printf(", %3u", (unsigned int)ch);
            else
                printf("\n%s%3u", indent, (unsigned int)ch);

        printf("\n}; /* %zu bytes */\n\n", bytes);

        if (ferror(in)) {
            fclose(in);
            fprintf(stderr, "%s: Read error.\n", argv[arg]);
            return EXIT_FAILURE;
        }
        if (fclose(in)) {
            fprintf(stderr, "%s: Delayed read error.\n", argv[arg]);
            return EXIT_FAILURE;
        }

        if (fflush(stdout) || ferror(stdout)) {
            fprintf(stderr, "Error writing to standard output.\n");
            return EXIT_FAILURE;
        }

        arg++;
    }

    return EXIT_SUCCESS;
}

The only POSIX-ism in the above code is the use of %zu to scan and print a size_t.

Sign up to request clarification or add additional context in comments.

Comments

0

What you want to do is creating an object file from a binary blob in order to be able to link to it. It needs to be an object file that contains a symbol table with at least one symbol (probably the start of your blob), because you want to have a handle to your binary data in the rest of the program.

Because object files are entirely system- (sometimes even compiler-) dependent, there is no portable way to actually do this directly. Linux, for example, used to have at least two different object file formats for quite some time.

The most portable (and, maybe, even simplest) way to do that: Use a tool that knows how to build object files for your dev environment: The C compiler.

Write your own converter that creates C code from your binary data like

const unsigned char blob[] = {0x0,0x22,.....};

Then compile and link. shouldn't be too hard to do, rather a 10-mins job. Everything else is entirely non-portable.

On platforms where you have GNU objcopy available, this might be another way of doing things. (Specify 'binary' as input file format and your object file format as output target. You also need to use "--add-symbol" to add that start symbol of your blob.) But I have never done this myself.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.