grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Can grub2 support c++ module? (Patch)


From: Christian Franke
Subject: Re: Can grub2 support c++ module? (Patch)
Date: Wed, 25 Jun 2008 20:25:46 +0200
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.11) Gecko/20071128 SeaMonkey/1.1.7

Yoshinori K. Okuji wrote:
On Wednesday 18 June 2008 06:09:10 Pavel Roskin wrote:
On Wed, 2008-06-18 at 10:22 +0800, y.volta wrote:
Hi,

    I just wondering, can grub2 support module coded with c++? Let's
suppose we are trying to apply the fancy menu. ;-)
I think it can be done, but you'll need to disable exception handling
and RTTI.  You won't be able to use any C++ libraries.  The build system
will need to be changed to call C++ compiler when needed.

Still, I think it would be an overkill.  C is quite good for large
projects if used properly.  Linux is fine with C.  C also has checkers
such as sparse, but I'm not aware of C++ equivalents.

When Vesa tried C++, the binary size got bloated, so it was abandoned. I am afraid that C++ adds too much overhead.


Hi,

With exceptions/RTTI turned off and careful use of C++ features, C++ size overhead is close to zero. Even some overhead does not matter for modules which are typically not included into core.img.

The problem using C++ in portable stand-alone programs like grub are the 'object machine' runtime support functions which differ between platforms and gcc releases.

But if new/delete are replaced in the standard way and calls to other runtime support (like global/static ctors/dtors) are avoided, a reasonable C++ subset remains.

The following C++ subset can be used:
+ classes, inheritance, virtual functions, virtual inheritance
+ objects allocated on stack, by new, and by placement new.
+ templates

The following cannot be used:
- global or static objects
- try/catch/throw, dynamic_cast, RTTI
- most of STL
- nested functions (gcc supports these for C only)

See attached patch for a working example. It adds some tests for basic C++ functionality to hello/hello.c. Some C++ support is provided by new includes mm.hpp and dl.hpp. No additions to kernel should be necessary (there might be some minor issues with kern/dl.c due to new symbol types).

For testing purposes, hello.c can be compiled as a C++ source using:

$ make hello_mod-hello_hello.o \
   hello_mod_CFLAGS='-x c++ -fno-exceptions -fno-rtti $(COMMON_CFLAGS)' \
  && make

I also would appreciate support for grub modules written in C++.

Christian

diff -ruN grub2.orig/hello/hello.c grub2/hello/hello.c
--- grub2.orig/hello/hello.c    2007-07-22 01:32:21.000000000 +0200
+++ grub2/hello/hello.c 2008-06-25 19:51:30.907250000 +0200
@@ -18,25 +18,164 @@
  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+
+// Include C++ support first
+#include <grub/dl.hpp>
+#include <grub/mm.hpp>
+
+extern "C" { // TODO: add extern "C" to all includes [__cplusplus]
 #include <grub/types.h>
 #include <grub/misc.h>
 #include <grub/mm.h>
 #include <grub/err.h>
 #include <grub/dl.h>
 #include <grub/normal.h>
+}
+
+
+// Class to set and restore grub_mm_debug in a block
+
+class do_mm_debug
+{
+#ifdef MM_DEBUG
+public:
+  explicit do_mm_debug (bool debug = true)
+    : m_debug_old (grub_mm_debug)
+    { grub_mm_debug = (int)debug; }
+  
+  ~do_mm_debug ()
+    { grub_mm_debug = m_debug_old; }
+
+private:
+  int m_debug_old;
+#else
+
+public:
+  do_mm_debug () { }
+  explicit do_mm_debug (bool) { }
+#endif
+};
+
+
+
+// Classes to test single inheritance and vtable
+
+class A
+{
+public:
+  explicit A (const char * name);
+  virtual ~A ();
+
+  virtual void hello () const;
+
+protected:
+  char * m_name;
+};
+
+class B : public A
+{
+public:
+  explicit B (const char * name);
+  virtual ~B ();
+
+  virtual void hello () const;
+};
+
+
+
+A::A (const char * name)
+: m_name (new char[grub_strlen (name) + 1]) // calls grub_fatal on failure
+{
+  grub_strcpy (m_name, name);
+  grub_printf ("%sA::A ()\n", m_name);
+}
+
+A::~A ()
+{
+  grub_printf ("%sA::~A ()\n", m_name);
+  delete [] m_name;
+}
+
+void A::hello () const
+{
+  grub_printf ("%sA::hello (): Something wrong!\n", m_name);
+}
+
+
+B::B (const char * name)
+: A (name)
+{
+  grub_printf ("%sB::B ()\n", m_name);
+}
+
+B::~B ()
+{
+  grub_printf ("%sB::~B ()\n", m_name);
+}
+
+void B::hello () const
+{
+  grub_printf ("%sB::hello (): Hello C++ World\n", m_name);
+}
+
+
+// Globals
+
+//static B gb ("gb."); // NOT SUPPORTED
+
+static char gb_mem[sizeof (B)];
+B & gb = *(B *)gb_mem;  // ctor called by placement new
+
+A * pa = 0;
+
+
+static void
+say_hello (const A & a)
+{
+  a.hello ();
+}
+
 
 static grub_err_t
 grub_cmd_hello (struct grub_arg_list *state __attribute__ ((unused)),
                int argc __attribute__ ((unused)),
                char **args __attribute__ ((unused)))
 {
-  grub_printf ("Hello World\n");
-  return 0;
+  do_mm_debug here;
+
+  // call virtual function B::hello () for all objects
+
+  say_hello (gb); // global object
+
+  if (pa)
+    {
+      say_hello (*pa); // global pointer (actually B *)
+      delete pa;
+      pa = 0;
+    }
+
+  //static B sb ("sb."); // NOT SUPPORTED
+  //say_hello (&sb);
+
+  B vb ("vb.");
+  say_hello (vb); // local object
+
+  say_hello ( B("_temp_B.") ); // temporary object
+
+  return GRUB_ERR_NONE;
 }
 
 GRUB_MOD_INIT(hello)
 {
-  (void)mod;                   /* To stop warning. */
+  {
+    do_mm_debug here;
+
+    pa = new (std::nothrow) B ("pa->");
+    if (! pa)
+      return;
+
+    new (gb_mem) B ("gb."); // returns &gb
+  }
   grub_register_command ("hello", grub_cmd_hello, GRUB_COMMAND_FLAG_BOTH,
                         "hello", "Say hello", 0);
 }
@@ -44,4 +183,11 @@
 GRUB_MOD_FINI(hello)
 {
   grub_unregister_command ("hello");
+
+  do_mm_debug here;
+
+  gb.~B();
+
+  delete pa;
+  pa = 0;
 }
diff -ruN grub2.orig/include/grub/dl.hpp grub2/include/grub/dl.hpp
--- grub2.orig/include/grub/dl.hpp      1970-01-01 01:00:00.000000000 +0100
+++ grub2/include/grub/dl.hpp   2008-06-25 11:23:48.000000000 +0200
@@ -0,0 +1,37 @@
+// GRUB2 C++ GRUB_MOD_INIT/FINI
+
+#ifndef GRUB_DL_HPP
+#define GRUB_DL_HPP 1
+
+extern "C" {
+  #include <grub/dl.h> // GRUB_MOD_INIT/FINI
+}
+
+
+// TODO: Add this to dl.h [__cplusplus]
+
+#undef GRUB_MOD_INIT
+#undef GRUB_MOD_FINI
+
+#define GRUB_MOD_INIT(name)    \
+extern "C" { \
+  static void grub_mod_init (grub_dl_t mod __attribute__ ((unused))) 
__attribute__ ((used)); \
+  void grub_##name##_init (void); \
+} \
+void \
+grub_##name##_init (void) { grub_mod_init (0); } \
+static void \
+grub_mod_init (grub_dl_t mod __attribute__ ((unused)))
+
+#define GRUB_MOD_FINI(name)    \
+extern "C" { \
+  static void grub_mod_fini (void) __attribute__ ((used)); \
+  void grub_##name##_fini (void); \
+} \
+void \
+grub_##name##_fini (void) { grub_mod_fini (); } \
+static void \
+grub_mod_fini (void)
+
+#endif
+
diff -ruN grub2.orig/include/grub/mm.hpp grub2/include/grub/mm.hpp
--- grub2.orig/include/grub/mm.hpp      1970-01-01 01:00:00.000000000 +0100
+++ grub2/include/grub/mm.hpp   2008-06-25 13:34:00.000000000 +0200
@@ -0,0 +1,102 @@
+// GRUB2 C++ memory management
+
+#ifndef GRUB_MM_HPP
+#define GRUB_MM_HPP 1
+
+extern "C" {
+  #include <grub/mm.h>
+}
+
+
+// TODO: Add grub_malloc_nonull () to mm.c ???
+
+static void *
+grub_malloc_nonull (grub_size_t size)
+{
+  void * p = grub_malloc (size);
+  if (! p)
+    grub_fatal ("out of memory"); // no-exceptions, sorry
+  return p;
+}
+
+
+// TODO: add new/delete to <grub/mm.h> [__cplusplus]
+
+// Standard (throw) new
+
+inline void *
+operator new (grub_size_t size)
+{
+  return grub_malloc_nonull (size);
+}
+
+inline void *
+operator new[] (grub_size_t size)
+{
+  return grub_malloc_nonull (size);
+}
+
+
+// Standard nothrow new
+
+namespace std {
+  enum nothrow_t { nothrow };
+}
+
+inline void *
+operator new (grub_size_t size, std::nothrow_t /*mode*/) throw ()
+{
+  return grub_malloc (size);
+}
+
+inline void *
+operator new[] (grub_size_t size, std::nothrow_t /*mode*/) throw ()
+{
+  return grub_malloc (size);
+}
+
+
+// Standard delete
+
+inline void
+operator delete (void * ptr) throw ()
+{
+  grub_free (ptr);
+}
+
+inline void
+operator delete[] (void * ptr) throw ()
+{
+  grub_free (ptr);
+}
+
+
+// Placement new
+
+inline void *
+operator new (grub_size_t /*size*/, void * here) throw ()
+{
+  return here;
+}
+
+inline void *
+operator new[] (grub_size_t /*size*/, void * here) throw ()
+{
+  return here;
+}
+
+
+// Placement delete (not used if no-exceptions)
+
+inline void
+operator delete (void * /*ptr*/, void * /*here*/) throw ()
+{
+}
+
+inline void
+operator delete[] (void * /*ptr*/, void * /*here*/) throw ()
+{
+}
+
+#endif
+

reply via email to

[Prev in Thread] Current Thread [Next in Thread]