From 8abcf666cb638152634790a882875f52f7f0432b Mon Sep 17 00:00:00 2001
From: stijn <stinos@zoho.com>
Date: Thu, 12 Jun 2014 17:45:41 +0200
Subject: [PATCH] windows: Enable GC and implement bss start and end symbols

The pointers to the bss section are acquired in init.c()
by inspecting the PE header. Works for msvc and mingw.
---
 unix/gccollect.c           | 16 +++++++--
 windows/Makefile           |  2 ++
 windows/bss.c              | 74 ++++++++++++++++++++++++++++++++++++++
 windows/init.c             |  3 ++
 windows/mpconfigport.h     | 19 ++++++++++
 windows/msvc/common.props  |  3 +-
 windows/msvc/sources.props |  2 +-
 7 files changed, 115 insertions(+), 4 deletions(-)
 create mode 100644 windows/bss.c

diff --git a/unix/gccollect.c b/unix/gccollect.c
index 1014a2629..792913821 100644
--- a/unix/gccollect.c
+++ b/unix/gccollect.c
@@ -130,8 +130,11 @@ void gc_collect(void) {
 
     gc_collect_start();
     // this traces the .bss section
-#ifdef __CYGWIN__
+#if defined( __CYGWIN__ )
 #define BSS_START __bss_start__
+#elif defined( _MSC_VER ) || defined( __MINGW32__ )
+#define BSS_START *bss_start
+#define _end *bss_end
 #else
 #define BSS_START __bss_start
 #endif
@@ -141,7 +144,16 @@ void gc_collect(void) {
     regs_t regs;
     gc_helper_get_regs(regs);
     // GC stack (and regs because we captured them)
-    gc_collect_root((void**)&regs, ((machine_uint_t)stack_top - (machine_uint_t)&regs) / sizeof(machine_uint_t));
+#ifdef __MINGW32__
+    // The Mingw cross-compiler on Travis complains
+    // 'warning: dereferencing type-punned pointer will break strict-aliasing rules'
+    // when casting &regs to void** directly so use a union.
+    union { regs_t *r; void **ptr; } cast_regs = { &regs };
+    void **regs_ptr = cast_regs.ptr;
+#else
+    void **regs_ptr = (void**)&regs;
+#endif
+    gc_collect_root(regs_ptr, ((machine_uint_t)stack_top - (machine_uint_t)&regs) / sizeof(machine_uint_t));
     gc_collect_end();
 
     //printf("-----\n");
diff --git a/windows/Makefile b/windows/Makefile
index a188979bd..e3085ff23 100644
--- a/windows/Makefile
+++ b/windows/Makefile
@@ -35,9 +35,11 @@ SRC_C = \
 	unix/file.c \
 	unix/input.c \
 	unix/modtime.c \
+	unix/gccollect.c \
 	realpath.c \
 	init.c \
 	sleep.c \
+	bss.c \
 
 OBJ = $(PY_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
 
diff --git a/windows/bss.c b/windows/bss.c
new file mode 100644
index 000000000..b860c4ee8
--- /dev/null
+++ b/windows/bss.c
@@ -0,0 +1,74 @@
+/*
+* This file is part of the Micro Python project, http://micropython.org/
+*
+* The MIT License (MIT)
+*
+* Copyright (c) 2013, 2014 Damien P. George
+*
+* Permission is hereby granted, free of charge, to any person obtaining a copy
+* of this software and associated documentation files (the "Software"), to deal
+* in the Software without restriction, including without limitation the rights
+* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+* copies of the Software, and to permit persons to whom the Software is
+* furnished to do so, subject to the following conditions:
+*
+* The above copyright notice and this permission notice shall be included in
+* all copies or substantial portions of the Software.
+*
+* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+* THE SOFTWARE.
+*/
+
+#include "mpconfig.h"
+#include "misc.h"
+#include "nlr.h"
+#include "qstr.h"
+#include "obj.h"
+#include <windows.h>
+
+IMAGE_NT_HEADERS *header_from_memory(const char *module) {
+    BYTE *base_addr = (BYTE*)GetModuleHandleA(module);
+    IMAGE_DOS_HEADER *dos_header = (IMAGE_DOS_HEADER*)base_addr;
+    return (IMAGE_NT_HEADERS*)(base_addr + dos_header->e_lfanew);
+}
+
+IMAGE_SECTION_HEADER *find_section(IMAGE_NT_HEADERS *nt_header, const char *name) {
+    int i;
+    IMAGE_SECTION_HEADER *section = IMAGE_FIRST_SECTION(nt_header);
+    for (i = 0; i < nt_header->FileHeader.NumberOfSections; ++i) {
+        if (strcmp((const char *)section->Name, name) == 0) {
+            return section;
+        }
+        ++section;
+    }
+    return NULL;
+}
+
+void section_boundaries(IMAGE_NT_HEADERS *nt_header, IMAGE_SECTION_HEADER *section, char **start, char **end) {
+    if (section == NULL) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "Could not lookup section boundaries"));
+    }
+    *start = (char*)(nt_header->OptionalHeader.ImageBase + section->VirtualAddress);
+    *end = *start + section->Misc.VirtualSize;    
+}
+
+void section_boundaries_from_module(const char *module, const char *section, char **start, char **end) {
+    IMAGE_NT_HEADERS *nt_header = header_from_memory(module);
+    IMAGE_SECTION_HEADER *dsection = find_section(nt_header, section);
+    section_boundaries(nt_header, dsection, start, end);   
+}
+
+char *bss_start = 0;
+char *bss_end = 0;
+
+//MSVC has no __bss_start and _end but we can get accurate section info from the PE header.
+//The standard .bss section is appended to the standard .data section however so it cannot
+//be looked up by name. To deal with that we put all uPy static variables in a named section.
+void getbss() {
+    section_boundaries_from_module(NULL, MICROPY_PORT_BSSSECTION, &bss_start, &bss_end);
+}
diff --git a/windows/init.c b/windows/init.c
index a370c464e..57f349ef8 100644
--- a/windows/init.c
+++ b/windows/init.c
@@ -28,9 +28,12 @@
 #include <stdio.h>
 #include <windows.h>
 
+extern void getbss();
+
 HANDLE hSleepEvent = NULL;
 
 void init() {
+    getbss();
     hSleepEvent = CreateEvent(NULL, TRUE, FALSE, FALSE);
 #ifdef __MINGW32__
     putenv("PRINTF_EXPONENT_DIGITS=2");
diff --git a/windows/mpconfigport.h b/windows/mpconfigport.h
index b9a50b084..c930fe95e 100644
--- a/windows/mpconfigport.h
+++ b/windows/mpconfigport.h
@@ -43,6 +43,12 @@
 #define MICROPY_PY_CMATH            (1)
 #define MICROPY_PY_SYS_STDFILES     (1)
 #define MICROPY_PY_SYS_EXIT         (1)
+#define MICROPY_ENABLE_GC           (1)
+#define MICROPY_ENABLE_FINALISER    (1)
+#define MICROPY_PY_GC_COLLECT_RETVAL (1)
+#ifdef _MSC_VER
+#define MICROPY_GCREGS_SETJMP       (1)
+#endif
 #define MICROPY_FLOAT_IMPL          (MICROPY_FLOAT_IMPL_DOUBLE)
 #define MICROPY_LONGINT_IMPL        (MICROPY_LONGINT_IMPL_MPZ)
 #define MICROPY_PORT_INIT_FUNC      init()
@@ -113,6 +119,14 @@ void msec_sleep(double msec);
 #define S_ISDIR(m)                  (((m) & S_IFMT) == S_IFDIR)
 
 
+// Put static/global variables in sections with a known name we can lookup for the GC
+// For this to work this header must be included by all sources, which is the case normally
+#define MICROPY_PORT_DATASECTION "upydata"
+#define MICROPY_PORT_BSSSECTION "upybss"
+#pragma data_seg(MICROPY_PORT_DATASECTION)
+#pragma bss_seg(MICROPY_PORT_BSSSECTION)
+
+
 // System headers (needed e.g. for nlr.h)
 
 #include <stddef.h> //for NULL
@@ -122,3 +136,8 @@ void msec_sleep(double msec);
 
 int snprintf(char *dest, size_t count, const char *format, ...);
 #endif
+
+// MingW specifics
+#ifdef __MINGW32__
+#define MICROPY_PORT_BSSSECTION ".bss"
+#endif
diff --git a/windows/msvc/common.props b/windows/msvc/common.props
index 300de46a5..b6f22c615 100644
--- a/windows/msvc/common.props
+++ b/windows/msvc/common.props
@@ -16,7 +16,8 @@
     </ClCompile>
     <Link>
       <GenerateDebugInformation>true</GenerateDebugInformation>
+      <GenerateMapFile>true</GenerateMapFile>
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup />
-</Project>
\ No newline at end of file
+</Project>
diff --git a/windows/msvc/sources.props b/windows/msvc/sources.props
index 8af03e756..6fd3306b9 100644
--- a/windows/msvc/sources.props
+++ b/windows/msvc/sources.props
@@ -5,7 +5,7 @@
   </PropertyGroup>
   <ItemGroup>
     <ClCompile Include="$(PyBaseDir)py\*.c" />
-    <ClCompile Include="$(PyBaseDir)unix\*.c" Exclude="$(PyBaseDir)unix\modffi.c;$(PyBaseDir)unix\modsocket.c" />
+    <ClCompile Include="$(PyBaseDir)unix\*.c" Exclude="$(PyBaseDir)unix\modffi.c;$(PyBaseDir)unix\modsocket.c;$(PyBaseDir)unix\seg_helpers.c" />
     <ClCompile Include="$(PyBaseDir)windows\*.c" />
     <ClCompile Include="$(PyBaseDir)windows\msvc\*.c" />
   </ItemGroup>
-- 
GitLab