TL: DR: вы, вероятно, добавили fpuctl: .word 0
в раздел только для чтения .text
вместе со своим кодом. Храните на некотором пустом месте в стеке или на BSS, если вы действительно хотите использовать статическое хранилище.
Вы правы, единственная форма fnstcw
- это адресат памяти.
Более часто используемый fnstsw %ax
(слово x87 status ) имеет форму AX-destination, но fnstcw
не имеет.
(Конечно, x87 устарел, за исключением случаев, когда вам действительно нужна 80-битная точность; современный x86 имеет как минимум SSE2, так что вы можете и должны выполнять скалярную FP математику в регистрах XMM . Управление и утилита SSE FPU все биты состояния находятся в MXCSR, отдельно от элемента управления и статуса x87.)
Также обратите внимание, что если вы вызываете это из C и собираетесь изменить управляющее слово x87, вам нужно сообщить об этом компилятору , чтобы он не предполагал, что режим округления по-прежнему округляется до ближайшего, или точность по-прежнему составляет 80 бит (64-битная мантисса). Это может иметь значение для постоянного распространения во время компиляции и других оптимизаций. Например, у gcc есть -frounding-math
и -fsignaling-nans
. Он также может поддерживать C99 #pragma STDC FENV ACCESS ON
, но я не уверен, что gcc или clang полностью соответствуют стандартам для этого. См. Также https://gcc.gnu.org/wiki/FloatingPointMath. (Для изменения среды FP вы должны использовать стандартные функции C из fenv.h
, например fegetenv
/ fesetenv
.)
Если вы собираетесь использовать это из написанного от руки asm, сделайте его макросом, который принимает 2-й аргумент в качестве ячейки памяти, вместо функции. Это слишком мало, чтобы иметь смысл как не встроенная функция; это 2 полезные инструкции (fnstcw
и перезагрузка); остальное накладные расходы.
Кстати, AX - это 16-битный регистр. AL - младшие 8 бит EAX.
Кроме того, movzwl fpuctl, %eax
будет выполнять загрузку слова с расширением нуля, поэтому вам не нужно будет сначала xor-zero eax.
Вы не предоставили MCVE, но , вероятно, вы указали fpuctl: .word 0
в разделе только для чтения .text
вместе со своим кодом, , чтобы вы получили ту же ошибку, как если бы вы использовали mov %eax, getFPUControlState
.
Вместо этого поместите его в BSS (статическое хранилище с нулевым инициализацией, где нули не хранятся в вашем исполняемом файле, просто общий размер).
.bss
fpuctl: .zero 2 # 2 bytes of zeros in the BSS
.text # switch back to the .text section
.globl getFPUControlState # GAS does accept .global as a synonym for .globl
.type getFPUControlState, function
getFPUControlState:
# leave out the EBP stack-frame stuff, you're not using it for anything
fnstcw fpuctl
movzwl fpuctl, %eax # zero-extending word load into EAX
ret
.size getFPUControlState, . - getFPUControlState
В качестве альтернативы используйте .lcomm
, чтобы зарезервировать 2 байта в BSS и пометить его как fpuctl как неэкспортированную метку:
.lcomm fpuctl, 2 # puts stuff in the BSS regardless of current section
Но на самом деле вам не нужно статическое хранилище для этого
Таким образом, вы можете спасти себя от потенциальной ошибки страницы, просто используя стек.
.globl getFPUControlState
.type getFPUControlState, function
getFPUControlState:
sub $4, %esp # reserve space on the stack
fnstcw (%esp)
movzwl (%esp), %eax
add $4, %esp # restore ESP to point at the return address
ret
Или, если вы предпочитаете оптимизировать размер кода за счет задержки пересылки из магазина после загрузки слова после хранилища слов:
push $0
fnstcw (%esp)
pop %eax # upper 2 bytes are zero from the PUSHed data
ret