В ветвях PowerPC доступно только 24 бита для целевого смещения, поэтому, если текстовый раздел становится слишком большим, ветви на одном конце не смогут достичь целей на другом. Существует более длинная последовательность инструкций, которые могут достигать целей дальше (смещение 32 бита вместо 24), но GCC не использует его по умолчанию, если вы не передадите его -mlongcall
вариант. Однако, даже если эта опция включена, GCC по-прежнему генерирует короткие вызовы для определенных функций, а именно operator new
а также operator delete
Например, учитывая этот код:
extern void foo();
int main(int argc, char** argv) {
foo();
new char;
}
Обычный прогон GCC сгенерирует сборку:
bl _Z3foov // void foo()
bl _Znwj // operator new(unsigned int)
Запуск GCC с -mlongcall
Опция генерирует:
lis r9, _Z3foov@ha
addi r9, r9, _Z3foov@l
mtctr r9
bctrl
bl _Znwj
Первые четыре инструкции — это длинный звонок foo()
, как и ожидалось, но призыв к operator new
без изменений. Вызовы случайных функций libc и libstdc ++ преобразуются в длинные вызовы, как и ожидалось. Почему operator new
а также operator delete
звонки по-прежнему заканчиваются как bl
инструкции? Есть ли способ заставить GCC совершать длинные звонки? Я использую GCC 4.7.2 на 64-битной машине PowerPC Fedora (хотя я собираю 32-битную)
Похоже, что у цепочки инструментов g ++ есть какая-то ошибка в том, как она вызывает восемь «заменяемых» функций стандартной библиотеки C ++ в вашей архитектуре, если эти функции фактически не заменяются пользовательским кодом.
Переносная реализация замены для всех восьми:
#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);
}
Если мы можем определить эту функцию в #pragma long_calls или объявить атрибут long-call внутри этой функции, мы можем заставить GCC также выполнять их длинные вызовы.
Оформить заказ GCC-варианты.