Undefined reference with new lib


20 posts
by codyrigney92 » Thu Jan 17, 2013 3:55 am
Hi,
I can't seem to compile my code with the newlib libs. I am using an example posted by a forum user that contains both C and C++ code and assembly, while adding the newlib to it for my malloc calls and what not. This borrows from dwelch67's repository for syscalls.c and also uses code from the Baking Pi tutorials.

My error is this:
Code: Select all
arm-none-eabi-gcc -c source/cppcode.cpp -o build/cppcode.o
arm-none-eabi-ld --no-undefined build/gpio.o build/main.o build/systemTimer.o build/ccode.o build/syscalls.o build/cppcode.o -Map kernel.map -o build/output.elf -L /Users/cmrigney/Developer/GNUArm/newlib/newlib-1.18.0/arm-none-eabi/newlib/ -lc -lg -lm -T kernel.ld
build/cppcode.o:(.ARM.exidx+0x0): undefined reference to `__aeabi_unwind_cpp_pr1'


I believe the problem area may lie in the makefile?

Code: Select all
###############################################################################
#   makefile
#    by Alex Chadwick
#
#   A makefile script for generation of raspberry pi kernel images.
###############################################################################

# The toolchain to use. arm-none-eabi works, but there does exist
# arm-bcm2708-linux-gnueabi.
ARMGNU ?= arm-none-eabi

# The intermediate directory for compiled object files.
BUILD = build/

# The directory in which source files are stored.
SOURCE = source/

# The name of the output file to generate.
TARGET = kernel.img

# The name of the assembler listing file to generate.
LIST = kernel.list

# The name of the map file to generate.
MAP = kernel.map

LIB = -L /Users/cmrigney/Developer/GNUArm/newlib/newlib-1.18.0/arm-none-eabi/newlib/

# The name of the linker script to use.
LINKER = kernel.ld

# The names of all object files that must be generated. Deduced from the
# assembly code files in source.
OBJECTS := $(patsubst $(SOURCE)%.s,$(BUILD)%.o,$(wildcard $(SOURCE)*.s)) \
           $(patsubst $(SOURCE)%.c,$(BUILD)%.o,$(wildcard $(SOURCE)*.c)) \
         $(patsubst $(SOURCE)%.cpp,$(BUILD)%.o,$(wildcard $(SOURCE)*.cpp))
 
# Rule to make everything.
all: $(TARGET) $(LIST)

# Rule to remake everything. Does not include clean.
rebuild: all

# Rule to make the listing file.
$(LIST) : $(BUILD)output.elf
   $(ARMGNU)-objdump -d $(BUILD)output.elf > $(LIST)

# Rule to make the image file.
$(TARGET) : $(BUILD)output.elf
   $(ARMGNU)-objcopy $(BUILD)output.elf -O binary $(TARGET)

# Rule to make the elf file.
$(BUILD)output.elf : $(OBJECTS) $(LINKER)
   $(ARMGNU)-ld --no-undefined $(OBJECTS) -Map $(MAP) -o $(BUILD)output.elf $(LIB) -lc -lg -lm -T $(LINKER)

# Rule to make the object files.
$(BUILD)%.o: $(SOURCE)%.s
   $(ARMGNU)-as -I $(SOURCE) $< -o $@
   
# Rule to make the object files.
$(BUILD)%.o: $(SOURCE)%.c
   $(ARMGNU)-gcc -c $< -o $@
   
# Rule to make the object files.
$(BUILD)%.o: $(SOURCE)%.cpp
   $(ARMGNU)-gcc -c $< -o $@

# Rule to clean files.
clean :
   -rm -f $(BUILD)*.o
   -rm -f $(BUILD)output.elf
   -rm -f $(TARGET)
   -rm -f $(LIST)
   -rm -f $(MAP)


If I remove the C++ code I believe the problem goes away. Any ideas?

Also here is my linker script:

Code: Select all
MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 0x20000
}

SECTIONS
{
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}



Thanks!
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am
by codyrigney92 » Thu Jan 17, 2013 4:41 am
I would like to add that the newlib didn't generate any C++ libs, only C libs that I could find. (libc.a, libg.a, libm.a).
Shouldn't it have generated a libstdc++.a?
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am
by dwelch67 » Thu Jan 17, 2013 5:00 am
if you borrowed something from me and you are using C++ it is likely not going to work. I would find other makefiles or cross compilers, etc that bridge the gap between C and C++, libraries, startup/bootstrap, etc.

David (dwelch67)
Posts: 415
Joined: Sat May 26, 2012 5:32 pm
by codyrigney92 » Thu Jan 17, 2013 5:21 am
I think I have to add the libgcc.a to the libraries but then there are other functions that have to be implemented as I get more undefined references. I might give up on C++ with newlib unless somebody figures it out.

Dwelch, where did you come up with the memory location for the heap in syscalls.c(newlib0 repo)? I don't think I am able to "malloc" even 500 bytes but 200 bytes works. Is this a limitation in the way you implemented it? Do you have any pointers to improve this?
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am
by DavidS » Thu Jan 17, 2013 1:20 pm
If I may suggest: PDCLib is much simpler, and easier to figure out, extend, maintain.
http://pdclib.e43.eu/
ARM Assembly Language: For those that want: Simple, Powerful, Easy to learn, and Easy to debug.
User avatar
Posts: 1251
Joined: Thu Dec 15, 2011 6:39 am
Location: USA
by dwelch67 » Thu Jan 17, 2013 10:47 pm
dwelch67 wrote:if you borrowed something from me and you are using C++ it is likely not going to work. I would find other makefiles or cross compilers, etc that bridge the gap between C and C++, libraries, startup/bootstrap, etc.

David (dwelch67)


Throw a dart at the wall kind of thing. You may be looking at code that was used on another platform. I obviously dont put a lot of complexity into my malloc routine, I normally get a feel for how big my application is and then set the heap somewhere above that.

I have successfully used that code or code very close to it for embedding things like mp3 decoders and jpeg stuff, zlib, etc. stuff that wants file I/O or a malloc. Mostly for one time execution (processor testing, etc) so I dont have to worry about reclaiming the allocated memory, and the file implementation can be as simple as the malloc.

David
Posts: 415
Joined: Sat May 26, 2012 5:32 pm
by codyrigney92 » Fri Jan 18, 2013 1:33 am
DavidS wrote:If I may suggest: PDCLib is much simpler, and easier to figure out, extend, maintain.
http://pdclib.e43.eu/


Unfortunately, I'm a noob when it comes to bare metal, so without a place to start I'll have some difficulties. I will check into it.
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am
by codyrigney92 » Fri Jan 18, 2013 1:38 am
dwelch67 wrote:Throw a dart at the wall kind of thing. You may be looking at code that was used on another platform. I obviously dont put a lot of complexity into my malloc routine, I normally get a feel for how big my application is and then set the heap somewhere above that.

I see. I assumed that the code on the repository was for the raspberry pi specifically. I'll try the technique you said for setting the heap.
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am
by dwelch67 » Fri Jan 18, 2013 4:15 am
actually it was setup for the raspberry pi, the program was intended to be loaded at 0x8000. I have the stack grow down from 0x8000 and my linker script is wrong it has 0x8000 with a size of 0x20000 which means 0x8000 to 0x27FFF so I should have put the heap for malloc at 0x28000 or change the linker script to be a size of 0x18000.

When built

Elf file type is EXEC (Executable file)
Entry point 0x8000
There are 1 program headers, starting at offset 52

Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x008000 0x00008000 0x00008000 0x0a0b8 0x0a100 RWE 0x8000

0x8000 + 0xA100 = 0x12100

and the disassembly agrees with that.

0x12100 is a little over 128 Kbytes leaving tons of space for mallocing stuff (not a lot of space for an application).

That is how I come up with a number like that I compile the binary and look at where the end is, I could do linker script stuff and put a variable in there and have that work its way into the code at link time so that that number is dynamic and rides on the end of the binary/data. The ramblings in my databss directory may give some insight into how to do that.

Why you can alloc 200 and not 500, that doesnt make sense to me, unless the binary has grown, the malloc is giving you data which collides with your program, and subtle changes in the program adjust whether it crashes or not. That is one idea. Check the size of your binary (arm-none-gnueabi-readelf -l arm_elf_file)

David
Posts: 415
Joined: Sat May 26, 2012 5:32 pm
by codyrigney92 » Fri Jan 18, 2013 5:47 am
Thanks for the info!

After running readelf I get this:
Code: Select all
Elf file type is EXEC (Executable file)
Entry point 0x8000
There are 2 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  LOAD           0x008000 0x00000000 0x00000000 0x00004 0x00004 R E 0x8000
  LOAD           0x010000 0x00008000 0x00008000 0x01c20 0x01c5c RWE 0x8000

 Section to Segment mapping:
  Segment Sections...
   00     .init
   01     .text .rodata .rodata.str1.4 .data .bss


Should I have 2 program headers?
It looks like 0x010000 + 0x01C5C = location I should have heap correct?
I'll look into your databss repo tomorrow when I get the chance and see what I can learn. What exactly is bss?

Thanks for the help!
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am
by dwelch67 » Fri Jan 18, 2013 6:04 am
what is bss. geez. I just saw someone define it on stack overflow the other day.

When you declare global variables (well, if you did, most folks avoid them)

int a = 5;
int b;

Some memory in your programs memory space is allocated by the compiler for each of these items. Rules of the C language say that when main() starts a will contain 5 and b will contain 0 (zero). The compiler/linker will put a in what is normally known as .data and b goes into what is now known as .bss, it might have been called something else in the past.

that may be what you are seeing in your program, .text (where the program is) and .data being two blocks in your binary. If you are not using a linker script but for example -Ttext=0x8000 then gnu ld (for arm) has a tendancy to at some point break .text into two chunks with a hole in the middle. I dont know why.

David
Posts: 415
Joined: Sat May 26, 2012 5:32 pm
by Joeboy » Fri Jan 18, 2013 11:38 am
On a slight tangent - I'm finding that when I initialize global / static variables to 0, gcc helpfully optimizes out the assignment, leading to confusing bugs. Is there any way to make it not do that (aside from zeroing the bss)?
Posts: 24
Joined: Wed Oct 31, 2012 11:59 am
by dwelch67 » Fri Jan 18, 2013 4:04 pm
Assuming you are asking what I think you are asking then...

read my data bss thing. If you are using any of my examples with my linker scripts and build commands then you cannot expect anything from the variables

int a=5;
int b=6;

Dont expect those to be set to those values (almost guarantee they wont) and

int c;
int d;

dont expect those to be zero.

My style is to do this:

int a,b,c,d;

void notmain ( void )
{
a = 5;
b = 6;
c = 0;
d = 0;

The short answer to "why" is related to keep it simple stupid and portability (avoiding compiler and linker specific dependencies as much as possible).

After enough questions about it I wrote that data bss thing, for folks that dont like my style but want to learn how bootstrapping the C compiled code works (with gnu tools).

If I have misunderstood your question, then please elaborate or show some examples.

David
Posts: 415
Joined: Sat May 26, 2012 5:32 pm
by Joeboy » Fri Jan 18, 2013 5:22 pm
Thanks David.

What I'm finding (and I've just tested this) is that if I have:

Code: Select all
int z = 23;


in global scope, z gets set to 23 when it's first used. This seems to happen reliably. However with

Code: Select all
int z = 0;

the instructions to zero z aren't generated (presumably because the compiler expects z to be 0 by default).

This is actually kind of liveable with, globals are bad and wrong anyway and I can just set them in main(). What's more annoying is that:

Code: Select all
void myfunc() {
    static int z = 0;
}


tends to leave nonsense (0x1a1a1a1a, actually) in z. I'm not sure how to work around that nicely.

My code/linker script etc are available for perusal at https://github.com/Joeboy/pixperiments/ ... /pitracker

I will read your bssdata thing, and hope for enlightenment.

Cheers.
Posts: 24
Joined: Wed Oct 31, 2012 11:59 am
by dwelch67 » Fri Jan 18, 2013 6:31 pm
Another name I use for static local variables is "local globals". They are not (usually) on the stack like normal locals but go into the .data space with the globals. And if the globals are not being initialized then neither will the static locals. I dont use static locals in my embedded code for that reason. I didnt used to use any locals, but the more I got into compilers, the more I saw the trade off, but you have and will still see me using globals where a local would have been the approach that most folks would have used. So like globals if you are using my build scripts then your static locals wont work either.

The solutions in my databss thing will work but are likely not the only way to do it. Gnu linker script is a language in and of itself that is IMO overly complicated. If you look at things that have in the past targeted for example the GBA, NDS (yagarto, devkitarm, etc), you can look at their default linker scripts and startup code (crt0.S usually) to see how they solved the problem. Those kinds of packages are not going to take the short cuts that I take, so pre-initialized variables should work in those environments, and C++ should also work.

dwelch67
Posts: 415
Joined: Sat May 26, 2012 5:32 pm
by codyrigney92 » Fri Jan 18, 2013 6:47 pm
Thanks David, your repository has been significant help for me. I have decided to stick with C for now.

I do have a question about your newlib syscalls implementation on the repository. Will printf() in this case spit it out to UART? It seems all your examples use the putc() to UART method which is fine, but will printf() just do a bunch of putc() according to this syscalls? i.e. printf("Hello") = putc('H') ... putc('e') ......
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am
by dwelch67 » Fri Jan 18, 2013 7:19 pm
with newlib yes. and the example program shows that. a few items in the syscalls are required to make it work. _isatty which I hardcode the return to a 1, normally you might want to inspect the file handle to see if it really is a tty (if so return 1). And then the _write function sends the characters to uart_putc() one at a time. the other functions in there I have return 0 or whatever the successful return value is, even though there really isnt any code. (I dont remember if newlib calls open with stdout or not, at one point in time I added debug outputs to see what was going on (cant use printf of course you may get into an infinite loop then crash the stack into the app).

dwelch67
Posts: 415
Joined: Sat May 26, 2012 5:32 pm
by codyrigney92 » Sat Jan 19, 2013 12:20 am
Ah I missed that in the example. I see it now.

What about the read? It returns len but does nothing else. Does this mean scanf will not work? If not, how would you implement that? Also how would you check the handle to see if it is UART? Again I'm a noob to bare metal so please bare with me. Thanks for the advice!
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am
by dwelch67 » Sat Jan 19, 2013 3:26 am
Right scanf should not work (I never use scanf so I have never tried it with newlib).

I wouldnt call printf and scanf, etc bare metal. They are higher level than that, newlib and other libraries like this are meant to live on and be part of an operating system. A bit high level for bare metal. Because newlib is so easy to rip out the back end and replace it, it provides a nice cushion for a boundary between system/application level programming and bare metal (if you are willing to implement the system underneath). So I wouldnt call this learning bare metall so much as learning newlib internals.

For scanf to work you would need to likely do the opposite of write. Implement a read that collects the length amount of bytes from whatever input you want scanf to read from, be it a block of .rodata that is pretending to be a file, or have it call some function to read characters from the uart, or go so far as to look at the file handle and keep track of which file is what.

You might want to instrument all the syscalls with some sort of uart_putc() or hexstring() output so that you can see all the parameters and all the functions involved in whatever library call you are using. Then implement an underlying system accordingly.

I had a hard time at first letting go of printf when I moved to this kind of programming. Finally I did and was able to let go of newlib and the headaches it used to involved. I was and still am surprised how easily it builds today. Not long ago it was a major source of pain to find the right combination of binutils, gcc, and newlib that all built together for a particular target as a cross compiler.

dwelch67
Posts: 415
Joined: Sat May 26, 2012 5:32 pm
by codyrigney92 » Sat Jan 19, 2013 4:29 am
Thanks for the tips. I'll explore what happens by printing it to output like you said. Now I just have to wait for my TTL Serial USB to come in the mail. :D
Posts: 58
Joined: Fri Dec 14, 2012 2:04 am