Как зайти в Даркнет?!
25th January, 01:11
6
0
Как в tkinter из поля ввода Entry получить значение в одну переменную и обновить строку кнопкой, затем получить ещё одно введённое значение и затем сложить их. Ниже пример кода
21st July, 19:00
895
0
Программа, которая создает фейковые сервера в поиске игровых серверов CS 1.6 Steam
21st March, 17:43
948
0
Очень долго работает Update запрос Oracle
27th January, 09:58
914
0
не могу запустить сервер на tomcat HTTP Status 404 – Not Found
21st January, 18:02
905
0
Где можно найти фрилансера для выполнения поступающих задач, на постоянной основе?
2nd December, 09:48
938
0
Разработка мобильной кроссплатформенной военной игры
16th July, 17:57
1724
0
период по дням
25th October, 10:44
3955
0
Пишу скрипты для BAS только на запросах
16th September, 02:42
3720
0
Некорректный скрипт для закрытия блока
14th April, 18:33
4613
0
прокидывать exception в блоках try-catch JAVA
11th March, 21:11
4381
0
Помогите пожалуйста решить задачи
24th November, 23:53
6086
0
Не понимаю почему не открывается детальное описание продукта
11th November, 11:51
4351
0
Нужно решить задачу по программированию на массивы
27th October, 18:01
4396
0
Метода Крамера С++
23rd October, 11:55
4309
0
помогите решить задачу на C++
22nd October, 17:31
4002
0
Помогите решить задачу на python с codeforces
22nd October, 11:11
4492
0
Python с нуля: полное руководство для начинающих
18th June, 13:58
2599
0
Нарушается ли GCC при приеме адреса аргумента на ARM7TDMI?
Фрагмент кода My C принимает адрес аргумента и сохраняет его в энергонезависимой памяти (предварительно обработанный код):
void foo(unsigned int x) {
*(volatile unsigned int*)(0x4000000 + 0xd4) = (unsigned int)(&x);
}
int main() {
foo(1);
while(1);
}
Я использовал SVN версию GCC для компиляции этого кода. В конце функции foo я ожидал бы, что значение 1 будет сохранено в стеке, а в 0x40000d4-адрес , указывающий на это значение. Когда я компилирую без оптимизации, используя флаг -O0, я получаю ожидаемый выход ARM7TMDI assembly (прокомментированный для вашего удобства):
.align 2
.global foo
.type foo, %function
foo:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
sub sp, sp, #8
str r0, [sp, #4] @ 3. Store the argument on the stack
mov r3, #67108864
add r3, r3, #212
add r2, sp, #4 @ 4. Address of the stack variable
str r2, [r3, #0] @ 5. Store the address at 0x40000d4
add sp, sp, #8
bx lr
.size foo, .-foo
.align 2
.global main
.type main, %function
main:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
stmfd sp!, {r4, lr}
mov r0, #1 @ 1. Pass the argument in register 0
bl foo @ 2. Call function foo
.L4:
b .L4
.size main, .-main
.ident "GCC: (GNU) 4.4.0 20080820 (experimental)"
Он четко сохраняет аргумент сначала в стеке, а оттуда сохраняет его в 0x40000d4 . Когда я компилирую с оптимизациями, используя -O1 , я получаю что-то неожиданное:
.align 2
.global foo
.type foo, %function
foo:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 8
@ frame_needed = 0, uses_anonymous_args = 0
@ link register save eliminated.
sub sp, sp, #8
mov r2, #67108864
add r3, sp, #4 @ 3. Address of *something* on the stack
str r3, [r2, #212] @ 4. Store the address at 0x40000d4
add sp, sp, #8
bx lr
.size foo, .-foo
.align 2
.global main
.type main, %function
main:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
stmfd sp!, {r4, lr}
mov r0, #1 @ 1. Pass the argument in register 0
bl foo @ 2. Call function foo
.L4:
b .L4
.size main, .-main
.ident "GCC: (GNU) 4.4.0 20080820 (experimental)"
На этот раз аргумент никогда не сохраняется в стеке, даже если что- то из стека все еще хранится в 0x40000d4 .
Это просто ожидаемое / неопределенное поведение? Я сделал что-то не так или действительно нашел компилятор Bug™?
Как только вы вернетесь из foo(), x исчезнет, и любые указатели на него будут недействительны. Впоследствии использование такого указателя приводит к тому, что стандарт C любит называть "undefined behavior,", что означает, что компилятор абсолютно может предположить, что вы не разыменуете его, или (если вы все равно настаиваете на этом) не нужно создавать код, который делает что-то отдаленно похожее на то, что вы могли бы ожидать. Если вы хотите, чтобы указатель на x оставался действительным после возвращения foo() , вы не должны выделять x в стеке foo, точка - даже если вы знаете, что в принципе ничто не имеет никаких причин его разбивать - потому что это просто не разрешено в C, независимо от того, как часто это происходит, чтобы сделать то, что вы ожидаете.
Самым простым решением может быть сделать x локальной переменной в main() (или в любой другой функции, имеющей достаточно длительную область видимости) и передать адрес в foo. Вы также можете сделать x глобальной переменной , или выделить его в куче, используя malloc(), или выделить для него память каким-то более экзотическим способом. Вы даже можете попытаться выяснить, где находится верхняя часть стека в некотором (надеюсь) более переносимом виде и явно хранить свои данные в какой-то части стека, если вы уверены, что вам больше ничего не понадобится, и вы уверены, что это то, что вам действительно нужно сделать. Но метод, который вы использовали для этого, как вы обнаружили, недостаточно надежен.
Итак, вы помещаете адрес локальной переменной стека в используемый контроллер DMA, а затем возвращаетесь из функции, в которой доступна переменная стека?
Хотя это может сработать с вашим примером main() (так как вы больше не пишете в стеке), он не будет работать в программе 'real' позже - это значение будет перезаписано до или во время обращения к нему DMA, когда вызывается другая функция и стек используется снова.
Вам нужно иметь структуру или глобальную переменную, которую вы можете использовать для хранения этого значения, пока DMA обращается к нему - иначе он просто будет забит!
-Adam
Я на самом деле не думаю, что компилятор ошибается, хотя это странный случай.
Из анализа кода point-of-view он видит, что вы храните адрес переменной, но этот адрес никогда не разыменовывается, и вы не переходите за пределы функции к внешнему коду, который мог бы использовать этот адрес, который вы сохранили. При выходе из функции адрес стека теперь считается фиктивным, так как это адрес переменной, которая больше не существует.
Ключевое слово "volatile" действительно не делает много в C, особенно в отношении нескольких потоков или оборудования. Он просто говорит компилятору, что он должен сделать доступ. Однако, поскольку нет пользователей значения x в соответствии с потоком данных, нет никакой причины хранить "1" в стеке.
Вероятно, это сработало бы, если бы вы написали
void foo(unsigned int x) {
volatile int y = x;
*(volatile unsigned int*)(0x4000000 + 0xd4) = (unsigned int)(&y);
}
хотя это все еще может быть незаконным кодом, так как адрес y считается недействительным, как только foo возвращается, но природа системы DMA будет ссылаться на это местоположение независимо от потока программы.
Следует отметить, что согласно стандарту, приведения являются r-значениями. GCC раньше позволял это, но в последних версиях стал немного сторонником стандартов.
Я не знаю, будет ли это иметь значение, но вы должны попробовать это:
void foo(unsigned int x) {
volatile unsigned int* ptr = (unsigned int*)(0x4000000 + 0xd4);
*ptr = (unsigned int)(&x);
}
int main() {
foo(1);
while(1);
}
Кроме того, я сомневаюсь, что вы это задумали, но вы сохраняете адрес функции local x (которая является копией переданного вами int). Вероятно, вы хотите заставить foo взять "unsigned int *" и передать адрес того, что вы действительно хотите сохранить.
Поэтому я считаю, что более правильным решением было бы следующее:
void foo(unsigned int *x) {
volatile unsigned int* ptr = (unsigned int*)(0x4000000 + 0xd4);
*ptr = (unsigned int)(x);
}
int main() {
int x = 1;
foo(&x);
while(1);
}
EDIT: наконец, если вы кодируете разрывы с оптимизацией, это обычно признак того, что ваш код делает что-то неправильно.
Томи писал Канкаанпяа
разработка для Game Boy Advance. Я читал о его системе DMA и Я экспериментировал с ним, создавая одноцветные растровые изображения плитки. Идея должен был иметь индексированный цвет быть передается в качестве аргумента функции который будет использовать DMA для заполнения плитки с этим цветом. Адрес источника для передачи DMA хранится на 0x40000d4.
Это совершенно правильная вещь для вас, и я могу видеть, как (неожиданный) код, который вы получили с оптимизацией-O1, не будет работать.
Я вижу, что (ожидаемый) код, который вы получили с помощью оптимизации-O0, делает то, что вы ожидаете-он помещает значение нужного цвета в стек и указатель на этот цвет в регистре передачи DMA.
Однако даже тот (ожидаемый) код, который вы получили с помощью оптимизации-O0, также не будет работать. К тому времени, когда аппаратное обеспечение DMA соберется взять этот указатель и использовать его для чтения нужного цвета, это значение в стеке (вероятно) уже давно было перезаписано другими подпрограммами или обработчиками прерываний или и тем, и другим. Таким образом, и ожидаемый, и неожиданный код приводят к одному и тому же результату-DMA (вероятно) будет получать неверный цвет.
Я думаю, что вы действительно намеревались сохранить значение цвета в каком-то месте, где оно будет оставаться безопасным, пока DMA не закончит его читать. Таким образом, глобальная переменная или функция-локальная статическая переменная, такая как
// Предупреждение: трехзвездочный программист на работе
// Warning: untested code.
void foo(unsigned int x) {
static volatile unsigned int color = x; // "static" so it's not on the stack
volatile unsigned int** dma_register =
(volatile unsigned int**)(0x4000000 + 0xd4);
*dma_register = &color;
}
int main() {
foo(1);
while(1);
}
Это работает на вас?
Вы видите, что я использую "volatile" дважды, потому что я хочу заставить два значения быть записанными в этом конкретном порядке.
Будь я проклят, если я могу найти ссылку на данный момент, но я уверен, что вы всегда должны быть в состоянии взять адрес аргумента, и это зависит от компилятора, чтобы тонко разобраться в деталях соглашений о вызовах, использовании регистра и т. д.
Действительно, я бы подумал, что это настолько распространенное требование, что трудно увидеть, что в этом может быть общая проблема - интересно, не связано ли это с изменчивыми указателями, которые нарушили оптимизацию.
Лично я, возможно, попробую это сделать, чтобы посмотреть, лучше ли он компилируется:
void foo(unsigned int x)
{
volatile unsigned int* pArg = &x;
*(volatile unsigned int*)(0x4000000 + 0xd4) = (unsigned int)pArg;
}
спаркес писал:
Если вы думаете, что нашли ошибку в GCC списки рассылки будут рады вам заскочили но как правило они находят какая-то дыра в ваших знаниях должна быть виноваты и беспощадно глумиться :(
Я решил сначала попытать счастья здесь, прежде чем идти в список рассылки GCC, чтобы показать свою некомпетентность :)
Adam Дэвис писал:
Из любопытства, что ты пытаешься сделать чтобы добиться этого?
Я пробовал разработку для Game Boy Advance. Я читал о его системе DMA и экспериментировал с ней, создавая одноцветные растровые изображения плиток. Идея заключалась в том, чтобы индексированный цвет был передан в качестве аргумента функции, которая будет использовать DMA для заполнения плитки этим цветом. Исходный адрес для передачи DMA хранится в 0x40000d4 .
Уилл Дин написал:
Лично я мог бы попробовать это увидеть если бы он был лучше скомпилирован:
void foo(unsigned int x) { volatile unsigned int* pArg = &x; *(volatile unsigned int*)(0x4000000 + 0xd4) = (unsigned int)pArg; }
С -O0 это работает так же, как и с -O1 , который оптимизирован для того же самого -O1 assembly, который я опубликовал в своем вопросе.
Я думаю, даже у Ти есть ответ. Вы передали переменную, вы не можете взять адрес этой переменной внутри функции, вы можете взять адрес копии этой переменной, хотя, кстати, эта переменная обычно является регистром, поэтому у нее нет адреса. Как только вы оставляете эту функцию, она полностью исчезает, вызывающая функция теряет ее. Если вам нужен адрес в функции, которую вы должны передать по ссылке, а не передать по значению, отправьте адрес. Мне кажется, что ошибка находится в вашем коде, а не в gcc.
BTW, используя *(volatile blah *) 0xabcd или любой другой метод, чтобы попытаться запрограммировать регистры, в конечном счете, укусит вас. gcc и у большинства других компиляторов есть этот сверхъестественный способ точно знать худшее время для удара.
Скажем, в тот день, когда ты изменишься от этого
*(volatile unsigned int *)0x12345 = someuintvariable;
к
*(volatile unsigned int *)0x12345 = 0x12;
Хороший компилятор поймет, что вы храните только 8 бит, и нет никакой причины тратить на это 32-битное хранилище, в зависимости от архитектуры, которую вы указали, или архитектуры по умолчанию для этого компилятора в тот день, поэтому он имеет право оптимизировать это в strb вместо str.
После того как я был сожжен gcc и другими с этим десятки раз я прибегал к форсированию вопроса:
.globl PUT32
PUT32:
str r1,[r0]
bx lr
PUT32(0x12345,0x12);
Стоит несколько дополнительных тактов, но мой код продолжает работать вчера, сегодня и будет работать завтра с любым флагом оптимизации. Отсутствие необходимости повторно посещать старый код и спокойно спать всю ночь стоит нескольких дополнительных циклов часов здесь и там.
Кроме того, если ваш код прерывается при компиляции для выпуска вместо компиляции для отладки, это также означает, что это, скорее всего, ошибка в вашем коде.
В целом я бы сказал, что это правильная оптимизация. Если вы хотите заглянуть глубже в него, вы можете скомпилировать с -da Это порождает а .c.Number.Passname, где вы можете взглянуть на rtl (промежуточное представление в пределах gcc). Там вы можете увидеть, какой проход делает какую оптимизацию (и, возможно, отключить только тот, который вы не хотите иметь)
Является ли это просто ожидаемым/неопределенным
поведение? Неужели я сделал что-то не так?
или Я действительно нашел компилятор
™Ошибка?
Является ли это просто ожидаемым/неопределенным поведение? Неужели я сделал что-то не так? или Я действительно нашел компилятор ™Ошибка?
Нет ошибки просто определенное поведение что параметры оптимизации могут производить нечетный код который может не работать :)
EDIT:
Если вы думаете, что нашли ошибку в GCC списки рассылки будут рады вам заглянуть, но в целом они находят какую-то дыру в ваших знаниях, виноваты и безжалостно издеваются :(
В этом случае я думаю, что это, вероятно, варианты-O, пытающиеся ярлыки, которые ломают ваш код, который нужно обойти.