[haiku-development] [RFC] [PATCH] Making runtime_loader PE aware

  • From: Alexander von Gluck IV <kallisti5@xxxxxxxxxxx>
  • To: haiku-development@xxxxxxxxxxxxx
  • Date: Tue, 7 Jan 2014 17:00:50 -0600

I've made some changes locally to make the runtime_loader somewhat aware of MZ
and PE binaries. Old BeOS R3 x86 applications, Windows applications, and dos
applications fall into this group.

Previously tracker showed the "Fix mistakenly marked executable" dialog when
any of these were executed (as seen in the text file example)

A text file "accidently" marked as executable:
   http://www.zimagez.com/zimage/screenshot-010714-164802.php

Now the PE executables get a special warning:

Old BeOS R3 x86 applications that are marked as executable:
   http://www.zimagez.com/zimage/screenshot-010714-164656.php

Windows applications marked as executable:
   http://www.zimagez.com/zimage/screenshot-010714-164515.php

This could be extended someday, however for now makes trying to run old BeOS
binaries a little less confusing. (unless they are PowerPC coff)

Thoughts?  This patch touches a lot of core OS parts, so I didn't want
to commit it without getting general approval

The check for PE is the last action performed before giving up, so it
shouldn't introduce any speed impacts.


-------------------------------------------

diff --git a/headers/build/os/support/Errors.h 
b/headers/build/os/support/Errors.h
index 750a660..82f6aba 100644
--- a/headers/build/os/support/Errors.h
+++ b/headers/build/os/support/Errors.h
@@ -82,6 +82,7 @@ enum {
        B_NOT_AN_EXECUTABLE,
        B_MISSING_LIBRARY,
        B_MISSING_SYMBOL,
+       B_BAD_EXECUTABLE_FORMAT,
 
        B_DEBUGGER_ALREADY_INSTALLED = B_OS_ERROR_BASE + 0x400
 };
@@ -109,7 +110,8 @@ enum
        B_BAD_MIME_SNIFFER_RULE,
        B_NOT_A_MESSAGE,
        B_SHUTDOWN_CANCELLED,
-       B_SHUTTING_DOWN
+       B_SHUTTING_DOWN,
+       B_LAUNCH_BAD_EXECUTABLE
 };
 
 
diff --git a/headers/os/support/Errors.h b/headers/os/support/Errors.h
index 48f5699..d9499e2 100644
--- a/headers/os/support/Errors.h
+++ b/headers/os/support/Errors.h
@@ -71,6 +71,7 @@
 #define B_NOT_AN_EXECUTABLE                    (B_OS_ERROR_BASE + 0x302)
 #define B_MISSING_LIBRARY                      (B_OS_ERROR_BASE + 0x303)
 #define B_MISSING_SYMBOL                       (B_OS_ERROR_BASE + 0x304)
+#define B_BAD_EXECUTABLE_FORMAT                (B_OS_ERROR_BASE + 0x305)
 
 #define B_DEBUGGER_ALREADY_INSTALLED   (B_OS_ERROR_BASE + 0x400)
 
@@ -94,6 +95,7 @@
 #define B_NOT_A_MESSAGE                                                
(B_APP_ERROR_BASE + 16)
 #define B_SHUTDOWN_CANCELLED                           (B_APP_ERROR_BASE + 17)
 #define B_SHUTTING_DOWN                                                
(B_APP_ERROR_BASE + 18)
+#define B_LAUNCH_BAD_EXECUTABLE                                
(B_APP_ERROR_BASE + 19)
 
 /* Storage Kit/File System Errors */
 #define B_FILE_ERROR                                           
(B_STORAGE_ERROR_BASE + 0)
diff --git a/headers/private/system/pe_common.h 
b/headers/private/system/pe_common.h
new file mode 100644
index 0000000..59d464d
--- /dev/null
+++ b/headers/private/system/pe_common.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2013-2014, Haiku, Inc.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ *     Alexander von Gluck IV, <kallisti5@xxxxxxxxxxx>
+ */
+#ifndef _PE_COMMON_H
+#define _PE_COMMON_H
+
+
+#include <SupportDefs.h>
+#include <ByteOrder.h>
+
+
+// Magic strings
+#define MZ_MAGIC "MZ"
+#define PE_MAGIC "PE"
+#define PE_OPTIONAL_MAGIC_PE32 0x010b
+#define PE_OPTIONAL_MAGIC_PE32P 0x020b
+
+
+typedef struct {
+       uint16 magic; /* == MZ_MAGIC */
+       uint16 bytesInLastBlock;
+       uint16 blocksInFile;
+       uint16 numRelocations;
+       uint16 headerParagraphs;
+       uint16 minExtraParagraphs;
+       uint16 maxExtraParagraphs;
+       uint16 ss;
+       uint16 sp;
+       uint16 checksum;
+       uint16 ip;
+       uint16 cs;
+       uint16 relocationTableOffset;
+       uint16 overlayNumber;
+       uint16 reserved[4];
+       uint16 oemID;
+       uint16 oemInfo;
+       uint16 reserved2[10];
+       uint32 lfaNew;  // PE Header start addr
+} MzHeader;
+
+typedef struct {
+       uint32 magic; // == PE_MAGIC */
+       uint16 machine;
+       uint16 numberOfSections;
+       uint32 timeDateStamp;
+       uint32 pointerToSymbolTable;
+       uint32 numberOfSymbols;
+       uint16 sizeOfOptionalHeader;
+       uint16 characteristics;
+} PeHeader;
+
+typedef struct {
+       uint16 magic; // == 0x010b - PE32, 0x020b - PE32+ (64 bit)
+       uint8  majorLinkerVersion;
+       uint8  minorLinkerVersion;
+       uint32 sizeOfCode;
+       uint32 sizeOfInitializedData;
+       uint32 sizeOfUninitializedData;
+       uint32 addressOfEntryPoint;
+       uint32 baseOfCode;
+       uint32 baseOfData;
+       uint32 imageBase;
+       uint32 sectionAlignment;
+       uint32 fileAlignment;
+       uint16 majorOperatingSystemVersion;
+       uint16 minorOperatingSystemVersion;
+       uint16 majorImageVersion;
+       uint16 minorImageVersion;
+       uint16 majorSubsystemVersion;
+       uint16 minorSubsystemVersion;
+       uint32 win32VersionValue;
+       uint32 sizeOfImage;
+       uint32 sizeOfHeaders;
+       uint32 checksum;
+       uint16 subsystem;
+       uint16 llCharacteristics;
+       uint32 sizeOfStackReserve;
+       uint32 sizeOfStackCommit;
+       uint32 sizeOfHeapReserve;
+       uint32 sizeOfHeapCommit;
+       uint32 loaderFlags;
+       uint32 numberOfRvaAndSizes;
+} Pe32OptionalHeader;
+
+#endif /* _PE_COMMON_H */
diff --git a/src/kits/app/Roster.cpp b/src/kits/app/Roster.cpp
index 051d200..82e0071 100644
--- a/src/kits/app/Roster.cpp
+++ b/src/kits/app/Roster.cpp
@@ -2235,6 +2235,8 @@ BRoster::_LaunchApp(const char* mimeType, const 
entry_ref* ref,
                                        team = threadInfo.team;
                        } else if (wasDocument && appThread == 
B_NOT_AN_EXECUTABLE)
                                error = B_LAUNCH_FAILED_EXECUTABLE;
+                       else if (wasDocument && appThread == 
B_BAD_EXECUTABLE_FORMAT)
+                               error = B_LAUNCH_BAD_EXECUTABLE;
                        else
                                error = appThread;
 
diff --git a/src/kits/tracker/FSUtils.cpp b/src/kits/tracker/FSUtils.cpp
index 63eda73..5b4f9b0 100644
--- a/src/kits/tracker/FSUtils.cpp
+++ b/src/kits/tracker/FSUtils.cpp
@@ -3514,8 +3514,12 @@ _TrackerLaunchDocuments(const entry_ref* /*doNotUse*/, 
const BMessage* refs,
                        openWithOK = false;
                        openedDocuments = false;
                }
-
-               if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) {
+               if (error == B_LAUNCH_BAD_EXECUTABLE && !refsToPass) {
+                       // We know it's an executable, but something unsupported
+                       alertString.SetTo(B_TRANSLATE("\"%name\" is an 
unsupported "
+                               "executable."));
+                       alertString.ReplaceFirst("%name", app.name);
+               } else if (error == B_LAUNCH_FAILED_EXECUTABLE && !refsToPass) {
                        alertString.SetTo(B_TRANSLATE("Could not open 
\"%name\". "
                                "The file is mistakenly marked as executable. 
"));
                        alertString.ReplaceFirst("%name", app.name);
diff --git a/src/system/runtime_loader/Jamfile 
b/src/system/runtime_loader/Jamfile
index 06d9b56..3b4a802 100644
--- a/src/system/runtime_loader/Jamfile
+++ b/src/system/runtime_loader/Jamfile
@@ -76,6 +76,7 @@ local sources =
        elf_load_image.cpp
        elf_symbol_lookup.cpp
        elf_versioning.cpp
+       pe.cpp
        errors.cpp
        export.cpp
        heap.cpp
diff --git a/src/system/runtime_loader/pe.cpp b/src/system/runtime_loader/pe.cpp
new file mode 100644
index 0000000..5da7758
--- /dev/null
+++ b/src/system/runtime_loader/pe.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013-2014, Haiku, Inc.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ *     Alexander von Gluck IV, <kallisti5@xxxxxxxxxxx>
+ */
+
+
+#include "pe.h"
+
+#include <ctype.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+static status_t
+parse_mz_header(MzHeader* mzHeader, off_t* peOffset)
+{
+       if (memcmp(&mzHeader->magic, MZ_MAGIC, 2) != 0)
+               return B_NOT_AN_EXECUTABLE;
+
+       *peOffset = (off_t)mzHeader->lfaNew;
+       return B_OK;
+}
+
+
+static status_t
+parse_pe_header(PeHeader* peHeader)
+{
+       if (memcmp(&peHeader->magic, PE_MAGIC, 2) != 0)
+               return B_NOT_AN_EXECUTABLE;
+
+       // Legacy BeOS x86 PE binary
+       // Every R3 binary I can find sets characteristics to 10E
+       // peHeader->characteristics == 0x10E
+
+       return B_OK;
+}
+
+
+/*! Read and verify the PE header */
+status_t
+pe_verify_header(void *header, size_t length)
+{
+       if (length < sizeof(MzHeader))
+               return B_NOT_AN_EXECUTABLE;
+
+       // Verify MZ header, pull PE header offset
+       off_t peOffset = 0;
+       if (parse_mz_header((MzHeader*)header, &peOffset) != B_OK)
+               return B_NOT_AN_EXECUTABLE;
+
+       // MS-DOS program
+       if (peOffset == 0)
+               return B_BAD_EXECUTABLE_FORMAT;
+
+       // Something is wrong with the binary
+       if (peOffset + sizeof(PeHeader) > length)
+               return B_BAD_EXECUTABLE_FORMAT;
+
+       // Find the PE header based on MZ provided offset
+       uint8* pePtr = (uint8*)header;
+       pePtr += peOffset;
+
+       // Win32 program or old BeOS R3 x86 program
+       return parse_pe_header((PeHeader*)pePtr);
+}
diff --git a/src/system/runtime_loader/pe.h b/src/system/runtime_loader/pe.h
new file mode 100644
index 0000000..94a365e
--- /dev/null
+++ b/src/system/runtime_loader/pe.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013-2014, Haiku, Inc.
+ * Distributed under the terms of the MIT License.
+ *
+ * Authors:
+ *  Alexander von Gluck IV, <kallisti5@xxxxxxxxxxx>
+ */
+#ifndef PE_H
+#define PE_H
+
+
+#include <OS.h>
+
+#include <syscalls.h>
+#include <util/kernel_cpp.h>
+
+#include "pe_common.h"
+
+
+status_t pe_verify_header(void *header, size_t length);
+
+
+#endif /* PE_H */
diff --git a/src/system/runtime_loader/runtime_loader.cpp 
b/src/system/runtime_loader/runtime_loader.cpp
index 8864ef3..ed3657e 100644
--- a/src/system/runtime_loader/runtime_loader.cpp
+++ b/src/system/runtime_loader/runtime_loader.cpp
@@ -24,6 +24,7 @@
 #include <vm_defs.h>
 
 #include "elf_symbol_lookup.h"
+#include "pe.h"
 
 
 struct user_space_program_args *gProgramArgs;
@@ -396,8 +397,8 @@ test_executable(const char *name, char *invoker)
 
        status = elf_verify_header(buffer, length);
        if (status == B_NOT_AN_EXECUTABLE) {
-               // test for shell scripts
                if (!strncmp(buffer, "#!", 2)) {
+                       // test for shell scripts
                        char *end;
                        buffer[min_c((size_t)length, sizeof(buffer) - 1)] = 
'\0';
 
@@ -414,6 +415,13 @@ test_executable(const char *name, char *invoker)
                        }
 
                        status = B_OK;
+               } else {
+                       // Something odd like a PE?
+                       status = pe_verify_header(buffer, length);
+
+                       // It is a PE, throw B_BAD_EXECUTABLE_FORMAT
+                       if (status == B_OK)
+                               status = B_BAD_EXECUTABLE_FORMAT;
                }
        } else if (status == B_OK) {
                elf_ehdr *elfHeader = (elf_ehdr *)buffer;

Other related posts: