Skip to content

Commit

Permalink
Support for embedding images in unix executables. See deploy/packagin…
Browse files Browse the repository at this point in the history
…g/README.unix

for instructions.
  • Loading branch information
eliotmiranda committed May 16, 2024
1 parent cc08215 commit be70093
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 24 deletions.
29 changes: 29 additions & 0 deletions deploy/packaging/README.unix
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
The Unix VMs can be packaged to embed the image file in the executable. In this
configuration a VM executable will only accept the image it includes and cannot
be used to load other images. An embedded image is added to a VM executable using
the genUnixImageResource.c program in this directory, hacking a VM build makefile,
and then building the VM.

First compile genUnixImageResource.c, e.g.

cc -o genUnixImageResource -O3 genUnixImageResource.c

then use it and the C compiler to generate an image.o file, e.g.

./genUnixImageResource myimagefile.image | gcc -c -o image.o -pipe -x c -

Note that in the above we use the -pipe option to avoid creating temporary files
as these are huge. The output of genUnixImageResource is five times larger than
the image file, and the assembler file generated may be even larger. Even using
pipes creating an image.o file from a 48Mb image file on a Raspberry Pi 4 takes
nearly 9 minutes.

Copy/link the image.o file to a VM build directory, e.g. into
building/linux64ARMv8/squeak.cog.spur/build. Edit the line that defines SQLIBS
to either include image.o or a variable you can define on the make line:

SQLIBS = vm/vm$a $(EIO) ...

Then remove any existing squeak executable and make:

$ rm -f ./squeak; make EIO=image.o
61 changes: 61 additions & 0 deletions deploy/packaging/genUnixImageResource.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Take as input an image file. Produce as output a C file defining
// a byte array called embeddedImage which contains the contents of
// the image file, and a string called embeddedImageName containing
// the base name of the image.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define NITEMS (80/5)
int
main(int argc, char *argv[])
{
FILE *ifh, *ofh;
unsigned char data[NITEMS];
char *basename, *dot;
size_t n;

if (argc == 2 || argc == 3) {
if (!(ifh = fopen(argv[1], "rb"))) {
perror("fopen");
return 1;
}
if (argc == 3) {
if (!(ofh = fopen(argv[2], "w"))) {
perror("fopen");
return 2;
}
}
else
ofh = stdout;
}
else {
printf("usage: %s file.image [output file]\n", argv[0]);
exit(2);
}
if ((basename = strrchr(argv[1],'/')))
basename = basename + 1;
else
basename = argv[1];
if (!(dot = strrchr(basename,'.')))
dot = basename + strlen(basename) - 1;
fprintf(ofh,
"char embeddedImageName[] = \"%.*s\";\n",
dot - basename, basename);
fseek(ifh,0,SEEK_END);
fprintf(ofh,"unsigned long embeddedImageSize = %ld;\n",ftell(ifh));
fseek(ifh,0,SEEK_SET);
fprintf(ofh, "unsigned char embeddedImage[] = {\n");
while ((n = fread(data,sizeof(unsigned char),NITEMS,ifh)) > 0) {
unsigned char lastchar = feof(ifh) ? '\n' : ',';
for (int i = 0; i < n; i++)
fprintf(ofh,"0x%02x%c", data[i], i < (n - 1) ? ',' : (feof(ifh) ? '\n' : ','));
if (!feof(ifh))
fprintf(ofh,"\n");
}
fprintf(ofh,"};\n");
(void)fclose(ifh);
(void)fclose(ofh);
return 0;
}
89 changes: 75 additions & 14 deletions platforms/unix/vm/sqImageFileAccess.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@
#include <unistd.h>
#include <errno.h>

// On Unix we use the native file interface.
// On Unix we use the native file interface. There is also support for embedded images

#define sqImageFile int
#define invalidSqImageFile(sif) ((sif) < 0)
#define squeakFileOffsetType off_t
#define ImageIsEmbedded ((sqImageFile)1)

// Save/restore.

Expand All @@ -28,9 +29,29 @@ extern sqInt checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize);
// Read the image from the given file starting at the given image offset
size_t readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset);


// Image I/O API

sqImageFile sqImageFileOpen(const char *fileName, const char *mode);
void sqImageFileClose(sqImageFile f);
int sqImageFileIsEmbedded(void);
size_t sqImageFileRead(void *ptr_arg, long sz, long count, sqImageFile f);
// sqImageFileWrite answers the number of items written, not number of bytes
// size_t is for sizes. ssize_t is signed, for sizes + the -1 error flag
size_t sqImageFileWrite(void *ptr_arg, size_t sz, size_t count, sqImageFile f);
off_t sqImageFilePosition(sqImageFile f);
void sqImageFileSeek(sqImageFile f,off_t pos);
void sqImageFileSeekEnd(sqImageFile f,off_t pos);

#define sqImageFileStartLocation(f,fileName,sz) 0

// Image I/O API Implementation

#if INCLUDE_SIF_CODE

static int sIFOMode;

static inline sqImageFile
sqImageFile
sqImageFileOpen(const char *fileName, const char *mode)
{
int fd = open(fileName,
Expand All @@ -43,11 +64,14 @@ sqImageFileOpen(const char *fileName, const char *mode)
return fd;
}

static inline void
void
sqImageFileClose(sqImageFile f)
{
extern sqInt failed(void);

if (f == ImageIsEmbedded)
return;

if (!failed()
&& sIFOMode == O_RDWR+O_CREAT
&& ftruncate(f,lseek(f, 0, SEEK_CUR)) < 0)
Expand All @@ -57,19 +81,51 @@ extern sqInt failed(void);
perror("sqImageFileClose close");
}

// Support for image embedded as a resource
static unsigned char *eiData = NULL;
static unsigned long eiSize = 0;
static off_t eiReadPosition = 0;

static inline void
noteEmbeddedImage(unsigned char *data, unsigned long size)
{
eiData = data;
eiSize = size;
}

int
sqImageFileIsEmbedded(void) { return eiData != NULL; }

static inline size_t
sqEmbeddedImageRead(void *ptr, size_t sz, size_t count)
{
if (eiReadPosition + (sz * count) > eiSize) {
fprintf(stderr,"Attempting to read beyond end of embedded image\n");
return 0;
}

memcpy(ptr,eiData + eiReadPosition, sz * count);
eiReadPosition += sz * count;
return count;
}


#if !defined(min)
# define min(a,b) ((a)<=(b)?(a):b)
#endif
#define OneGb 0x40000000 // hah,hah,hah,hah https://y2u.be/watch?v=EJR1H5tf5wE

// sqImageFileRead answers the number of items read, not number of bytes
// size_t is for sizes. ssize_t is signed, for sizes + the -1 error flag
static inline size_t
size_t
sqImageFileRead(void *ptr_arg, long sz, long count, sqImageFile f)
{
size_t to_be_read = sz * count, nread_in_total = 0;
unsigned char *ptr = ptr_arg;

if (f == ImageIsEmbedded)
return sqEmbeddedImageRead(ptr,sz,count);

/* read may refuse to write more than 2Gb-1. At least on MacOS 10.13.6,
* read craps out above 2Gb, so chunk the read into to 1Gb segments.
*/
Expand All @@ -91,7 +147,7 @@ sqImageFileRead(void *ptr_arg, long sz, long count, sqImageFile f)

// sqImageFileWrite answers the number of items written, not number of bytes
// size_t is for sizes. ssize_t is signed, for sizes + the -1 error flag
static inline size_t
size_t
sqImageFileWrite(void *ptr_arg, size_t sz, size_t count, sqImageFile f)
{
size_t to_be_written = sz * count, nwritten_in_total = 0;
Expand All @@ -117,30 +173,35 @@ sqImageFileWrite(void *ptr_arg, size_t sz, size_t count, sqImageFile f)
}
#undef OneGb

static inline off_t
off_t
sqImageFilePosition(sqImageFile f)
{
if (f == ImageIsEmbedded)
return eiReadPosition;

off_t pos = lseek(f, 0, SEEK_CUR);
if (pos == (off_t)-1)
perror("sqImageFilePosition lseek");
return pos;
}

static inline void
void
sqImageFileSeek(sqImageFile f,off_t pos)
{
if (lseek(f, pos, SEEK_SET) < 0)
if (f == ImageIsEmbedded)
eiReadPosition = pos;
else if (lseek(f, pos, SEEK_SET) < 0)
perror("sqImageFileSeek lseek");
}

static inline void
void
sqImageFileSeekEnd(sqImageFile f,off_t pos)
{
if (lseek(f, pos, SEEK_END) < 0)
if (f == ImageIsEmbedded)
eiReadPosition = eiSize;
else if (lseek(f, pos, SEEK_END) < 0)
perror("sqImageFileSeekEnd lseek");
}

#define sqImageFileStartLocation(f,fileName,sz) 0

#define sqImageFileIsEmbedded() 0
#endif /* _SQ_IMAGE_FILE_ACCESS_H */
#endif // INCLUDE_SIF_CODE
#endif // _SQ_IMAGE_FILE_ACCESS_H
40 changes: 30 additions & 10 deletions platforms/unix/vm/sqUnixMain.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "sq.h"
#include "sqAssert.h"
#include "sqMemoryAccess.h"
#define INCLUDE_SIF_CODE 1
#include "sqImageFileAccess.h"
#include "sqaio.h"
#include "sqUnixCharConv.h"
Expand All @@ -58,6 +59,7 @@
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <dlfcn.h>
#if !defined(NOEXECINFO) && defined(HAVE_EXECINFO_H)
# include <execinfo.h>
# define BACKTRACE_DEPTH 64
Expand Down Expand Up @@ -2151,28 +2153,46 @@ imgInit(void)
int fd;
struct stat sb;
char imagePath[MAXPATHLEN];
sq2uxPath(shortImageName, strlen(shortImageName), imagePath, MAXPATHLEN - 1, 1);
if (-1 == stat(imagePath, &sb) || (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))) {
imageNotFound(imagePath); // imageNotFound will exit
}
fd = sqImageFileOpen(imagePath, "rb"); // sqImageFileOpen handles the errors. fd is valid here

// first check for an embedded image

void *handle = dlopen(NULL, RTLD_NOW);
void *embeddedImage;
if (handle && (embeddedImage = dlsym(handle,"embeddedImage"))) {
strcpy(shortImageName,dlsym(handle,"embeddedImageName"));
noteEmbeddedImage(embeddedImage,
*(unsigned long *)dlsym(handle,"embeddedImageSize"));
dlclose(handle);
fd = ImageIsEmbedded;
}
else {

sq2uxPath(shortImageName, strlen(shortImageName), imagePath, MAXPATHLEN - 1, 1);
if (-1 == stat(imagePath, &sb) || (!S_ISREG(sb.st_mode) && !S_ISLNK(sb.st_mode))) {
imageNotFound(imagePath); // imageNotFound will exit
}
fd = sqImageFileOpen(imagePath, "rb"); // sqImageFileOpen handles the errors. fd is valid here
#ifdef DEBUG_IMAGE
printf("fstat(%d) => %d\n", fd, fstat(fd, &sb));
printf("fstat(%d) => %d\n", fd, fstat(fd, &sb));
#endif

recordFullPathForImageName(shortImageName); /* full image path */
recordFullPathForImageName(shortImageName); /* full image path */
}

if (extraMemory)
useMmap= 0;
else
extraMemory= DefaultHeapSize * 1024 * 1024;
#ifdef DEBUG_IMAGE
printf("image size %ld + heap size %ld (useMmap = %d)\n", (long)sb.st_size, extraMemory, useMmap);
if (fd != ImageIsEmbedded)
printf("image size %ld + heap size %ld (useMmap = %d)\n", (long)sb.st_size, extraMemory, useMmap);
#endif
#if SPURVM
readImageFromFileHeapSizeStartingAt(fd, 0, 0);
#else
extraMemory += (long)sb.st_size;
readImageFromFileHeapSizeStartingAt(fd, extraMemory, 0);
if (fd != ImageIsEmbedded)
extraMemory += (long)sb.st_size;
readImageFromFileHeapSizeStartingAt(fd, extraMemory, 0);
#endif
sqImageFileClose(fd);
}
Expand Down

0 comments on commit be70093

Please sign in to comment.