Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C++

Sign Extension of C++ Pointer: The Cause of NIC’s Failure to Send Packets

Rate me:
Please Sign up or sign in to vote.
4.89/5 (4 votes)
20 Feb 2014CPOL2 min read 10.8K   4   2
Sign Extension of C++ Pointer: The cause of NIC’s failure to send packets

I’ve been working on a NIC driver project on Pharlap. Due to some reason, it can’t send out any packets. I took some time troubleshooting the problem and finally it turned out to be the incorrect setting of the following hardware register.

C++
// Memory-mapped buffer
 struct Registers
 {
    // High 32 bits of transmit descriptor base address
    unsigned long TxDescBaseHi;
    // Low 32 bits of transmit descriptor base address
    unsigned long TxDescBaseLo;
    ..
 };

The register consists of two 32-bit registers which are written by the software driver during initialization to tell the hardware about the starting address of the transmit descriptors. Transmit descriptor is a structure storing the address of the packet to send, along with some control information. The two registers are there to support 64-bit addressing system.

The initialization code is like:

C++
TxDescriptor *m_txRing = m_DMABuffer.Get(SIZE, ALIGNMENT)
..
u64 baseAddr = reinterpret_cast<u64>(m_txRing);
m_pReg->TxDescBaseHi = baseAddr >> 32;
m_pReg->TxDescBaseLo = static_cast<u32>(baseAddr);

The code is simple to be self-describing. However, it’s where the problem is. Since the Pharlap I’m working on is 32-bit, the pointer m_txRing is 4-byte long. Here are the printed values of these variables.

C++
m_txRing: 0x80236400
m_pReg->TxDescBaseHi:0xFFFFFFFF
m_pReg->TxDescBaseLo:0x80236400

As you can see, the value in high register is not what we expect. 32-bit pointer m_txRing is sign extended. In C++/C, converting a pointer to an integer is implementation-defined.

"A pointer can be explicitly converted to any integral type large 
enough to hold it. The mapping function is implementation-defined."
                                                          - C++03

In MSVC/GCC, if you cast a pointer to an integer of a bigger size, the pointer is first sign extended, and the resulting value is then converted to the integer. In this case, 0×80236400 is sign extended to 0xFFFFFFFF80236400, and since it’s in the range of u64, it’s the final value.

The solution is to use uintptr_t. uintptr_t is an alias(typedef) of an unsigned integer which has the same size of a pointer(Note that intptr_t/uintptr_t is optional in C++11). First, convert the pointer to uintptr_t. There is no extension here. Second, convert the result to target integer.

A few takeaways:

  1. Don’t convert between pointer and integer unless it’s essential. When the time comes, use uintptr_t. Convert the pointer to uintptr_t first, to avoid possible sign extension.
  2. reinterpret_cast is not guaranteed by the standard to have the same bit pattern as the original although it’s a common practice. Sign extension in this case, is one place where it doesn’t hold, kind of.
"The mapping performed by reinterpret_cast might, or might not, 
produce a representation different from the original value. "
                                                      - C++03

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Technical Lead National Instruments
China China
Senior software engineer at National Instruments, to implement various Ethernet-based industrial protocols, e.g., EtherCAT. Favorite languages are C/C++ and Python. For fun, I like watching films (sci-fi, motion), walking, and various reading.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Member 43208441-Jan-14 7:42
Member 43208441-Jan-14 7:42 
GeneralRe: My vote of 5 Pin
Eric Z (Jing)1-Jan-14 14:18
Eric Z (Jing)1-Jan-14 14:18 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.