write proper ELF section for Unix launchers/executables

This commit is contained in:
Matthew Flatt 2011-04-17 16:05:20 -06:00 committed by Matthew Flatt
parent 875385b8f7
commit 6b2219b9e5
3 changed files with 502 additions and 50 deletions

View File

@ -15,6 +15,7 @@
"private/winsubsys.ss"
"private/macfw.ss"
"private/mach-o.ss"
"private/elf.ss"
"private/windlldir.ss"
"private/collects-path.ss"
"find-exe.rkt")
@ -1168,8 +1169,44 @@
compiler
expand-namespace
src-filter
get-extra-imports))])
(let-values ([(start end)
get-extra-imports))]
[make-full-cmdline
(lambda (start end)
(let ([start-s (number->string start)]
[end-s (number->string end)])
(append (if launcher?
(if (and (eq? 'windows (system-type))
keep-exe?)
;; argv[0] replacement:
(list (path->string
(if relative?
(relativize exe dest-exe values)
exe)))
;; No argv[0]:
null)
(list "-k" start-s end-s))
cmdline)))]
[make-starter-cmdline
(lambda (full-cmdline)
(apply bytes-append
(map (lambda (s)
(bytes-append
(cond
[(path? s) (path->bytes s)]
[else (string->bytes/locale s)])
#"\0"))
(append
(list (if relative?
(relativize exe dest-exe values)
exe)
(let ([dir (find-dll-dir)])
(if dir
(if relative?
(relativize dir dest-exe values)
dir)
"")))
full-cmdline))))])
(let-values ([(start end cmdline-end)
(if (and (eq? (system-type) 'macosx)
(not unix-starter?))
;; For Mach-O, we know how to add a proper segment
@ -1178,29 +1215,37 @@
(let ([s (get-output-bytes s)])
(let ([start (add-plt-segment dest-exe s)])
(values start
(+ start (bytes-length s))))))
;; Other platforms: just add to the end of the file:
(let ([start (file-size dest-exe)])
(call-with-output-file* dest-exe write-module
#:exists 'append)
(values start (file-size dest-exe))))])
(+ start (bytes-length s))
#f))))
;; Unix starter: Maybe ELF, in which case we
;; can add a proper section
(let-values ([(s e p)
(if unix-starter?
(add-racket-section
orig-exe
dest-exe
(if launcher? #".rackcmdl" #".rackprog")
(lambda (start)
(let ([s (open-output-bytes)])
(write-module s)
(let ([p (file-position s)])
(display (make-starter-cmdline
(make-full-cmdline start (+ start p)))
s)
(values (get-output-bytes s) p)))))
(values #f #f #f))])
(if (and s e)
;; ELF succeeded:
(values s (+ s p) e)
;; Otherwise, just add to the end of the file:
(let ([start (file-size dest-exe)])
(call-with-output-file* dest-exe write-module
#:exists 'append)
(values start (file-size dest-exe) #f)))))])
(when verbose?
(fprintf (current-error-port) "Setting command line\n"))
(let ([start-s (number->string start)]
[end-s (number->string end)])
(let ([full-cmdline (append
(if launcher?
(if (and (eq? 'windows (system-type))
keep-exe?)
;; argv[0] replacement:
(list (path->string
(if relative?
(relativize exe dest-exe values)
exe)))
;; No argv[0]:
null)
(list "-k" start-s end-s))
cmdline)])
(let ()
(let ([full-cmdline (make-full-cmdline start end)])
(when collects-path-bytes
(when verbose?
(fprintf (current-error-port) "Setting collection path\n"))
@ -1218,27 +1263,12 @@
(lambda () (find-cmdline
"exeuctable type"
#"bINARy tYPe:"))))]
[cmdline
(apply bytes-append
(map (lambda (s)
(bytes-append
(cond
[(path? s) (path->bytes s)]
[else (string->bytes/locale s)])
#"\0"))
(append
(list (if relative?
(relativize exe dest-exe values)
exe)
(let ([dir (find-dll-dir)])
(if dir
(if relative?
(relativize dir dest-exe values)
dir)
"")))
full-cmdline)))]
[cmdline (if cmdline-end
#f
(make-starter-cmdline full-cmdline))]
[out (open-output-file dest-exe #:exists 'update)])
(let ([cmdline-end (+ end (bytes-length cmdline))]
(let ([old-cmdline-end cmdline-end]
[cmdline-end (or cmdline-end (+ end (bytes-length cmdline)))]
[write-num (lambda (n)
(write-bytes (integer->integer-bytes n 4 #t #f) out))])
(dynamic-wind
@ -1260,9 +1290,10 @@
(write-num (length full-cmdline))
(write-num (if mred? 1 0))
(flush-output out)
(file-position out end)
(write-bytes cmdline out)
(flush-output out))
(unless old-cmdline-end
(file-position out end)
(write-bytes cmdline out)
(flush-output out)))
(lambda ()
(close-output-port out)))))]
[else

View File

@ -0,0 +1,316 @@
#lang racket/base
(provide add-racket-section)
(define 32BIT 1)
(define 64BIT 2)
(define LITTLEEND 1)
(define BIGEND 2)
(define SECTION-ALIGN 16) ; conservative?
(define SHT_PROGBITS 1)
(struct elf (ph-offset ph-esize ph-ecount
sh-offset sh-esize sh-ecount
class format sh-str-index)
#:transparent)
(struct section (name-offset offset size)
#:transparent)
(struct program (offset size)
#:transparent)
(define (copy-port-bytes amt in out)
(let ([get (min amt 4096)])
(let ([s (read-bytes get in)])
(unless (and (bytes? s)
(= get (bytes-length s)))
(error 'add-elf-section "file data copy failed"))
(write-bytes s out))
(unless (= get amt)
(copy-port-bytes (- amt get) in out))))
(define (write-n-bytes amt out)
(let ([put (min amt 4096)])
(write-bytes (make-bytes put 0) out)
(unless (= put amt)
(write-n-bytes (- amt put) out))))
(define (round-up v align)
(let ([d (modulo v align)])
(if (zero? d)
v
(+ v (- align d)))))
(define (read-elf p fail-k k #:dump? [dump? #f])
(define (stop) (raise "unexpected input"))
(define (expect b)
(eq? b (read-byte p)))
(define (skip n)
(for ([i (in-range n)])
(when (eof-object? (read-byte p))
(stop))))
(define (read-a-byte)
(let ([v (read-byte p)])
(when (eof-object? v)
(stop))
v))
(define (skip-half) (skip 2))
(define (skip-word) (skip 4))
(define (show v) (displayln v) v)
;; Read ELF identification ---------------
(if (not (and
(expect #x7F)
(expect (char->integer #\E))
(expect (char->integer #\L))
(expect (char->integer #\F))))
;; Not an ELF binary
(fail-k)
;; Is an ELF binary:
(let ([class (read-byte p)])
(unless (or (= class 32BIT)
(= class 64BIT))
(stop))
(let ([format (read-byte p)])
(unless (or (= format LITTLEEND)
(= format BIGEND))
(stop))
;; Set up multi-byte reading ---------------
(let* ([read-word
(lambda ()
(let ([a (read-a-byte)]
[b (read-a-byte)]
[c (read-a-byte)]
[d (read-a-byte)])
(cond
[(= format LITTLEEND)
(bitwise-ior a
(arithmetic-shift b 8)
(arithmetic-shift c 16)
(arithmetic-shift d 24))]
[else
(bitwise-ior d
(arithmetic-shift c 8)
(arithmetic-shift b 16)
(arithmetic-shift a 24))])))]
[read-xword
(lambda ()
(if (= class 32BIT)
(read-word)
(let ([b (read-bytes 8 p)])
(if (and (bytes? b) (= 8 (bytes-length b)))
(integer-bytes->integer b #f (= format BIGEND))
(stop)))))]
[read-half
(lambda ()
(let ([a (read-a-byte)]
[b (read-a-byte)])
(cond
[(= format LITTLEEND)
(bitwise-ior a
(arithmetic-shift b 8))]
[else
(bitwise-ior b
(arithmetic-shift a 8))])))]
[skip-addr (lambda ()
(skip (if (= class 32BIT) 4 8)))]
[read-addr (lambda () (read-xword))]
[read-off (lambda () (read-xword))])
(skip 1) ; version
(skip 9) ; padding
(skip-half) ; type
(skip-half) ; machine
(skip-word) ; version
(skip-addr) ; entry
;; Read rest of ELF header -----------------
(let ([ph-offset (read-off)]
[sh-offset (read-off)]
[flags (read-word)]
[eh-size (read-half)]
[ph-esize (read-half)]
[ph-ecount (read-half)]
[sh-esize (read-half)]
[sh-ecount (read-half)]
[sh-str-index (read-half)])
;; Read sections ------------------------
(let ([sections
(for/list ([i (in-range sh-ecount)])
(file-position p (+ sh-offset (* i sh-esize)))
(let ([name-offset (read-word)]
[type (read-word)]
[flags (read-xword)]
[addr (read-addr)]
[offset (read-off)]
[size (read-xword)]
[link (read-word)]
[info (read-word)]
[align (read-xword)]
[esize (read-xword)])
(section name-offset offset size)))])
;; Read program headers ------------------------
(let ([programs
(for/list ([i (in-range ph-ecount)])
(file-position p (+ ph-offset (* i ph-esize)))
(let ([type (read-word)]
[flags (if (= class 32BIT)
0
(read-word))]
[offset (read-off)]
[vaddr (read-addr)]
[paddr (read-addr)]
[file-size (read-xword)])
(program offset file-size)))])
;; Load strings from string section ------------------------
(let* ([str-section (list-ref sections sh-str-index)]
[strs (begin
(file-position p (section-offset str-section))
(read-bytes (section-size str-section) p))])
(when dump?
(for ([s (in-list sections)])
(printf "~s ~x ~x\n"
(regexp-match #rx#"[^\0]*" strs (min (section-name-offset s)
(bytes-length strs)))
(section-offset s)
(section-size s))))
(k (elf ph-offset ph-esize ph-ecount
sh-offset sh-esize sh-ecount
class format sh-str-index)
sections programs
str-section strs))))))))))
(define (add-racket-section src-file dest-file section-name get-data)
(call-with-input-file*
src-file
(lambda (in)
(read-elf
in
(lambda () (values #f #f #f))
(lambda (elf sections programs str-section strs)
(let ([new-sec-pos (+ (elf-sh-offset elf)
(* (elf-sh-esize elf) (elf-sh-ecount elf)))]
[new-sec-delta (round-up (elf-sh-ecount elf) SECTION-ALIGN)]
[new-str-pos (+ (section-offset str-section)
(section-size str-section))]
[new-str-delta (round-up (add1 (bytes-length section-name))
SECTION-ALIGN)]
[total-size (file-size src-file)]
[class (elf-class elf)]
[format (elf-format elf)])
(let-values ([(a-pos a-delta b-pos b-delta)
(if (new-sec-pos . < . new-str-pos)
(values new-sec-pos new-sec-delta
new-str-pos new-str-delta)
(values new-str-pos new-str-delta
new-sec-pos new-sec-delta))]
[(data mid) (get-data (+ total-size new-str-delta new-sec-delta))])
(call-with-output-file*
dest-file
#:exists 'truncate
(lambda (out)
(let* ([write-word
(lambda (v)
(if (= format LITTLEEND)
(begin
(write-byte (bitwise-and v #xFF) out)
(write-byte (bitwise-and (arithmetic-shift v -8) #xFF) out)
(write-byte (bitwise-and (arithmetic-shift v -16) #xFF) out)
(write-byte (bitwise-and (arithmetic-shift v -24) #xFF) out))
(begin
(write-byte (bitwise-and (arithmetic-shift v -24) #xFF) out)
(write-byte (bitwise-and (arithmetic-shift v -16) #xFF) out)
(write-byte (bitwise-and (arithmetic-shift v -8) #xFF) out)
(write-byte (bitwise-and v #xFF) out))))]
[write-xword
(lambda (v)
(if (= class 32BIT)
(write-word v)
(display (integer->integer-bytes v 8 #f (= format BIGEND))
out)))]
[write-addr (lambda (v) (write-xword v))]
[write-off (lambda (v) (write-xword v))]
[write-half
(lambda (v)
(if (= format LITTLEEND)
(begin
(write-byte (bitwise-and v #xFF) out)
(write-byte (bitwise-and (arithmetic-shift v -8) #xFF) out))
(begin
(write-byte (bitwise-and (arithmetic-shift v -8) #xFF) out)
(write-byte (bitwise-and v #xFF) out))))]
[adjust (lambda (offset)
(if (offset . >= . a-pos)
(if (offset . >= . b-pos)
(+ offset a-delta b-delta)
(+ offset a-delta))
offset))]
[adjust* (lambda (offset)
(add1 (adjust (sub1 offset))))]
[at-class (lambda (a b) (if (= class 32BIT) a b))])
(file-position in 0)
(copy-port-bytes a-pos in out)
(write-n-bytes a-delta out)
(copy-port-bytes (- b-pos a-pos) in out)
(write-n-bytes b-delta out)
(copy-port-bytes (- total-size b-pos) in out)
;; Write new section:
(file-position out (adjust* new-sec-pos))
(write-word (section-size str-section))
(write-word SHT_PROGBITS)
(write-xword 0) ; flags
(write-addr 0) ; addr
(write-off (+ total-size new-sec-delta new-str-delta))
(write-xword (bytes-length data))
(write-word 0) ; link
(write-word 0) ; info
(write-xword 1) ; align
(write-xword 0) ; esize
;; Write new string:
(file-position out (adjust* new-str-pos))
(write-bytes section-name out)
;; Fix section-header and program-header offsets:
(file-position out (at-class 28 32))
(write-off (adjust (elf-ph-offset elf)))
(write-off (adjust (elf-sh-offset elf)))
;; Increment section count:
(file-position out (at-class 48 60))
(write-half (add1 (length sections)))
;; Increment string section size:
(file-position out (adjust (+ (elf-sh-offset elf)
(* (elf-sh-str-index elf)
(elf-sh-esize elf))
(at-class 20 32))))
(write-xword (+ (section-size str-section) new-str-delta))
;; Fix up section offsets:
(for ([s (in-list sections)]
[i (in-naturals)])
(let ([offset (section-offset s)])
(when (offset . > . a-pos)
(file-position out (adjust (+ (elf-sh-offset elf)
(* i (elf-sh-esize elf))
(at-class 16 24))))
(write-off (adjust offset)))))
;; Fix up program offsets:
(for ([p (in-list programs)]
[i (in-naturals)])
(let ([offset (program-offset p)])
(when (offset . > . a-pos)
(file-position out (adjust (+ (elf-ph-offset elf)
(* i (elf-ph-esize elf))
(at-class 4 8))))
(write-off (adjust offset)))))
;; Write new section data:
(let ([dest (+ total-size new-sec-delta new-str-delta)])
(file-position out dest)
(write-bytes data out)
(values dest (+ dest (bytes-length data)) mid))))))))))))

View File

@ -24,6 +24,13 @@
exe_path - program to start (relative is w.r.t. executable)
dll_path - DLL directory if non-empty (relative is w.r.t. executable)
cmdline_arg ...
For ELF binaries, the absolute values of `start', `prog_end', and
`end' are ignored if a ".rackcmdl" (starter) or ".rackprog"
(embedding) section is found. The `start' value is set to match the
section offset, and `prog_end' and `end' are correspondingly
adjusted. Using a seciton offset allows linking tools (such as
`strip') to move the data in the executable.
*/
char *config = "cOnFiG:[***************************";
@ -101,8 +108,6 @@ static int write_str(int fd, char *s)
return write(fd, s, strlen(s));
}
#if 0
/* Useful for debugging: */
static char *num_to_string(int n)
{
if (!n)
@ -118,7 +123,6 @@ static char *num_to_string(int n)
return d;
}
}
#endif
static char *string_append(char *s1, char *s2)
{
@ -225,12 +229,100 @@ static char *next_string(char *s)
return s + strlen(s) + 1;
}
typedef unsigned short ELF__Half;
typedef unsigned int ELF__Word;
typedef unsigned long ELF__Xword;
typedef unsigned long ELF__Addr;
typedef unsigned long ELF__Off;
typedef struct {
unsigned char e_ident[16];
ELF__Half e_type;
ELF__Half e_machine;
ELF__Word e_version;
ELF__Addr e_entry;
ELF__Off e_phoff;
ELF__Off e_shoff;
ELF__Word e_flags;
ELF__Half e_ehsize;
ELF__Half e_phentsize;
ELF__Half e_phnum;
ELF__Half e_shentsize;
ELF__Half e_shnum;
ELF__Half e_shstrndx;
} ELF__Header;
typedef struct
{
ELF__Word sh_name;
ELF__Word sh_type;
ELF__Xword sh_flags;
ELF__Addr sh_addr;
ELF__Off sh_offset;
ELF__Xword sh_size;
ELF__Word sh_link;
ELF__Word sh_info;
ELF__Xword sh_addralign;
ELF__Xword sh_entsize;
} Elf__Shdr;
static int try_elf_section(const char *me, int *_start, int *_prog_end, int *_end)
{
int fd, i;
ELF__Header e;
Elf__Shdr s;
char *strs;
fd = open(me, O_RDONLY, 0);
if (fd == -1) return 0;
if (read(fd, &e, sizeof(e)) == sizeof(e)) {
if ((e.e_ident[0] == 0x7F)
&& (e.e_ident[1] == 'E')
&& (e.e_ident[2] == 'L')
&& (e.e_ident[3] == 'F')) {
lseek(fd, e.e_shoff + (e.e_shstrndx * e.e_shentsize), SEEK_SET);
if (read(fd, &s, sizeof(s)) != sizeof(s)) {
close(fd);
return 0;
}
strs = (char *)malloc(s.sh_size);
lseek(fd, s.sh_offset, SEEK_SET);
if (read(fd, strs, s.sh_size) != s.sh_size) {
close(fd);
return 0;
}
for (i = 0; i < e.e_shnum; i++) {
lseek(fd, e.e_shoff + (i * e.e_shentsize), SEEK_SET);
if (read(fd, &s, sizeof(s)) != sizeof(s)) {
close(fd);
return 0;
}
if (!strcmp(strs + s.sh_name, ".rackcmdl")
|| !strcmp(strs + s.sh_name, ".rackprog")) {
*_prog_end = (*_prog_end - *_start) + s.sh_offset;
*_start = s.sh_offset;
*_end = s.sh_offset + s.sh_size;
close(fd);
return !strcmp(strs + s.sh_name, ".rackprog");
}
}
}
}
close(fd);
return 0;
}
int main(int argc, char **argv)
{
char *me = argv[0], *data, **new_argv;
char *exe_path, *lib_path, *dll_path;
int start, prog_end, end, count, fd, v, en, x11;
int argpos, inpos, collcount = 1;
int argpos, inpos, collcount = 1, fix_argv;
if (config[7] == '[') {
write_str(2, argv[0]);
@ -314,6 +406,8 @@ int main(int argc, char **argv)
count = as_int(config + 20);
x11 = as_int(config + 24);
fix_argv = try_elf_section(me, &start, &prog_end, &end);
{
int offset, len;
offset = _coldir_offset;
@ -407,6 +501,12 @@ int main(int argc, char **argv)
}
}
if (fix_argv) {
/* next three args are "-k" and numbers; fix
the numbers to match start and prog_end */
fix_argv = argpos + 1;
}
/* Add built-in flags: */
while (count--) {
new_argv[argpos++] = data;
@ -420,6 +520,11 @@ int main(int argc, char **argv)
new_argv[argpos] = NULL;
if (fix_argv) {
new_argv[fix_argv] = num_to_string(start);
new_argv[fix_argv+1] = num_to_string(prog_end);
}
/* Execute the original binary: */
v = execv(exe_path, new_argv);