URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [gcc/] [config/] [arm/] [README-interworking] - Rev 717
Go to most recent revision | Compare with Previous | Blame | View Log
Arm / Thumb Interworking========================The Cygnus GNU Pro Toolkit for the ARM7T processor supports functioncalls between code compiled for the ARM instruction set and codecompiled for the Thumb instruction set and vice versa. This documentdescribes how that interworking support operates and explains thecommand line switches that should be used in order to produce workingprograms.Note: The Cygnus GNU Pro Toolkit does not support switching betweencompiling for the ARM instruction set and the Thumb instruction seton anything other than a per file basis. There are in fact twocompletely separate compilers, one that produces ARM assemblerinstructions and one that produces Thumb assembler instructions. Thetwo compilers share the same assembler, linker and so on.1. Explicit interworking support for C and C++ files====================================================By default if a file is compiled without any special command lineswitches then the code produced will not support interworking.Provided that a program is made up entirely from object files andlibraries produced in this way and which contain either exclusivelyARM instructions or exclusively Thumb instructions then this will notmatter and a working executable will be created. If an attempt ismade to link together mixed ARM and Thumb object files and libraries,then warning messages will be produced by the linker and a non-workingexecutable will be created.In order to produce code which does support interworking it should becompiled with the-mthumb-interworkcommand line option. Provided that a program is made up entirely fromobject files and libraries built with this command line switch aworking executable will be produced, even if both ARM and Thumbinstructions are used by the various components of the program. (Nowarning messages will be produced by the linker either).Note that specifying -mthumb-interwork does result in slightly larger,slower code being produced. This is why interworking support must bespecifically enabled by a switch.2. Explicit interworking support for assembler files====================================================If assembler files are to be included into an interworking programthen the following rules must be obeyed:* Any externally visible functions must return by using the BXinstruction.* Normal function calls can just use the BL instruction. Thelinker will automatically insert code to switch between ARMand Thumb modes as necessary.* Calls via function pointers should use the BX instruction ifthe call is made in ARM mode:.code 32mov lr, pcbx rXThis code sequence will not work in Thumb mode however, sincethe mov instruction will not set the bottom bit of the lrregister. Instead a branch-and-link to the _call_via_rXfunctions should be used instead:.code 16bl _call_via_rXwhere rX is replaced by the name of the register containingthe function address.* All externally visible functions which should be entered inThumb mode must have the .thumb_func pseudo op specified justbefore their entry point. e.g.:.code 16.global function.thumb_funcfunction:...start of function....* All assembler files must be assembled with the switch-mthumb-interwork specified on the command line. (If the fileis assembled by calling gcc it will automatically pass on the-mthumb-interwork switch to the assembler, provided that itwas specified on the gcc command line in the first place.)3. Support for old, non-interworking aware code.================================================If it is necessary to link together code produced by an older,non-interworking aware compiler, or code produced by the new compilerbut without the -mthumb-interwork command line switch specified, thenthere are two command line switches that can be used to support this.The switch-mcaller-super-interworkingwill allow calls via function pointers in Thumb mode to work,regardless of whether the function pointer points to old,non-interworking aware code or not. Specifying this switch doesproduce slightly slower code however.Note: There is no switch to allow calls via function pointers in ARMmode to be handled specially. Calls via function pointers frominterworking aware ARM code to non-interworking aware ARM code workwithout any special considerations by the compiler. Calls viafunction pointers from interworking aware ARM code to non-interworkingaware Thumb code however will not work. (Actually under somecircumstances they may work, but there are no guarantees). This isbecause only the new compiler is able to produce Thumb code, and thiscompiler already has a command line switch to produce interworkingaware code.The switch-mcallee-super-interworkingwill allow non-interworking aware ARM or Thumb code to call Thumbfunctions, either directly or via function pointers. Specifying thisswitch does produce slightly larger, slower code however.Note: There is no switch to allow non-interworking aware ARM or Thumbcode to call ARM functions. There is no need for any special handlingof calls from non-interworking aware ARM code to interworking awareARM functions, they just work normally. Calls from non-interworkingaware Thumb functions to ARM code however, will not work. There is nooption to support this, since it is always possible to recompile theThumb code to be interworking aware.As an alternative to the command line switch-mcallee-super-interworking, which affects all externally visiblefunctions in a file, it is possible to specify an attribute ordeclspec for individual functions, indicating that that particularfunction should support being called by non-interworking aware code.The function should be defined like this:int __attribute__((interfacearm)) function{... body of function ...}orint __declspec(interfacearm) function{... body of function ...}4. Interworking support in dlltool==================================It is possible to create DLLs containing mixed ARM and Thumb code. Itis also possible to call Thumb code in a DLL from an ARM program andvice versa. It is even possible to call ARM DLLs that have been compiledwithout interworking support (say by an older version of the compiler),from Thumb programs and still have things work properly.A version of the `dlltool' program which supports the `--interwork'command line switch is needed, as well as the following specialconsiderations when building programs and DLLs:*Use `-mthumb-interwork'*When compiling files for a DLL or a program the `-mthumb-interwork'command line switch should be specified if calling between ARM andThumb code can happen. If a program is being compiled and themode of the DLLs that it uses is not known, then it should beassumed that interworking might occur and the switch used.*Use `-m thumb'*If the exported functions from a DLL are all Thumb encoded then the`-m thumb' command line switch should be given to dlltool whenbuilding the stubs. This will make dlltool create Thumb encodedstubs, rather than its default of ARM encoded stubs.If the DLL consists of both exported Thumb functions and exportedARM functions then the `-m thumb' switch should not be used.Instead the Thumb functions in the DLL should be compiled with the`-mcallee-super-interworking' switch, or with the `interfacearm'attribute specified on their prototypes. In this way they will begiven ARM encoded prologues, which will work with the ARM encodedstubs produced by dlltool.*Use `-mcaller-super-interworking'*If it is possible for Thumb functions in a DLL to callnon-interworking aware code via a function pointer, then the Thumbcode must be compiled with the `-mcaller-super-interworking'command line switch. This will force the function pointer callsto use the _interwork_call_via_rX stub functions which willcorrectly restore Thumb mode upon return from the called function.*Link with `libgcc.a'*When the dll is built it may have to be linked with the GCClibrary (`libgcc.a') in order to extract the _call_via_rX functionsor the _interwork_call_via_rX functions. This represents a partialredundancy since the same functions *may* be present in theapplication itself, but since they only take up 372 bytes thisshould not be too much of a consideration.*Use `--support-old-code'*When linking a program with an old DLL which does not supportinterworking, the `--support-old-code' command line switch to thelinker should be used. This causes the linker to generate specialinterworking stubs which can cope with old, non-interworking awareARM code, at the cost of generating bulkier code. The linker willstill generate a warning message along the lines of:"Warning: input file XXX does not support interworking, whereas YYY does."but this can now be ignored because the --support-old-code switchhas been used.5. How interworking support works=================================Switching between the ARM and Thumb instruction sets is accomplishedvia the BX instruction which takes as an argument a register name.Control is transfered to the address held in this register (with thebottom bit masked out), and if the bottom bit is set, then Thumbinstruction processing is enabled, otherwise ARM instructionprocessing is enabled.When the -mthumb-interwork command line switch is specified, gccarranges for all functions to return to their caller by using the BXinstruction. Thus provided that the return address has the bottom bitcorrectly initialized to indicate the instruction set of the caller,correct operation will ensue.When a function is called explicitly (rather than via a functionpointer), the compiler generates a BL instruction to do this. TheThumb version of the BL instruction has the special property ofsetting the bottom bit of the LR register after it has stored thereturn address into it, so that a future BX instruction will correctlyreturn the instruction after the BL instruction, in Thumb mode.The BL instruction does not change modes itself however, so if an ARMfunction is calling a Thumb function, or vice versa, it is necessaryto generate some extra instructions to handle this. This is done inthe linker when it is storing the address of the referenced functioninto the BL instruction. If the BL instruction is an ARM style BLinstruction, but the referenced function is a Thumb function, then thelinker automatically generates a calling stub that converts from ARMmode to Thumb mode, puts the address of this stub into the BLinstruction, and puts the address of the referenced function into thestub. Similarly if the BL instruction is a Thumb BL instruction, andthe referenced function is an ARM function, the linker generates astub which converts from Thumb to ARM mode, puts the address of thisstub into the BL instruction, and the address of the referencedfunction into the stub.This is why it is necessary to mark Thumb functions with the.thumb_func pseudo op when creating assembler files. This pseudo opallows the assembler to distinguish between ARM functions and Thumbfunctions. (The Thumb version of GCC automatically generates thesepseudo ops for any Thumb functions that it generates).Calls via function pointers work differently. Whenever the address ofa function is taken, the linker examines the type of the functionbeing referenced. If the function is a Thumb function, then it setsthe bottom bit of the address. Technically this makes the addressincorrect, since it is now one byte into the start of the function,but this is never a problem because:a. with interworking enabled all calls via function pointerare done using the BX instruction and this ignores thebottom bit when computing where to go to.b. the linker will always set the bottom bit when the addressof the function is taken, so it is never possible to takethe address of the function in two different places andthen compare them and find that they are not equal.As already mentioned any call via a function pointer will use the BXinstruction (provided that interworking is enabled). The only problemwith this is computing the return address for the return from thecalled function. For ARM code this can easily be done by the codesequence:mov lr, pcbx rX(where rX is the name of the register containing the functionpointer). This code does not work for the Thumb instruction set,since the MOV instruction will not set the bottom bit of the LRregister, so that when the called function returns, it will return inARM mode not Thumb mode. Instead the compiler generates thissequence:bl _call_via_rX(again where rX is the name if the register containing the functionpointer). The special call_via_rX functions look like this:.thumb_func_call_via_r0:bx r0nopThe BL instruction ensures that the correct return address is storedin the LR register and then the BX instruction jumps to the addressstored in the function pointer, switch modes if necessary.6. How caller-super-interworking support works==============================================When the -mcaller-super-interworking command line switch is specifiedit changes the code produced by the Thumb compiler so that all callsvia function pointers (including virtual function calls) now go via adifferent stub function. The code to call via a function pointer nowlooks like this:bl _interwork_call_via_r0Note: The compiler does not insist that r0 be used to hold thefunction address. Any register will do, and there are a suite of stubfunctions, one for each possible register. The stub functions looklike this:.code 16.thumb_func_interwork_call_via_r0bx pcnop.code 32tst r0, #1stmeqdb r13!, {lr}adreq lr, _arm_returnbx r0The stub first switches to ARM mode, since it is a lot easier toperform the necessary operations using ARM instructions. It thentests the bottom bit of the register containing the address of thefunction to be called. If this bottom bit is set then the functionbeing called uses Thumb instructions and the BX instruction to comewill switch back into Thumb mode before calling this function. (Notethat it does not matter how this called function chooses to return toits caller, since the both the caller and callee are Thumb functions,and mode switching is necessary). If the function being called is anARM mode function however, the stub pushes the return address (withits bottom bit set) onto the stack, replaces the return address withthe address of the a piece of code called '_arm_return' and thenperforms a BX instruction to call the function.The '_arm_return' code looks like this:.code 32_arm_return:ldmia r13!, {r12}bx r12.code 16It simply retrieves the return address from the stack, and thenperforms a BX operation to return to the caller and switch back intoThumb mode.7. How callee-super-interworking support works==============================================When -mcallee-super-interworking is specified on the command line theThumb compiler behaves as if every externally visible function that itcompiles has had the (interfacearm) attribute specified for it. Whatthis attribute does is to put a special, ARM mode header onto thefunction which forces a switch into Thumb mode:without __attribute__((interfacearm)):.code 16.thumb_funcfunction:... start of function ...with __attribute__((interfacearm)):.code 32function:orr r12, pc, #1bx r12.code 16.thumb_func.real_start_of_function:... start of function ...Note that since the function now expects to be entered in ARM mode, itno longer has the .thumb_func pseudo op specified for its name.Instead the pseudo op is attached to a new label .real_start_of_<name>(where <name> is the name of the function) which indicates the startof the Thumb code. This does have the interesting side effect in thatif this function is now called from a Thumb mode piece of codeoutside of the current file, the linker will generate a calling stubto switch from Thumb mode into ARM mode, and then this is immediatelyoverridden by the function's header which switches back into Thumbmode.In addition the (interfacearm) attribute also forces the function toreturn by using the BX instruction, even if has not been compiled withthe -mthumb-interwork command line flag, so that the correct mode willbe restored upon exit from the function.8. Some examples================Given these two test files:int arm (void) { return 1 + thumb (); }int thumb (void) { return 2 + arm (); }The following pieces of assembler are produced by the ARM and Thumbversion of GCC depending upon the command line options used:`-O2':.code 32 .code 16.global _arm .global _thumb.thumb_func_arm: _thumb:mov ip, spstmfd sp!, {fp, ip, lr, pc} push {lr}sub fp, ip, #4bl _thumb bl _armadd r0, r0, #1 add r0, r0, #2ldmea fp, {fp, sp, pc} pop {pc}Note how the functions return without using the BX instruction. Ifthese files were assembled and linked together they would fail to workbecause they do not change mode when returning to their caller.`-O2 -mthumb-interwork':.code 32 .code 16.global _arm .global _thumb.thumb_func_arm: _thumb:mov ip, spstmfd sp!, {fp, ip, lr, pc} push {lr}sub fp, ip, #4bl _thumb bl _armadd r0, r0, #1 add r0, r0, #2ldmea fp, {fp, sp, lr} pop {r1}bx lr bx r1Now the functions use BX to return their caller. They have grown by4 and 2 bytes respectively, but they can now successfully be linkedtogether and be expect to work. The linker will replace thedestinations of the two BL instructions with the addresses of callingstubs which convert to the correct mode before jumping to the calledfunction.`-O2 -mcallee-super-interworking':.code 32 .code 32.global _arm .global _thumb_arm: _thumb:orr r12, pc, #1bx r12mov ip, sp .code 16stmfd sp!, {fp, ip, lr, pc} push {lr}sub fp, ip, #4bl _thumb bl _armadd r0, r0, #1 add r0, r0, #2ldmea fp, {fp, sp, lr} pop {r1}bx lr bx r1The thumb function now has an ARM encoded prologue, and it no longerhas the `.thumb-func' pseudo op attached to it. The linker will notgenerate a calling stub for the call from arm() to thumb(), but it willstill have to generate a stub for the call from thumb() to arm(). Alsonote how specifying `--mcallee-super-interworking' automaticallyimplies `-mthumb-interworking'.9. Some Function Pointer Examples=================================Given this test file:int func (void) { return 1; }int call (int (* ptr)(void)) { return ptr (); }The following varying pieces of assembler are produced by the Thumbversion of GCC depending upon the command line options used:`-O2':.code 16.globl _func.thumb_func_func:mov r0, #1bx lr.globl _call.thumb_func_call:push {lr}bl __call_via_r0pop {pc}Note how the two functions have different exit sequences. Inparticular call() uses pop {pc} to return, which would not work if thecaller was in ARM mode. func() however, uses the BX instruction, eventhough `-mthumb-interwork' has not been specified, as this is the mostefficient way to exit a function when the return address is held in thelink register.`-O2 -mthumb-interwork':.code 16.globl _func.thumb_func_func:mov r0, #1bx lr.globl _call.thumb_func_call:push {lr}bl __call_via_r0pop {r1}bx r1This time both functions return by using the BX instruction. Thismeans that call() is now two bytes longer and several cycles slowerthan the previous version.`-O2 -mcaller-super-interworking':.code 16.globl _func.thumb_func_func:mov r0, #1bx lr.globl _call.thumb_func_call:push {lr}bl __interwork_call_via_r0pop {pc}Very similar to the first (non-interworking) version, except that adifferent stub is used to call via the function pointer. This new stubwill work even if the called function is not interworking aware, andtries to return to call() in ARM mode. Note that the assembly code forcall() is still not interworking aware itself, and so should not becalled from ARM code.`-O2 -mcallee-super-interworking':.code 32.globl _func_func:orr r12, pc, #1bx r12.code 16.globl .real_start_of_func.thumb_func.real_start_of_func:mov r0, #1bx lr.code 32.globl _call_call:orr r12, pc, #1bx r12.code 16.globl .real_start_of_call.thumb_func.real_start_of_call:push {lr}bl __call_via_r0pop {r1}bx r1Now both functions have an ARM coded prologue, and both functionsreturn by using the BX instruction. These functions are interworkingaware therefore and can safely be called from ARM code. The code forthe call() function is now 10 bytes longer than the original, noninterworking aware version, an increase of over 200%.If a prototype for call() is added to the source code, and thisprototype includes the `interfacearm' attribute:int __attribute__((interfacearm)) call (int (* ptr)(void));then this code is produced (with only -O2 specified on the commandline):.code 16.globl _func.thumb_func_func:mov r0, #1bx lr.globl _call.code 32_call:orr r12, pc, #1bx r12.code 16.globl .real_start_of_call.thumb_func.real_start_of_call:push {lr}bl __call_via_r0pop {r1}bx r1So now both call() and func() can be safely called vianon-interworking aware ARM code. If, when such a file is assembled,the assembler detects the fact that call() is being called by anotherfunction in the same file, it will automatically adjust the target ofthe BL instruction to point to .real_start_of_call. In this way thereis no need for the linker to generate a Thumb-to-ARM calling stub sothat call can be entered in ARM mode.10. How to use dlltool to build ARM/Thumb DLLs==============================================Given a program (`prog.c') like this:extern int func_in_dll (void);int main (void) { return func_in_dll(); }And a DLL source file (`dll.c') like this:int func_in_dll (void) { return 1; }Here is how to build the DLL and the program for a purely ARM basedenvironment:*Step OneBuild a `.def' file describing the DLL:; example.def; This file describes the contents of the DLLLIBRARY exampleHEAPSIZE 0x40000, 0x2000EXPORTSfunc_in_dll 1*Step TwoCompile the DLL source code:arm-pe-gcc -O2 -c dll.c*Step ThreeUse `dlltool' to create an exports file and a library file:dlltool --def example.def --output-exp example.o --output-lib example.a*Step FourLink together the complete DLL:arm-pe-ld dll.o example.o -o example.dll*Step FiveCompile the program's source code:arm-pe-gcc -O2 -c prog.c*Step SixLink together the program and the DLL's library file:arm-pe-gcc prog.o example.a -o progIf instead this was a Thumb DLL being called from an ARM program, thesteps would look like this. (To save space only those steps that aredifferent from the previous version are shown):*Step TwoCompile the DLL source code (using the Thumb compiler):thumb-pe-gcc -O2 -c dll.c -mthumb-interwork*Step ThreeBuild the exports and library files (and support interworking):dlltool -d example.def -z example.o -l example.a --interwork -m thumb*Step FiveCompile the program's source code (and support interworking):arm-pe-gcc -O2 -c prog.c -mthumb-interworkIf instead, the DLL was an old, ARM DLL which does not supportinterworking, and which cannot be rebuilt, then these steps would beused.*Step OneSkip. If you do not have access to the sources of a DLL, there isno point in building a `.def' file for it.*Step TwoSkip. With no DLL sources there is nothing to compile.*Step ThreeSkip. Without a `.def' file you cannot use dlltool to build anexports file or a library file.*Step FourSkip. Without a set of DLL object files you cannot build the DLL.Besides it has already been built for you by somebody else.*Step FiveCompile the program's source code, this is the same as before:arm-pe-gcc -O2 -c prog.c*Step SixLink together the program and the DLL's library file, passing the`--support-old-code' option to the linker:arm-pe-gcc prog.o example.a -Wl,--support-old-code -o progIgnore the warning message about the input file not supportinginterworking as the --support-old-code switch has taken care if this.Copyright (C) 1998, 2002, 2003, 2004 Free Software Foundation, Inc.Copying and distribution of this file, with or without modification,are permitted in any medium without royalty provided the copyrightnotice and this notice are preserved.
Go to most recent revision | Compare with Previous | Blame | View Log
