diff --git a/collects/compiler/embed-unit.ss b/collects/compiler/embed-unit.ss index 0a425084e2..e4cdf91eeb 100644 --- a/collects/compiler/embed-unit.ss +++ b/collects/compiler/embed-unit.ss @@ -13,6 +13,7 @@ "private/winicon.ss" "private/winsubsys.ss" "private/macfw.ss" + "private/mach-o.ss" "private/windlldir.ss") (provide compiler:embed@) @@ -607,12 +608,23 @@ (let-values ([(orig-dir name dir?) (split-path (path->complete-path orig-exe))]) (update-dll-dir dest (build-path orig-dir dir)))))))) - (let ([start (file-size dest-exe)]) - (with-output-to-file dest-exe - (lambda () - (write-module-bundle verbose? modules literal-files literal-expression)) - 'append) - (let ([end (file-size dest-exe)]) + (let ([write-module + (lambda () + (write-module-bundle verbose? modules literal-files literal-expression))]) + (let-values ([(start end) + (if (eq? (system-type) 'macosx) + ;; For Mach-O, we know how to add a proper segment + (let ([s (open-output-bytes)]) + (parameterize ([current-output-port s]) + (write-module)) + (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)]) + (with-output-to-file dest-exe write-module 'append) + (values start (file-size dest-exe))))]) (when verbose? (fprintf (current-error-port) "Setting command line~n")) (let ([start-s (number->string start)] diff --git a/collects/compiler/private/macfw.ss b/collects/compiler/private/macfw.ss index 88775becc9..c391cb5a71 100644 --- a/collects/compiler/private/macfw.ss +++ b/collects/compiler/private/macfw.ss @@ -1,6 +1,8 @@ (module macfw mzscheme - (require (lib "process.ss")) + (require "mach-o.ss" + (lib "string.ss") + (lib "process.ss")) (provide update-framework-path get-current-framework-path @@ -20,43 +22,36 @@ (let* ([orig (get-current-framework-path dest p)] [3m (if (and orig (regexp-match #rx"_3m" orig)) "_3m" - "")]) - (system* "/usr/bin/install_name_tool" - "-change" - (or orig - (format "~a.framework/Versions/~a~a/~a" p (version) 3m p)) - (format "~a~a.framework/Versions/~a~a/~a" - fw-path - p (version) 3m p) - dest))) + "")] + [old-path (or orig + (format "~a.framework/Versions/~a~a/~a" p (version) 3m p))] + [new-path (format "~a~a.framework/Versions/~a~a/~a" + fw-path + p (version) 3m p)]) + (get/set-dylib-path dest + (byte-regexp + (bytes-append + #"^" + (string->bytes/utf-8 + (regexp-quote old-path)) + #"$")) + (string->bytes/utf-8 new-path)))) (if mred? '("PLT_MrEd") '("PLT_MzScheme"))))) (define (get-current-framework-path dest p) - (let-values ([(r w) (make-pipe)]) - (parameterize ([current-output-port w]) - (system* "/usr/bin/otool" - "-L" - (if (path? dest) - (path->string dest) - dest))) - (close-output-port w) - (let ([m (regexp-match (bytes-append #"[\r\n][ \t]*([^ \t][^\r\n]*" - (string->bytes/utf-8 p) - #"[^\r\n]*)" - #" [(]compatibility version [0-9.]*," - #" current version [0-9.]*[)][\r\n]") - r)]) - (if m - (bytes->string/utf-8 (cadr m)) - (begin - (fprintf (current-error-port) - "warning: cannot find existing link for ~a in ~a\n" - p dest) - #f)))))) + (let ([v (get/set-dylib-path dest + (byte-regexp (string->bytes/utf-8 p)) + #f)]) + (if v + (bytes->string/utf-8 v) + (begin + (fprintf (current-error-port) + "warning: cannot find existing link for ~a in ~a\n" + p dest) + #f))))) - \ No newline at end of file diff --git a/collects/compiler/private/mach-o.ss b/collects/compiler/private/mach-o.ss index be19f21fab..4b09e18616 100644 --- a/collects/compiler/private/mach-o.ss +++ b/collects/compiler/private/mach-o.ss @@ -1,13 +1,14 @@ (module mach-o mzscheme - (provide add-plt-segment) + (provide add-plt-segment + get/set-dylib-path) (define (read-ulong p) - (integer-bytes->integer (read-bytes 4 p) #f #t)) + (integer-bytes->integer (read-bytes 4 p) #f)) (define (write-ulong v out) - (display (integer->integer-bytes v 4 #f #t) out)) - + (display (integer->integer-bytes v 4 #f) out)) + (define (check-same a b) (unless (= a b) (error 'check-same "not: ~e ~e" a b))) @@ -15,6 +16,12 @@ (define (round-up-page v) (bitwise-and #xFFFFF000 (+ v #xFFF))) + (define (mult-of-4 n) + (let ([m (modulo n 4)]) + (if (zero? m) + n + (+ n (- 4 m))))) + (define move-link-edit? #t) (define (add-plt-segment file segdata) @@ -37,14 +44,14 @@ [link-edit-addr 0] [link-edit-offset 0] [link-edit-len 0]) - (printf "~a cmds, length 0x~x\n" cnt cmdssz) + ;; (printf "~a cmds, length 0x~x\n" cnt cmdssz) (read-ulong p) (let loop ([cnt cnt]) (unless (zero? cnt) (let ([pos (file-position p)] [cmd (read-ulong p)] [sz (read-ulong p)]) - (printf "~x (~a)\n" cmd sz) + ;; (printf "~x (~a)\n" cmd sz) (case cmd [(1) ;; Segment @@ -53,13 +60,13 @@ [vmlen (read-ulong p)] [offset (read-ulong p)] [len (read-ulong p)]) - (printf "~s\n" segname) + ;; (printf "~s\n" segname) (when (equal? segname #"__LINKEDIT\0\0\0\0\0\0") (set! link-edit-pos pos) (set! link-edit-addr vmaddr) (set! link-edit-offset offset) (set! link-edit-len len)) - (printf " 0x~x 0x~x -> 0x~x 0x~x\n" offset len vmaddr vmlen) + ;; (printf " 0x~x 0x~x -> 0x~x 0x~x\n" offset len vmaddr vmlen) (read-ulong p) (read-ulong p) (let ([nsects (read-ulong p)]) @@ -73,10 +80,9 @@ [offset (read-ulong p)]) (when ((+ offset vmsz) . > . (+ cmdssz 28)) (when (offset . < . min-used) - (printf " new min!\n") + ;; (printf " new min!\n") (set! min-used offset))) - (printf " ~s,~s 0x~x 0x~x\n" - seg sect offset vmsz) + ;; (printf " ~s,~s 0x~x 0x~x\n" seg sect offset vmsz) (read-ulong p) (read-ulong p) (read-ulong p) (read-ulong p) (read-ulong p) (read-ulong p)) (loop (sub1 nsects))))))] [(2) @@ -92,7 +98,7 @@ (void)]) (file-position p (+ pos sz)) (loop (sub1 cnt))))) - (printf "Start offset: 0x~x\n" min-used) + ;; (printf "Start offset: 0x~x\n" min-used) (let ([end-cmd (+ cmdssz 28)] [new-cmd-sz 56] [outlen (round-up-page (bytes-length segdata))] @@ -133,7 +139,7 @@ (set! hints-pos (+ hints-pos 56))) (set! link-edit-pos (+ link-edit-pos 56)) (file-position out (+ link-edit-pos 32)) - (printf "Update to ~a\n" (+ out-offset outlen)) + ;; (printf "Update to ~a\n" (+ out-offset outlen)) (write-ulong (+ out-offset outlen) out) ;; Read link-edit segment: (file-position p link-edit-offset) @@ -170,9 +176,20 @@ (close-input-port p) (close-output-port out))))) - #; - (define (get/set-dylib-path rx new-path) - (let-values ([(p out) (open-input-output-file file 'update)]) + (define (fix-offset p pos out d base delta) + (when (and out (not (zero? delta))) + (file-position p (+ pos d)) + (let ([offset (read-ulong p)]) + (when (offset . > . base) + (file-position out (+ pos d)) + (write-ulong (+ offset delta) out) + (flush-output out))))) + + (define (get/set-dylib-path file rx new-path) + (let-values ([(p out) (if new-path + (open-input-output-file file 'update) + (values (open-input-file file) + #f))]) (dynamic-wind void (lambda () @@ -181,144 +198,63 @@ (read-ulong p) (check-same #x2 (read-ulong p)) (let* ([cnt (read-ulong p)] - [cmdssz (read-ulong p)] - [min-used (round-up-page cmdssz)] - [sym-tab-pos 0] - [dysym-pos 0] - [hints-pos 0] - [link-edit-pos 0] - [link-edit-addr 0] - [link-edit-offset 0] - [link-edit-len 0]) - (printf "~a cmds, length 0x~x\n" cnt cmdssz) + [cmdssz (read-ulong p)]) (read-ulong p) - (let loop ([cnt cnt]) - (unless (zero? cnt) - (let ([pos (file-position p)] - [cmd (read-ulong p)] - [sz (read-ulong p)]) - (printf "~x (~a)\n" cmd sz) - (case cmd - [(1) - ;; Segment - (let ([segname (read-bytes 16 p)] - [vmaddr (read-ulong p)] - [vmlen (read-ulong p)] - [offset (read-ulong p)] - [len (read-ulong p)]) - (printf "~s\n" segname) - (when (equal? segname #"__LINKEDIT\0\0\0\0\0\0") - (set! link-edit-pos pos) - (set! link-edit-addr vmaddr) - (set! link-edit-offset offset) - (set! link-edit-len len)) - (printf " 0x~x 0x~x -> 0x~x 0x~x\n" offset len vmaddr vmlen) - (read-ulong p) - (read-ulong p) - (let ([nsects (read-ulong p)]) - (read-ulong p) - (let loop ([nsects nsects]) - (unless (zero? nsects) - (let ([sect (read-bytes 16 p)] - [seg (read-bytes 16 p)] - [vmaddr (read-ulong p)] - [vmsz (read-ulong p)] - [offset (read-ulong p)]) - (when ((+ offset vmsz) . > . (+ cmdssz 28)) - (when (offset . < . min-used) - (printf " new min!\n") - (set! min-used offset))) - (printf " ~s,~s 0x~x 0x~x\n" - seg sect offset vmsz) - (read-ulong p) (read-ulong p) (read-ulong p) (read-ulong p) (read-ulong p) (read-ulong p)) - (loop (sub1 nsects))))))] - [(2) - ;; Symbol table - (set! sym-tab-pos pos)] - [(#xB) - ;; Dysym - (set! dysym-pos pos)] - [(#x16) - ;; 2-level hints table - (set! hints-pos pos)] - [else - (void)]) - (file-position p (+ pos sz)) - (loop (sub1 cnt))))) - (printf "Start offset: 0x~x\n" min-used) - (let ([end-cmd (+ cmdssz 28)] - [new-cmd-sz 56] - [outlen (round-up-page (bytes-length segdata))] - [out-offset (if move-link-edit? - link-edit-offset - (+ link-edit-offset (round-up-page link-edit-len)))] - [out-addr (+ link-edit-addr (round-up-page link-edit-len))]) - (unless ((+ end-cmd new-cmd-sz) . < . min-used) - (error 'check-header "no room for a new section load command")) - ;; Shift commands after link-edit segment: - (file-position p link-edit-pos) - (let ([s (read-bytes (- end-cmd link-edit-pos) p)]) - (file-position out (+ link-edit-pos 56)) - (display s out)) - (file-position out 16) - ;; The segment: - (write-ulong (+ cnt 1) out) - (write-ulong (+ cmdssz new-cmd-sz) out) - (file-position out link-edit-pos) - (write-ulong 1 out) ; LC_SEGMENT - (write-ulong new-cmd-sz out) - (display #"__PLTSCHEME\0\0\0\0\0" out) - (write-ulong out-addr out) - (write-ulong outlen out) - (write-ulong out-offset out) - (write-ulong outlen out) - (write-ulong 0 out) - (write-ulong 0 out) - (write-ulong 0 out) - (write-ulong 4 out) ; 4 means SG_NORELOC - (when move-link-edit? - ;; Update link-edit segment entry: - (when (sym-tab-pos . > . link-edit-pos) - (set! sym-tab-pos (+ sym-tab-pos 56))) - (when (dysym-pos . > . link-edit-pos) - (set! dysym-pos (+ dysym-pos 56))) - (when (hints-pos . > . link-edit-pos) - (set! hints-pos (+ hints-pos 56))) - (set! link-edit-pos (+ link-edit-pos 56)) - (file-position out (+ link-edit-pos 32)) - (printf "Update to ~a\n" (+ out-offset outlen)) - (write-ulong (+ out-offset outlen) out) - ;; Read link-edit segment: - (file-position p link-edit-offset) - (let ([link-edit (read-bytes link-edit-len p)]) - ;; Write link-edit data in new location: - (file-position out (+ link-edit-offset outlen)) - (display link-edit out)) - ;; Shift symbol-table pointer: - (file-position p (+ sym-tab-pos 8)) - (let ([symtab-offset (read-ulong p)] - [_ (read-ulong p)] - [symstr-offset (read-ulong p)]) - (file-position out (+ sym-tab-pos 8)) - (write-ulong (+ symtab-offset outlen) out) - (file-position out (+ sym-tab-pos 16)) - (write-ulong (+ symstr-offset outlen) out)) - ;; Shift dysym pointers: - (file-position p (+ dysym-pos 56)) - (let ([ind-offset (read-ulong p)]) - (file-position out (+ dysym-pos 56)) - (write-ulong (+ ind-offset outlen) out)) - ;; Shift hints pointer: - (file-position p (+ hints-pos 8)) - (let ([hints-offset (read-ulong p)]) - (file-position out (+ hints-pos 8)) - (write-ulong (+ hints-offset outlen) out))) - ;; Write segdata to former link-data offset: - (file-position out out-offset) - (display segdata out) - (display (make-bytes (- outlen (bytes-length segdata)) 0) out) - ;; Result is offset where data was written: - out-offset))) + (let loop ([cnt cnt][base 0][delta 0][result #f]) + (if (zero? cnt) + result + (let ([pos (file-position p)] + [cmd (read-ulong p)] + [sz (read-ulong p)]) + (case cmd + [(#xC) + ;; LC_LOAD_DYLIB + (let ([offset (read-ulong p)]) + (file-position p (+ pos offset)) + (let* ([namelen (- sz offset)] + [segname (read-bytes namelen p)] + [segname (car (regexp-match #rx#"^[^\0]*" segname))]) + (if (regexp-match rx segname) + (let* ([newnamelen (and out + (mult-of-4 (+ 1 (bytes-length new-path))))] + [delta (if out + (- newnamelen namelen) + 0)]) + (when out + (unless (zero? delta) + ;; We assume that there's enough header room to + ;; extend this load command, because the binary + ;; was linked with -headerpad_max_install_names + (file-position out (+ pos 4)) + (write-ulong (+ sz delta) out) + (flush-output out) + ;; Shift rest of load commands by delta + (let ([end cmdssz]) + (file-position p (+ pos sz)) + (let ([s (read-bytes (- end (+ pos sz)) p)]) + (file-position out (+ pos sz delta)) + (write-bytes s out) + (when (negative? delta) + ;; zero-out now-unneeded space: + (write-bytes (make-bytes (- delta) 0) out)) + (flush-output out)) + ;; Change load-commands size in header: + (file-position out 20) + (write-ulong (+ end delta) out) + (flush-output out))) + (file-position out (+ pos offset)) + (write-bytes new-path out) + (write-bytes (make-bytes (- newnamelen (bytes-length new-path)) 0) out) + (flush-output out)) + (file-position p (+ pos sz delta)) + (loop (sub1 cnt) pos delta segname)) + (begin + (file-position p (+ pos sz)) + (loop (sub1 cnt) base delta result)))))] + [else + (file-position p (+ pos sz)) + (loop (sub1 cnt) base delta result)])))))) (lambda () (close-input-port p) - (close-output-port out)))))) + (when out + (close-output-port out))))))) diff --git a/collects/setup/dirs.ss b/collects/setup/dirs.ss index 8572d0c5c1..3bce270f22 100644 --- a/collects/setup/dirs.ss +++ b/collects/setup/dirs.ss @@ -1,5 +1,6 @@ (module dirs mzscheme - (require (lib "winutf16.ss" "compiler" "private")) + (require (lib "winutf16.ss" "compiler" "private") + (lib "mach-o.ss" "compiler" "private")) (define main-collects-dir (delay @@ -71,11 +72,42 @@ (unless m (error "cannot find \"dLl dIRECTORy\" tag in binary")) (let-values ([(dir name dir?) (split-path exe)]) (if (regexp-match #rx#"^<" (cadr m)) - ;; no DLL dir in binary, so assume exe dir: - dir + ;; no DLL dir in binary + #f ;; resolve relative directory: (let ([p (bytes->path (utf-16-bytes->bytes (cadr m)))]) (path->complete-path p dir))))))))] + [(macosx) + (let ([exe (parameterize ([current-directory (find-system-path 'orig-dir)]) + (let loop ([p (find-executable-path (find-system-path 'exec-file))]) + (if (link-exists? p) + (loop (let-values ([(r) (resolve-path p)] + [(dir name dir?) (split-path p)]) + (if (and (path? dir) + (relative-path? r)) + (build-path dir r) + r))) + p)))]) + (let ([rel (get/set-dylib-path exe "PLT_M[rz]" #f)]) + (if rel + (cond + [(regexp-match #rx#"^(@executable_path/)?(.*?)PLT_M(?:rEd|zScheme).framework" rel) + => (lambda (m) + (let ([b (caddr m)]) + (if (and (not (cadr m)) + (bytes=? b #"")) + #f ; no path in exe + (simplify-path + (path->complete-path (if (not (cadr m)) + (bytes->path b) + (let-values ([(dir name dir?) (split-path exe)]) + (if (bytes=? b #"") + dir + (build-path dir (bytes->path b))))) + (find-system-path 'orig-dir))))))] + [else (find-lib-dir)]) + ;; no framework reference found!? + #f)))] [else (find-lib-dir)]))) (define (find-dll-dir) diff --git a/collects/setup/doc.txt b/collects/setup/doc.txt index 29070cfa45..6fdf4f4367 100644 --- a/collects/setup/doc.txt +++ b/collects/setup/doc.txt @@ -303,7 +303,8 @@ installation directories: Returns a path to the directory that contains DLLs for use with the current executable (e.g., "libmzsch.dll" under Windows). The result - is #f if no such directory is available. + is #f if no such directory is available, or if no specific directory + is available (i.e., other than the platform's normal search path). _Getting info.ss fields_