Skip to content

Commit

Permalink
Add an encapper for Windows and Linux
Browse files Browse the repository at this point in the history
The way it works is:
1. embedded the script into the executable file
2. the interpreter checks for the existence of the embedded script
3. if it finds it, runs it and ignores "--do" and script passed in from
the command line
4. if not, act as a regular interpreter

The encapper on linux is just a wrapper around objcopy, wihch adds a
section with the script to the executable

On Windows, the script is added as a resource, and some windows APIs are
used to retrieve the information.
  • Loading branch information
zsx committed Oct 20, 2014
1 parent a3eaf9f commit c4a478b
Show file tree
Hide file tree
Showing 8 changed files with 317 additions and 3 deletions.
111 changes: 111 additions & 0 deletions make/encap.r
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
REBOL[]

args: parse system/script/args ""
exe: none
payload: none
output: none
windows?: 3 = fourth system/version

while [not tail? args] [
arg: first args
case [
any [arg = "/rebol"
arg = "/r"] [
exe: second args
args: next args
]
any [arg = "/payload"
arg = "/p"] [
payload: second args
args: next args
]
any [arg = "/payload"
arg = "/p"] [
payload: second args
args: next args
]
any [arg = "/output"
arg = "/o"] [
output: second args
args: next args
]
]
args: next args
]

if any [none? exe
none? payload
none? output][
print ["pack.r"]
print ["^-/rebol | /r^- path-to-rebol"]
print ["^-/payload | /p^- path-to-payload"]
print ["^-/output | /o^- path-to-output"]
quit
]

payload-data: join #{01000000} compress read to file! payload
tmp: join payload ".tmp"
write to file! tmp payload-data

either windows? [
unicodify: function [
s [string!]
][
ret: copy #{}
foreach c s [
append ret join to binary! c #{00}
]
join ret #{0000} ;NULL terminator
]

kernel32: make library! %kernel32

print ["kernel:" mold kernel32]
BeginUpdateResource: make routine! compose [[
filename [pointer]
delete-existing-resources [int32]
return: [pointer]
] (kernel32) "BeginUpdateResourceW"]

UpdateResource: make routine! compose [[
hUpdate [pointer]
lpType [pointer]
lpName [pointer]
wLanguage [uint16]
lpData [pointer]
cbData [uint32]
return: [int32]
] (kernel32) "UpdateResourceW"]

EndUpdateResource: make routine! compose [[
hUpdate [pointer]
fDiscard [uint8]
return: [int32]
] (kernel32) "EndUpdateResourceW"]

GetLastError: make routine! compose [[
return: [uint32]
] (kernel32) "GetLastError"]

write to file! output read to file! exe
h: BeginUpdateResource unicodify output 0
if zero? h [
print ["failed to open exe"]
halt
]
if zero? UpdateResource h 10
unicodify "EmbEddEdREbol"
0 payload-data length? payload-data [
print ["failed to update resource"]
halt
]
if zero? EndUpdateResource h 0 [
print ["failed to write change back to the file due to " GetLastError]
halt
]
][
magic: ".EmbEddEdREbol"
call reform ["objcopy -R" magic exe]
call rejoin ["objcopy --add-section " magic "=" tmp " " exe " " output]
delete to file! tmp
]
Binary file removed make/upx.exe
Binary file not shown.
28 changes: 27 additions & 1 deletion src/core/a-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ extern int Do_Callback(REBSER *obj, u32 name, RXIARG *args, RXIARG *result);

/***********************************************************************
**
*/ RL_API int RL_Start(REBYTE *bin, REBINT len, REBCNT flags)
*/ RL_API int RL_Start(REBYTE *bin, REBINT len, REBYTE *script, REBINT script_len, REBCNT flags)
/*
** Evaluate the default boot function.
**
Expand Down Expand Up @@ -164,6 +164,32 @@ extern int Do_Callback(REBSER *obj, u32 name, RXIARG *args, RXIARG *result);
Set_Binary(val, ser);
}

if (script && script_len > 4) {
/* a 4-byte long payload type at the beginning */
i32 ptype = 0;
void *data = script + sizeof(ptype);
script_len -= sizeof(ptype);

COPY_MEM(&ptype, script, sizeof(ptype));

if (ptype == 1) {/* COMPRESSed data */
spec.data = data;
spec.tail = script_len;
ser = Decompress(&spec, 0, -1, 10000000, 0);
} else {
ser = Make_Binary(script_len);
if (ser == NULL) {
OS_Free(script);
return 1;
}
COPY_MEM(BIN_HEAD(ser), data, script_len);
}
OS_Free(script);

val = BLK_SKIP(Sys_Context, SYS_CTX_BOOT_EMBEDDED);
Set_Binary(val, ser);
}

return Init_Mezz(0);
}

Expand Down
1 change: 1 addition & 0 deletions src/mezz/sys-base.r
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ boot-host: none ; any host add-ons to the lib (binary)
boot-mezz: none ; built-in mezz code (put here on boot)
boot-prot: none ; built-in boot protocols
boot-exts: none ; boot extension list
boot-embedded: none ; embedded script

export: func [
"Low level export of values (e.g. functions) to lib."
Expand Down
7 changes: 7 additions & 0 deletions src/mezz/sys-start.r
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ start: func [
; Import module?
if import [lib/import import]

unless none? boot-embedded [
code: load boot-embedded
;boot-print ["executing embedded script:" mold code]
do code
quit ;ignore user script and "--do" argument
]

;-- Evaluate: --do "some code" if found
if do-arg [
do intern do-arg
Expand Down
7 changes: 5 additions & 2 deletions src/os/host-main.c
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,8 @@ int main(int argc, char **argv)
REBYTE vers[8];
REBYTE *line;
REBINT n;
REBYTE *embedded_script = NULL;
REBI64 embedded_size = 0;

#ifdef TO_WIN32 // In Win32 get args manually:
// Fetch the win32 unicoded program arguments:
Expand All @@ -146,6 +148,7 @@ int main(int argc, char **argv)
always_malloc = atoi(env_always_malloc);
}

embedded_script = OS_Read_Embedded((REBCHR*)argv[0], &embedded_size);
Parse_Args(argc, (REBCHR **)argv, &Main_Args);

vers[0] = 5; // len
Expand Down Expand Up @@ -228,9 +231,9 @@ int main(int argc, char **argv)
// Returns: 0: ok, -1: error, 1: bad data.
#ifdef CUSTOM_STARTUP
// For custom startup, you can provide compressed script code here:
n = RL_Start((REBYTE *)(&Reb_Init_Code[0]), REB_INIT_SIZE, 0); // TRUE on halt
n = RL_Start((REBYTE *)(&Reb_Init_Code[0]), REB_INIT_SIZE, embedded_script, (REBINT)embedded_size, 0); // TRUE on halt
#else
n = RL_Start(0, 0, 0);
n = RL_Start(0, 0, embedded_script, (REBINT)embedded_size, 0);
#endif

#ifdef TO_WIN32
Expand Down
111 changes: 111 additions & 0 deletions src/os/linux/host-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@
#include <dlfcn.h>
#endif

#include <elf.h>

#ifndef REB_CORE
REBSER* Gob_To_Image(REBGOB *gob);
#endif
Expand Down Expand Up @@ -831,3 +833,112 @@ static int Try_Browser(char *browser, REBCHR *url)
}
return TRUE;
}

/***********************************************************************
**
*/ REBYTE * OS_Read_Embedded (const REBCHR *path, REBI64 *script_size)
/*
***********************************************************************/
{
#ifdef __LP64__
Elf64_Ehdr file_header;
Elf64_Shdr *sec_headers;
#else
Elf32_Ehdr file_header;
Elf32_Shdr *sec_headers;
#endif

#define PAYLOAD_NAME ".EmbEddEdREbol"

FILE *script = NULL;
REBI64 nbytes = 0;
int i = 0;
char *ret = NULL;
char *embedded_script = NULL;

script = fopen(path, "r");
if (script == NULL) return NULL;

nbytes = fread(&file_header, sizeof(file_header), 1, script);
if (nbytes < 1) {
fclose(script);
return NULL;
}

sec_headers = OS_Make(file_header.e_shnum * file_header.e_shentsize);
if (sec_headers == NULL) {
fclose(script);
return NULL;
}

if (fseek(script, file_header.e_shoff, SEEK_SET) < 0) {
OS_Free(sec_headers);
fclose(script);
return NULL;
}

nbytes = fread(sec_headers, file_header.e_shentsize, file_header.e_shnum, script);
if (nbytes < file_header.e_shnum) {
ret = NULL;
goto header_failed;
}

char *shstr = OS_Make(sec_headers[file_header.e_shstrndx].sh_size);
if (shstr == NULL) {
ret = NULL;
goto header_failed;
}

if (fseek(script, sec_headers[file_header.e_shstrndx].sh_offset, SEEK_SET) < 0) {
ret = NULL;
goto shstr_failed;
}

nbytes = fread(shstr, sec_headers[file_header.e_shstrndx].sh_size, 1, script);
if (nbytes < 1) {
ret = NULL;
goto shstr_failed;
}

for (i = 0; i < file_header.e_shnum; i ++) {
/* check the section name */
if (!strncmp(shstr + sec_headers[i].sh_name, PAYLOAD_NAME, sizeof(PAYLOAD_NAME))) {
*script_size = sec_headers[i].sh_size;
break;
}
}

if (i == file_header.e_shnum) {
ret = NULL;
goto cleanup;
}

embedded_script = OS_Make(sec_headers[i].sh_size); /* will be free'ed by RL_Start */
if (embedded_script == NULL) {
ret = NULL;
goto shstr_failed;
}
if (fseek(script, sec_headers[i].sh_offset, SEEK_SET) < 0) {
ret = NULL;
goto embedded_failed;
}

nbytes = fread(embedded_script, 1, sec_headers[i].sh_size, script);
if (nbytes < sec_headers[i].sh_size) {
ret = NULL;
goto embedded_failed;
}

ret = embedded_script;
goto cleanup;

embedded_failed:
OS_Free(embedded_script);
cleanup:
shstr_failed:
OS_Free(shstr);
header_failed:
OS_Free(sec_headers);
fclose(script);
return ret;
}
55 changes: 55 additions & 0 deletions src/os/win32/host-lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -815,3 +815,58 @@ int CALLBACK ReqDirCallbackProc( HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpD
*string = (len == 0) ? NULL : str; //empty string check
return FALSE;
}

/***********************************************************************
**
** Read embedded rebol script from the executable
*/ REBYTE * OS_Read_Embedded (const REBCHR *path, REBI64 *script_size)
/*
* rebol script is appended to the executable file, with a PAYLOAD_NAME + '0' + 8-byte offset at the very end
* offset refers to the starting offset of script in the executable file from the end
***********************************************************************/
{
#define PAYLOAD_NAME L"EMBEDDEDREBOL"

FILE *script = NULL;
char *embedded_script = NULL;
HMODULE h_mod= 0;
HRSRC h_res = 0;
HGLOBAL h_res_mem = NULL;
void *res_ptr = NULL;

h_mod = GetModuleHandle(NULL);
if (h_mod == 0) {
return NULL;
}

h_res = FindResource(h_mod, PAYLOAD_NAME, RT_RCDATA);
if (h_res == 0) {
return NULL;
}

h_res_mem = LoadResource(h_mod, h_res);
if (h_res_mem == 0) {
return NULL;
}

res_ptr = LockResource(h_res_mem);
if (res_ptr == NULL) {
return NULL;
}

*script_size = SizeofResource(h_mod, h_res);

if (*script_size <= 0) {
return NULL;
}

embedded_script = OS_Make(*script_size);

if (embedded_script == NULL) {
return NULL;
}

memcpy(embedded_script, res_ptr, *script_size);

return embedded_script;
}

2 comments on commit c4a478b

@Oldes
Copy link

@Oldes Oldes commented on c4a478b Nov 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zsx Is there a reason why you added embedded_script and not just used the script which could be included with CUSTOM_STARTUP definition? Are you having situation when you embed a script with additional custom script?

@zsx
Copy link
Owner Author

@zsx zsx commented on c4a478b Nov 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really other than being back compatible.

Please sign in to comment.