[Top][All Lists]
[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
+