PowerPC branches only have 24 bits available for the target offset, so if the text section gets too big, branches on one end won't be able to reach targets on the other. There's a longer sequence of instructions that can reach targets farther away (the offset is 32 bits instead of 24), but GCC doesn't use it by default unless you pass it the -mlongcall
option. However, even with this option on, GCC still generates short calls for certain functions, namely operator new
and operator delete
For example, given this code:
extern void foo();
int main(int argc, char** argv) {
foo();
new char;
}
A normal run of GCC will generate the assembly:
bl _Z3foov // void foo()
bl _Znwj // operator new(unsigned int)
Running GCC with the -mlongcall
option generates:
lis r9, _Z3foov@ha
addi r9, r9, _Z3foov@l
mtctr r9
bctrl
bl _Znwj
The first four instructions are a long call to foo()
, as expected, but the call to operator new
is unchanged. Calls to random libc and libstdc++ functions are all converted to long calls as expected. Why do operator new
and operator delete
calls still end up as bl
instructions? Is there any way to force GCC to make them long calls as well? I'm using GCC 4.7.2 on a 64-bit PowerPC Fedora machine (although I'm building 32-bit)
It seems the g++ toolchain has some sort of bug in how it calls the eight "replaceable" functions of the C++ Standard Library on your architecture, if those functions are not in fact replaced by user code.
A portable replacement implementation for all eight is:
#include <memory>
#include <cstdlib>
// May never return a null pointer.
void* operator new(std::size_t size) {
void* p = std::malloc(size, 1);
while (!p) {
std::new_handler handler = std::get_new_handler();
if (handler) {
handler();
} else {
throw std::bad_alloc();
}
// A handler is only allowed to return if it did something to make more
// memory available, so try again.
p = std::malloc(size, 1);
}
return p;
}
void operator delete(void* p) noexcept {
if (p) std::free(p);
}
void* operator new(std::size_t size, const std::nothrow_t&) noexcept {
void* p = nullptr;
try {
p = operator new(size);
} catch(...) {}
return p;
}
void operator delete(void* p, const std::nothrow_t&) noexcept {
operator delete(p);
}
// May never return a null pointer.
void* operator new[](std::size_t size) {
return operator new(size);
}
void operator delete[](void* p) noexcept {
operator delete(p);
}
void* operator new[](std::size_t size, const std::nothrow_t& nt) noexcept {
return operator new(size, nt);
}
void operator delete[](void* p, const std::nothrow_t& nt) noexcept {
operator delete(p, nt);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With