#c
Вопрос:
Я работал над этой простой ОС для любителей, и я решил добавить некоторую поддержку C . Вот простой сценарий, который я написал. Когда я его компилирую, я получаю это сообщение:
cp.o: In function `caller':
test.cpp:(.text 0x3a): undefined reference to `__stack_chk_fail'
Вот сценарий:
class CPP {
public:
int a;
void test(void);
};
void CPP::test(void) {
// Code here
}
int caller() {
CPP caller;
caller.test();
return CPP.a;
}
Комментарии:
1. Для C требуется
main
функция.2. В этом случае он будет разглагольствовать о несуществующей функции входа
3. @Lala5th не обязательно, например, может быть так, что определение
main
также заставляет компилятор включать некоторые другие символы запуска, а другой не был найден первым. И/или операция не дала полного результата4. Показать команду сборки
5. @NathanOliver это часть сценария в ядре моей ОС, который я пишу. Я попробую добавить основную функцию.
Ответ №1:
Попробуй вот так.
class CPP {
public:
int a;
void test(void);
};
void CPP::test(void) {
CPP::a = 4;
}
int caller() {
CPP caller;
caller.test();
return caller.a;
}
int main(){
int called = caller();
std::cout << called << std::endl;
return 0;
}
Комментарии:
1. разве CPP::a не приведет к ошибке компилятора, поскольку он не является статическим членом?
2. @Lala5th вы можете квалифицировать нестатические элементы внутри функции-члена, это просто избыточно
3. Нет, этого не произойдет, так как «a» является общедоступной переменной
4. Я не могу использовать <iostream>, так как это ядро, которое я разрабатываю
Ответ №2:
Мне кажется, что компоновщик, который вы используете, не может найти библиотеку, содержащую функцию безопасности, которая приводит к сбою программы при обнаружении разбиения стека. (Может быть, компилятор по какой-то причине не включает объявление функции? Я не знаком с тем, кто на самом деле игнорирует эту конкретную функцию.) Попробуйте скомпилировать с помощью-fno-stack-protector или его эквивалента.
Какой используется компилятор? Обходным путем может быть определение функции как чего-то вроде exit(1) или аналогичного. Это дало бы желаемый эффект, но на данный момент решило бы проблему.
Я создал тестовую программу, чтобы показать, как это на самом деле происходит. Испытательная программа:
int main(){
int a[50]; // To have the compiler manage the stack
return 0;
}
С помощью только -O0
в качестве флага гидра декомпилирует это в:
undefined8 main(void){
long in_FS_OFFSET;
if (*(long *)(in_FS_OFFSET 0x28) != *(long *)(in_FS_OFFSET 0x28)) {
/* WARNING: Subroutine does not return */
__stack_chk_fail();
}
return 0;
}
С -fno-stack-protector
:
undefined8 main(void){
return 0;
}
Массив был выброшен гидрой при декомпиляции, но мы видим, что защита стека отсутствует, если вы используете флаг. В гидре также есть некоторые запутанные части этого (например int->undefined8
), но это стандартно при декомпиляции.
Последствия использования флага
Компиляция без защиты стека сама по себе не очень хороша, но это не должно сильно повлиять на вас. Если вы напишете какой-нибудь код (о котором вам кричит компилятор), вы можете создать переполняемую буфером программу, что не должно быть такой большой проблемой в моем выборе.
Альтернатива
В качестве альтернативы взгляните на это. Они говорят о встроенных системах, но тема кажется подходящей.
Почему там код
Посмотрите, как разбивается стопка, но, насколько мне известно, я попытаюсь объяснить. Когда программа вводит функцию ( main
в данном случае), она сохраняет местоположение следующей инструкции в стеке. Если вы пишете ОС, вы, вероятно, знаете, что такое стек, но для полноты картины: стек-это просто некоторая память, в которую вы можете вставлять и из которой вы можете вставлять данные. Ты всегда вытаскиваешь последнюю сдвинутую вещь из стопки. C и другие языки также используют стек как способ хранения локальных переменных. Стек находится в конце памяти, и когда вы что-то нажимаете, новая вещь будет двигаться дальше вперед, а не назад, она заполняется «назад». Вы можете инициализировать буферы как локальную переменную, например char[20]
. Если вы заполнили буфер, не проверив длину, вы можете переполнить его и перезаписать вещи в стеке, отличные от буфера. Обратный адрес следующей инструкции также находится в стеке. Итак, если у нас есть такая программа, как эта:
int test(){
int a;
char buffer[20];
int c;
// someCode;
}
Тогда стек будет выглядеть примерно так в каком-то коде:
[ Unused space, c, buffer[0], buffer[1] ..., buffer[19], a, Return Address, variables of calling function ]
Теперь, если я заполнил буфер без проверки длины, я могу перезаписать a
(что является проблемой, поскольку я могу изменить способ работы программы) или даже обратный адрес (что является серьезным недостатком, поскольку я мог бы выполнить вредоносный шелл-код, введя его в буфер). Чтобы избежать этого, компиляторы вставляют «файл cookie стека» между a
и обратным адресом. Если эта переменная изменена, то программа должна завершиться перед вызовом return, и именно это __stack_chk_fail()
это для. Похоже, что он также определен в какой-то библиотеке, поэтому вы, возможно, не сможете его использовать, несмотря на то, что технически это использует компилятор.
Комментарии:
1. Я использую GCC для компиляции сценария. Вот команда, которую я использую для этого: gcc-m32-c test.cpp -о кп.о
2. кроме того, поскольку я создаю эту ОС, я не могу использовать <iostream>
3. @Red-Jay-Games Я отредактировал пост для некоторой ясности и объяснений того, что происходит (насколько мне известно). Сработало ли это в конце концов?
4. Я собираюсь попробовать, но я все еще немного не понимаю, что все это значит и что я должен делать