Hi everyone,
I try to build a dynamically linkable library using the C6000 CGT (from CCS v5).
For example, I created the simplest source file:
/* hello.c */
#include <stdio.h>__declspec(dllexport) int hello(int a, int b);
int hello(int a, int b){ int result = b - a; printf("Hello World, %d\n", result); return result;}
After this, I tried to build the file using gmake utility with --dynamic=lib option set. But I observed the following error message:
undefined first referenced symbol in file --------- ---------------- printf hello.objerror: unresolved symbols remainerror: errors encountered during linking; "hello.dll" not built
But as I think, the printf function should be linked dynamically.
Linked libc.a (with --library=libc.a option set) the file is built successfully but the output binary file contains printf and many other function from libc.a. So, the size of the output file is large.
But it seems like the output file's size should be reduced using dynamic linking of libraries. What is wrong in my approach?
Regards,
Nikolay
Sorry for my English :-)
Hi Nikolay,
There is nothing wrong with your approach, except that you are calling printf() which calls quite a few other functions that are defined in the RTS library. That is why your resulting dynamic library includes so much other stuff.
If you are truly starting from scratch, a good approach to take is to build your dynamic libraries from the bottom (of the call graph) up.
For example, consider that you have several leaf functions that are commonly used by several other dynamic libraries. You can collect the leaf functions into their own dynamic library (leaves.dll): The sources containing the definitions of the leaf functions should export the leaf function symbols (just like you do in your 'hello' source above).
cl6x --abi=eabi --mem_model:data=far -mv64+ leaf1.c leaf2,c leaf3.c -z --dynamic=lib -o leaves.dll
Now, suppose you have other groups of functions (limb_1*.c and limb_2*.c) that use the leaf functions from leaves.dll and you want to create dynamic libraries out of them ...
cl6x .--abi=eabi --mem_model:data=far -mv64+ limb_1a.c limb_1b.c limb_1c.c -z --dynamic=lib leaves.dll -o limb_1.dll
cl6x --abi=eabi --mem_model:data=far -mv64+ limb_2a.c limb_2b.c -z --dynamic=lib leaves.dll -o limb_2.dll
Note that leaves.dll is specified as an input to the static link step that builds limb_1.dll and limb_2.dll. This is because the calls to the leaf functions from limb_1.dll and limb_2.dll must be resolved at static link time. The specification of leaves.dll during the link step of the build of limb_1.dll and limb_2.dll does not have any impact on the size of limb_1.dll or limb_2.dll. In fact, you will see an overall size savings since the defintions of the leaf functions will only be in leaves.dll, not in limb_1.dll or limb_2.dll.
I hope this helps. Please don't hesitate to ask if you have further questions or need clarification.
Thanks and Regards,
Todd Snider
C6000 Code Generation Tools Team
Texas Instruments Incorporated
Hi Todd,
Thank you for your detailed answer. I agree with what you wrote...
Let me do a small preamble. I want to use RIDL (dynamic loader) on the my design. So, I added source code of the RIDL in the my RTCS project and successfully built a core image from it. Now I want to create a dll which will be use some functions from my RTCS project image (i.e. from my core image).
Because core image contains an C-runtime functions and also my other functions, I think that described above dll must not contain any of these functions.
How can I to specify the linker what my dll must be dynamically linked into core image?
Also I have a other question: Why when I build a dll with --dynamic=lib option then I do not have the .args section? If setting up --dynamic=exe option then the .args section is available.
Regards, Nikolay
Todd,
I tried to build my hello.c with following options as described above:
cl6x --abi=eabi --mem_model:data=far -mv64+ hello.c -z --dynamic=lib ritl.out -o hello.dll -m hello.map
where ritl.out a base image after build RIDL.
You can see MAP file contains an referenced of ridl.out and hello.dll has increase size.
8360.hello.map.zip
You can help me for resolve this issue?
Thanks
Hello Nikolay,
I'm sorry for the delay in responding. Let me address your post from 22 March first. If that does not shed some light on your later questions, we can continue the discussion.
One thing that I'd like to point out about RIDL is that it is a "reference implementation" of the dynamic loader. It is intended to be a demonstration of the interface between the client side of the dynamic loader and what we call the dynamic loader "core". The core contains code that knows about the details of the ELF object format (ELF file header, program header table, section table, etc.).
The boundary between the client side and the dynamic loader core is described in the header file, dload_api.h. Any functions that begin with a "DLOAD_" prefix are supplied in the core. Functions which begin with a "DLIF_" prefix are expected to be supplied by the client side implementation. The core part of the dynamic loader is partable from platform to platform, but the client side implementation will vary based on what the needs are for a given platform. In the case of RIDL, the client is implemented as an executable that can be loaded onto the target and run. When it runs, it will bring up a simplistic user interface that allows you to dynamically load and run DLLs that have been linked against the RIDL dynamic executable. For example,
cl6x -mv64+ --abi=eabi --mem_model:data=far hello.c -z --dynamic=lib ridl.exe -e start -o hello.dll
builds a dynamic library, hello.dll, which can call functions that are exported by ridl.exe. The "-e start" option defines the function start() in hello.c to be the entry point at which you want to start execiution when hello.dll is load and run from the RIDL user interface. To load and run hello.dll, you would first load and run ridl.exe at which point the RIDL user interface would come up. Using the RIDL user interface, you then load the symbols from ridl.exe with the "base_image" command. Then you can load your hello.dll dynamic library with the "load" command. Finally, you can run the hello.dll code from its entry point (start) using the "exec" command. To get out of the RIDL user interface and terminate the execution of ridl.exe, you can specify the "exit" command from the RIDL user interface.
With all of that said, I suspect that you don't intend for the dynamic loader in your system to work like RIDL. In which case, you will likely need to create your own implementation of the client side of the dynamic loader.
If you could provide a clearer picture of how you envision your system architecture and how all of the pieces interact with each other, I may be able to help point you in the right direction.
For example, if you want the dynamic loader to be able to load and run a dynamic executable, then you will need to implement a client side of the dynamic loader which can handle the .args section of the dynamic executable that is to be loaded and run. Other question to consider ... Do you want to run the dynamic loader on the target or on the host system? Do you have an OS running on the target that will initiate the dynamic loader?
Todd
In reply to your question from 26 March ... If you want to run hello.dll code from RIDL, you will want to define an entry point for hello.dll (not main(); you could just rename main() in hello.c to start() and then use "-e start" on the above linker command line).
- Todd
Hello Todd,
Thanks for answering about RIDL, it was very detailed and clear. But I wanted to ask you about dynamic linking.
So, you write:
>cl6x -mv64+ --abi=eabi --mem_model:data=far hello.c -z --dynamic=lib ridl.exe -e start -o hello.dll
>builds a dynamic library, hello.dll, which can call functions that are exported by ridl.exe
I tried to do it in my example (in my first post), but hello.dll DOESN'T use the functions exported ridl (such as printf).
Thus all functions exported by ridl have been statically linked into hello.dll (see hello.map file attached above in my post). I.e. the generated dll contains many functions from ridl image and don't have any imported functions.
I don't understand the reasons for this result.
PS: I use the evmomap-l137 platform, and the ridl (well-known as core and client) are running in the dsp side under sys/bios.
In the test example that I am working with, the hello.c file contains an export of the startt symbol and an import of the printf symbol:
#include <stdio>
__declspec(dllexport) int start();
__declspec(dllimport) int printf(const char *_format, ...);
int start()
{
printf("PASS - hello.c\n");
return 0;
}
Perhaps the detail that I neglected to mention is that you need to import functions that you want to call from a DLL. Similarly, you'll want to export functions that you want to be callable from your DLL.
It's simple and clear. I think what you do not understand me fully or what I can not explain a question :)
With same compiler options as described above, I get a statically link for all functions which must be imported.
For example, in our case, the printf function must be IMPORTED from ridl image. But this function as I can see from the map-file has static link and the dll-file not contains any imported functions.
****************************************************************************** TMS320C6x Linker PC v7.3.1 ******************************************************************************>> Linked Fri Mar 30 22:30:48 2012OUTPUT FILE NAME: <hello.dll>ENTRY POINT SYMBOL: "start" address: c0010e40MEMORY CONFIGURATION name origin length used unused attr fill---------------------- -------- --------- -------- -------- ---- -------- DSPRAM 11800000 00040000 00000000 00040000 RWIX SHAREDRAM 80000000 00020000 00000010 0001fff0 RWIX SDRAM c0000000 20000000 00010e60 1ffef1a0 RWIX VECS ffff0000 00000080 00000000 00000080 RWIX ARMRAM ffff0080 00001f80 00000000 00001f80 RWIXSEGMENT ALLOCATION MAPrun origin load origin length init length attrs members---------- ----------- ---------- ----------- ----- -------80000000 80000000 00000010 00000010 r-- 80000000 80000000 00000010 00000010 r-- .constc0000000 c0000000 00010e60 00010e60 r-x c0000000 c0000000 00010e60 00010e60 r-x .textSECTION ALLOCATION MAP output attributes/section page origin length input sections-------- ---- ---------- ---------- ----------------.cinit 0 80000000 00000000 UNINITIALIZED.const 0 80000000 00000010 80000000 00000010 hello.obj (.const).text 0 c0000000 00010e60 c0000000 00010e40 RIDL.out (.text) c0010e40 00000020 hello.obj (.text)
GLOBAL SYMBOLS: SORTED ALPHABETICALLY BY Name
.....
[205 symbols]DYNAMIC SYMBOLS: SORTED ALPHABETICALLY BY Name
[7 symbols]IMPORTED and EXPORTED SYMBOLS: SORTED ALPHABETICALLY BY Name IMPORTED SYMBOLSname size Imported from----------------------------- -------- -------------[0 symbols]EXPORTED SYMBOLSaddress name-------- ----c0010e40 start
Why is this happening?
Nikolay,
I am not seeing the same results as you are in a simple "hello" type example:
>> Linked Fri Mar 30 14:10:32 2012
OUTPUT FILE NAME: <hello.dll>
ENTRY POINT SYMBOL: "start" address: 00000040
MEMORY CONFIGURATION
name origin length used unused attr fill
---------------------- -------- --------- -------- -------- ---- --------
RAM 00000020 ffffffe0 00000080 ffffff60 RWIX
SEGMENT ALLOCATION MAP
run origin load origin length init length attrs members
---------- ----------- ---------- ----------- ----- -------
00000020 00000020 00000070 00000070 r-x
00000020 00000020 00000020 00000020 r-x .plt
00000040 00000040 00000040 00000040 r-x .text
00000080 00000080 00000010 00000010 r-- .const:.string
SECTION ALLOCATION MAP
output attributes/
section page origin length input sections
-------- ---- ---------- ---------- ----------------
.plt 0 00000020 00000020
00000020 00000020 (.plt.printf)
.text 0 00000040 00000040
00000040 00000040 hello.obj (.text)
.const:.string
* 0 00000080 00000010
00000080 00000010 hello.obj (.const:.string)
PROCEDURE LINKAGE TABLE
PLT entry name /
addr procedure name
-------- ------------------------
00000020 .plt.printf
printf
address name
-------- ----
00000000 __TI_STATIC_BASE
ffffffff __TI_pprof_out_hndl
ffffffff __TI_prof_data_size
ffffffff __TI_prof_data_start
UNDEFED printf
00000040 start
GLOBAL SYMBOLS: SORTED BY Symbol Address
[8 symbols]
DYNAMIC SYMBOLS: SORTED ALPHABETICALLY BY Name
UNDEFED
0000005c $C$RL0
00000080 $C$SL1
[9 symbols]
IMPORTED and EXPORTED SYMBOLS: SORTED ALPHABETICALLY BY Name
IMPORTED SYMBOLS
name size Imported from
----------------------------- -------- -------------
printf unified_dyn_loader/dlti.gen
EXPORTED SYMBOLS
Do you still have a definition of main() in your hello.c file? If so, this will cause the linker to pull in many of the things that are already in ridl.out (dlti.gen in my example) when building hello.dll.
There is certainly something amiss between your example and my example. Another source of information about what is happening during the link step is to look at the ofd6x output for hello.dll. ofd6x is an object file dump utility which gives you a more readable display of what is actually in the output file produced by the link. For example, if I run "ofd6x -v hello.dll > hello_ofd.txt" on my example, I get a file which includes the following snippet:
Dynamic Information in ".dynamic"
id tag value
-- --- -----
0 DT_NEEDED dlti.gen
1 DT_PLTRELSZ 0
2 DT_HASH 0x00001164
3 DT_STRTAB 0x000010e4
4 DT_SYMTAB 0x00001054
5 DT_RELA 0x00000168
6 DT_RELASZ 72
7 DT_RELAENT 12
8 DT_STRSZ 125
9 DT_SYMENT 16
10 DT_SONAME hello.dll
11 DT_PLTREL 7
12 DT_TEXTREL
13 DT_JMPREL 0x000001b0
14 DT_BIND_NOW
15 DT_FLAGS 12
16 DT_C6000_GSYM_OFFSET 112
17 DT_C6000_GSTR_OFFSET 93
18 DT_NULL
19 DT_NULL
20 DT_NULL
21 DT_NULL
22 DT_NULL
The DT_NEEDED dynamic tag above indicates that there is a dependency between hello.dll and dlti.gen (the base image of the dynamic loader). You should see something similar in the ofd6x output for your example hello.dll.
If this still doesn't help to solve your problem, can you write down the details of your build steps and describe exactly what you expect to see at the conclusion of each step? This might help to illuminate to me where our examples are diverging in behavior.
I checked reasons which you a said, but it did not help. I attach the hello source and makefile below:
3438.hello.zip
It looks like the difference between my example and yours is the dynamic loader itself.
In your RIDL.map, there are no exported symbols, so printf cannot be imported by hello.c (this is why it is linking against the RTS again).
In a map file generated from my build of the dynamic loader base image file (dlti.gen), I have the following exported symbols:
[0 symbols]
0002165c _DLModules
0001ea40 __c6xabi_cmpd
00020cc0 __c6xabi_cvtfd
0001df60 __c6xabi_divi
0001ffa0 __c6xabi_divu
000208c0 __c6xabi_llshru
0001eb40 __c6xabi_remi
00020320 __c6xabi_remu
00020e40 __c6xabi_subd
00020fa0 __c6xabi_subf
00021080 __cxa_atexit
0001de40 __cxa_finalize
902017ac _ctypes_
000211c0 clock
0001fa80 exit
90202384 export_var
000209c0 fclose
00020500 fopen
00018620 fread
00019520 free
0001c220 ftell
00014ec0 fwrite
000211e0 getenv
0001fb60 ltoa
0001b980 malloc
0001bbc0 memcpy
0001b2a0 open
0001e620 printf
00020bc0 strcmp
0001fe00 strstr
Please take a closer look at your build of RIDL.out to be sure you are exporting symbols (the RIDL package should contain a c60_export.cmd file that should be included in the link command of the RIDL.out build). Your RIDL.out also does not contain a .dynamic section which indicates that you did not build it with the --dynamic=exe option in the link command.
Hope this is helpful.
I did not know about --dynamic=exe option for building the ridl. Now, after rebuild with this option, hello example are used imported functions.
Thanks for you support.
Regards Nikolay