[Bug c++/39209] New: Code generated for calling pointer-to-member segfaults when address of p-t-m is not 16-bit aligned
james at jamesmolloy dot co dot uk
gcc-bugzilla@gcc.gnu.org
Mon Feb 16 22:47:00 GMT 2009
(Please note that in all the following code no standard libaries are linked -
this is for a bare-metal kernel project.)
The following code (full header/assembly attached below):
Iterator &operator ++ ()
{
m_Node = (m_Node->*FunctionNext)();
return *this;
}
When instantiated as:
template<typename T>
struct _ListNode_t
{
/** Get the next data structure in the list
*\return pointer to the next data structure in the list */
_ListNode_t *next()
{return m_Next;}
/** Get the previous data structure in the list
*\return pointer to the previous data structure in the list */
_ListNode_t *previous()
{return m_Previous;}
/** Pointer to the next node */
_ListNode_t *m_Next;
/** Pointer to the previous node */
_ListNode_t *m_Previous;
/** The value of the node */
T value;
};
** List template specialisation for void*
*\brief List template specialisation for void* */
template<>
class List<void*>
{
/** The data structure of the list's nodes */
typedef _ListNode_t<void*> node_t;
public:
/** Type of the bidirectional iterator */
typedef ::Iterator<void*, node_t> Iterator;
...
Produces the following (dis)assembly:
00000000
<_ZN8IteratorIPv11_ListNode_tIS0_EXadL_ZNS2_8previousEvEEXadL_ZNS2_4nextEvEES0_EppEv>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 08 sub $0x8,%esp
6: b8 00 00 00 00 mov $0x0,%eax
7: R_386_32 _ZN11_ListNode_tIPvE4nextEv
b: 83 e0 01 and $0x1,%eax
e: 84 c0 test %al,%al
10: 74 16 je 28
<_ZN8IteratorIPv11_ListNode_tIS0_EXadL_ZNS2_8previousEvEEXadL_ZNS2_4nextEvEES0_EppEv+0x28>
12: 8b 45 08 mov 0x8(%ebp),%eax
15: 8b 00 mov (%eax),%eax
17: 8b 10 mov (%eax),%edx
19: b8 ff ff ff ff mov $0xffffffff,%eax
1a: R_386_32 _ZN11_ListNode_tIPvE4nextEv
1e: 8d 04 02 lea (%edx,%eax,1),%eax
21: 8b 00 mov (%eax),%eax
23: 89 45 fc mov %eax,-0x4(%ebp)
26: eb 07 jmp 2f
<_ZN8IteratorIPv11_ListNode_tIS0_EXadL_ZNS2_8previousEvEEXadL_ZNS2_4nextEvEES0_EppEv+0x2f>
28: c7 45 fc 00 00 00 00 movl $0x0,-0x4(%ebp)
2b: R_386_32 _ZN11_ListNode_tIPvE4nextEv
2f: 8b 45 08 mov 0x8(%ebp),%eax
32: 8b 00 mov (%eax),%eax
34: 83 ec 0c sub $0xc,%esp
37: 50 push %eax
38: ff 55 fc call *-0x4(%ebp)
3b: 83 c4 10 add $0x10,%esp
3e: 89 c2 mov %eax,%edx
40: 8b 45 08 mov 0x8(%ebp),%eax
43: 89 10 mov %edx,(%eax)
45: 8b 45 08 mov 0x8(%ebp),%eax
48: c9 leave
49: c3 ret
Notice that the AND instruction tests the least significant bit of the address
of ListNode::next(). The code path changes depending on this - if it is zero,
that is the address is 16-bit aligned, the second code path is taken which
works fine.
If the address is not 16-bit aligned, the first path is taken, and it is on
taking that path that a segfault (well, actually page fault for me as I'm on
bare metal) occurs, on the dereference in the MOV after the LEA.
At the point of the LEA, %eax contains the address of ListNode::next(), and
%edx contains what appears to be a valid address on my kernel heap. This is
seemingly being used as an offset, but with bad consequences. The code fails
here.
I can only get the code to fail when ListNode::next() is not 16-bit aligned.
This code worked perfectly in GCC 4.3.0, and in that version of the compiler
the AND/JE pair was not generated - the only code path is the second one.
Is this deliberate? Is it a regression?
Thanks,
James Molloy
------------------------------------------------------
<Iterator.h>
/*
* Copyright (c) 2008 James Molloy, Jörg Pfähler, Matthew Iselin
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef KERNEL_UTILITIES_ITERATOR_H
#define KERNEL_UTILITIES_ITERATOR_H
#include <Log.h>
/** @addtogroup kernelutilities
* @{ */
/** General iterator for structures that provide functions for the next and
previous structure
* in the datastructure and a "value" member. This template provides a
bidirectional, a constant
* bidirectional, a reverse bidirectional and a constant reverse bidirectional
iterator.
*\brief An iterator applicable for many data structures
*\param[in] originalT the original element type of the iterator
*\param[in] Struct the datastructure that provides functions for the
next/previous datastructure
* and a "value" member
*\param[in] previous pointer to the member function used to iterate forward
*\param[in] next pointer to the member function used to iterate backwards
*\param[in] T the real element type of the iterator */
template<typename originalT,
class Struct,
Struct *(Struct::*FunctionPrev)() = &Struct::previous,
Struct *(Struct::*FunctionNext)() = &Struct::next,
typename T = originalT>
class Iterator
{
/** All iterators must be friend in order to allow casts between some
iterator types */
template<typename _originalT,
class _Struct,
_Struct *(_Struct::*_FunctionPrev)(),
_Struct *(_Struct::*_FunctionNext)(),
typename _T>
friend class Iterator;
/** The assignment operator is extern */
template<typename _originalT,
class _Struct,
_Struct *(_Struct::*_FunctionPrev)(),
_Struct *(_Struct::*_FunctionNext)(),
typename _T1,
typename _T2>
friend bool operator == (const Iterator<_originalT, _Struct, _FunctionPrev,
_FunctionNext, _T1> &x1,
const Iterator<_originalT, _Struct, _FunctionPrev,
_FunctionNext, _T2> &x2);
public:
/** Type of the constant bidirectional iterator */
typedef Iterator<originalT, Struct, FunctionPrev, FunctionNext, T const>
Const;
/** Type of the reverse iterator */
typedef Iterator<originalT, Struct, FunctionNext, FunctionPrev, T>
Reverse;
/** Type of the constant reverse iterator */
typedef Iterator<originalT, Struct, FunctionNext, FunctionPrev, T const>
ConstReverse;
/** The default constructor constructs an invalid/unusable iterator */
Iterator()
: m_Node(){}
/** The copy-constructor
*\param[in] Iterator the reference object */
Iterator(const Iterator &x)
: m_Node(x.m_Node){}
/** The constructor
*\param[in] Iterator the reference object */
template<typename T2>
Iterator(const Iterator<originalT, Struct, FunctionPrev, FunctionNext, T2>
&x)
: m_Node(x.m_Node){}
/** Constructor from a pointer to an instance of the data structure
*\param[in] Node pointer to an instance of the data structure */
Iterator(Struct *Node)
: m_Node(Node){}
/** The destructor does nothing */
~Iterator(){}
/** The assignment operator
*\param[in] Iterator the reference object */
Iterator &operator = (const Iterator &x)
{
m_Node = x.m_Node;
return *this;
}
/** Preincrement operator */
Iterator &operator ++ ()
{
m_Node = (m_Node->*FunctionNext)();
return *this;
}
/** Predecrement operator */
Iterator &operator -- ()
{
m_Node = (m_Node->*FunctionPrev)();
return *this;
}
/** Dereference operator yields the element value */
T &operator *()
{
return m_Node->value;
}
/** Dereference operator yields the element value */
T &operator ->()
{
return m_Node->value;
}
/** Conversion Operator to a constant iterator */
operator Const ()
{
return Const(m_Node);
}
/** Get the Node */
Struct *__getNode()
{
return m_Node;
}
private:
/** Pointer to the instance of the data structure or 0 */
Struct *m_Node;
};
/** Comparison operator for the Iterator class
*\param[in] x1 the first operand
*\param[in] x2 the second operand
*\return true, if the two iterator point to the same object, false otherwise
*/
template<typename originalT,
class Struct,
Struct *(Struct::*FunctionPrev)(),
Struct *(Struct::*FunctionNext)(),
typename T1,
typename T2>
bool operator == (const Iterator<originalT, Struct, FunctionPrev, FunctionNext,
T1> &x1,
const Iterator<originalT, Struct, FunctionPrev, FunctionNext,
T2> &x2)
{
if (x1.m_Node != x2.m_Node)return false;
return true;
}
/** @} */
#endif
------------------------------------
<List.h>
/*
* Copyright (c) 2008 James Molloy, Jörg Pfähler, Matthew Iselin
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef KERNEL_UTILITIES_LIST_H
#define KERNEL_UTILITIES_LIST_H
#include <utilities/template.h>
#include <processor/types.h>
#include <utilities/IteratorAdapter.h>
#include <utilities/Iterator.h>
#include <Log.h>
/** @addtogroup kernelutilities
* @{ */
/** One node in the list
*\brief One node in the list
*\param[in] T the element type */
template<typename T>
struct _ListNode_t
{
/** Get the next data structure in the list
*\return pointer to the next data structure in the list */
_ListNode_t *next()
{return m_Next;}
/** Get the previous data structure in the list
*\return pointer to the previous data structure in the list */
_ListNode_t *previous()
{return m_Previous;}
/** Pointer to the next node */
_ListNode_t *m_Next;
/** Pointer to the previous node */
_ListNode_t *m_Previous;
/** The value of the node */
T value;
};
template<class T>
class List;
/** List template specialisation for void*
*\brief List template specialisation for void* */
template<>
class List<void*>
{
/** The data structure of the list's nodes */
typedef _ListNode_t<void*> node_t;
public:
/** Type of the bidirectional iterator */
typedef ::Iterator<void*, node_t> Iterator;
/** Type of the constant bidirectional iterator */
typedef Iterator::Const ConstIterator;
/** Type of the reverse iterator */
typedef Iterator::Reverse ReverseIterator;
/** Type of the constant reverse iterator */
typedef Iterator::ConstReverse ConstReverseIterator;
/** Default constructor, does nothing */
List();
/** Copy-constructor
*\param[in] x reference object */
List(const List &x);
/** Destructor, deallocates memory */
~List();
/** Assignment operator
*\param[in] x the object that should be copied */
List &operator = (const List &x);
/** Get the number of elements we reserved space for
*\return number of elements we reserved space for */
size_t size() const;
/** Get the number of elements in the List */
size_t count() const;
/** Add a value to the end of the List
*\param[in] value the value that should be added */
void pushBack(void *value);
/** Remove the last element from the List
*\return the previously last element */
void *popBack();
/** Add a value to the front of the List
*\param[in] value the value that should be added */
void pushFront(void *value);
/** Remove the first element in the List
*\return the previously first element */
void *popFront();
/** Erase an element
*\param[in] iterator the iterator that points to the element */
Iterator erase(Iterator &Iter);
/** Get an iterator pointing to the beginning of the List
*\return iterator pointing to the beginning of the List */
inline Iterator begin()
{
return Iterator(m_First);
}
/** Get a constant iterator pointing to the beginning of the List
*\return constant iterator pointing to the beginning of the List */
inline ConstIterator begin() const
{
return ConstIterator(m_First);
}
/** Get an iterator pointing to the end of the List + 1
*\return iterator pointing to the end of the List + 1 */
inline Iterator end()
{
return Iterator();
}
/** Get a constant iterator pointing to the end of the List + 1
*\return constant iterator pointing to the end of the List + 1 */
inline ConstIterator end() const
{
return ConstIterator();
}
/** Get an iterator pointing to the reverse beginning of the List
*\return iterator pointing to the reverse beginning of the List */
inline ReverseIterator rbegin()
{
return ReverseIterator(m_Last);
}
/** Get a constant iterator pointing to the reverse beginning of the List
*\return constant iterator pointing to the reverse beginning of the List
*/
inline ConstReverseIterator rbegin() const
{
return ConstReverseIterator(m_Last);
}
/** Get an iterator pointing to the reverse end of the List + 1
*\return iterator pointing to the reverse end of the List + 1 */
inline ReverseIterator rend()
{
return ReverseIterator();
}
/** Get a constant iterator pointing to the reverse end of the List + 1
*\return constant iterator pointing to the reverse end of the List + 1 */
inline ConstReverseIterator rend() const
{
return ConstReverseIterator();
}
/** Remove all elements from the List */
void clear();
/** Copy the content of a List into this List
*\param[in] x the reference List */
void assign(const List &x);
private:
/** The number of Nodes/Elements in the List */
size_t m_Count;
/** Pointer to the first Node in the List */
node_t *m_First;
/** Pointer to the last Node in the List */
node_t *m_Last;
};
/** List template specialisation for pointers. Just forwards to the
* void* template specialisation of List.
*\brief List template specialisation for pointers */
template<class T>
class List<T*>
{
public:
/** Iterator */
typedef IteratorAdapter<T*, List<void*>::Iterator>
Iterator;
/** ConstIterator */
typedef IteratorAdapter<T* const, List<void*>::ConstIterator>
ConstIterator;
/** ReverseIterator */
typedef IteratorAdapter<T*, List<void*>::ReverseIterator>
ReverseIterator;
/** ConstReverseIterator */
typedef IteratorAdapter<T* const, List<void*>::ConstReverseIterator>
ConstReverseIterator;
/** Default constructor, does nothing */
inline List()
: m_VoidList(){}
/** Copy-constructor
*\param[in] x reference object */
inline List(const List &x)
: m_VoidList(x.m_VoidList){}
/** Destructor, deallocates memory */
inline ~List()
{}
/** Assignment operator
*\param[in] x the object that should be copied */
inline List &operator = (const List &x)
{
m_VoidList = x.m_VoidList;
return *this;
}
/** Get the number of elements we reserved space for
*\return number of elements we reserved space for */
inline size_t size() const
{
return m_VoidList.size();
}
/** Get the number of elements in the List */
inline size_t count() const
{
return m_VoidList.count();
}
/** Add a value to the end of the List
*\param[in] value the value that should be added */
inline void pushBack(T *value)
{
m_VoidList.pushBack(reinterpret_cast<void*>(const_cast<typename
nonconst_type<T>::type*>(value)));
}
/** Remove the last element from the List
*\return the previously last element */
inline T *popBack()
{
return reinterpret_cast<T*>(m_VoidList.popBack());
}
/** Add a value to the front of the List
*\param[in] value the value that should be added */
inline void pushFront(T *value)
{
m_VoidList.pushFront(reinterpret_cast<void*>(value));
}
/** Remove the first element in the List
*\return the previously first element */
inline T *popFront()
{
return reinterpret_cast<T*>(m_VoidList.popFront());
}
/** Erase an element
*\param[in] iterator the iterator that points to the element */
inline Iterator erase(Iterator &Iter)
{
return Iterator(m_VoidList.erase(Iter.__getIterator()));
}
/** Get an iterator pointing to the beginning of the List
*\return iterator pointing to the beginning of the List */
inline Iterator begin()
{
return Iterator(m_VoidList.begin());
}
/** Get a constant iterator pointing to the beginning of the List
*\return constant iterator pointing to the beginning of the List */
inline ConstIterator begin() const
{
return ConstIterator(m_VoidList.begin());
}
/** Get an iterator pointing to the end of the List + 1
*\return iterator pointing to the end of the List + 1 */
inline Iterator end()
{
return Iterator(m_VoidList.end());
}
/** Get a constant iterator pointing to the end of the List + 1
*\return constant iterator pointing to the end of the List + 1 */
inline ConstIterator end() const
{
return ConstIterator(m_VoidList.end());
}
/** Get an iterator pointing to the reverse beginning of the List
*\return iterator pointing to the reverse beginning of the List */
inline ReverseIterator rbegin()
{
return ReverseIterator(m_VoidList.rbegin());
}
/** Get a constant iterator pointing to the reverse beginning of the List
*\return constant iterator pointing to the reverse beginning of the List
*/
inline ConstReverseIterator rbegin() const
{
return ConstReverseIterator(m_VoidList.rbegin());
}
/** Get an iterator pointing to the reverse end of the List + 1
*\return iterator pointing to the reverse end of the List + 1 */
inline ReverseIterator rend()
{
return ReverseIterator(m_VoidList.rend());
}
/** Get a constant iterator pointing to the reverse end of the List + 1
*\return constant iterator pointing to the reverse end of the List + 1 */
inline ConstReverseIterator rend() const
{
return ConstReverseIterator(m_VoidList.rend());
}
/** Remove all elements from the List */
inline void clear()
{
m_VoidList.clear();
}
/** Copy the content of a List into this List
*\param[in] x the reference List */
inline void assign(const List &x)
{
m_VoidList.assign(x.m_VoidList);
}
private:
/** The actual container */
List<void*> m_VoidList;
};
/** @} */
#endif
--
Summary: Code generated for calling pointer-to-member segfaults
when address of p-t-m is not 16-bit aligned
Product: gcc
Version: 4.3.2
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: c++
AssignedTo: unassigned at gcc dot gnu dot org
ReportedBy: james at jamesmolloy dot co dot uk
GCC build triplet: i686-elf
GCC host triplet: x86_64-linux-gnu
GCC target triplet: i686-elf
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39209
More information about the Gcc-bugs
mailing list