Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(292)

Side by Side Diff: src/libmach/executable.c

Issue 2587041: code review 2587041: 5l, 6l, 8l: link pclntab and symtab as ordinary rodata ... (Closed)
Patch Set: code review 2587041: 5l, 6l, 8l: link pclntab and symtab as ordinary rodata ... Created 14 years, 5 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/cmd/ld/symtab.c ('k') | src/pkg/runtime/symtab.c » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Inferno libmach/executable.c 1 // Inferno libmach/executable.c
2 // http://code.google.com/p/inferno-os/source/browse/utils/libmach/executable.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/libmach/executable.c
3 // 3 //
4 // Copyright © 1994-1999 Lucent Technologies Inc. 4 // Copyright © 1994-1999 Lucent Technologies Inc.
5 // Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.ne t). 5 // Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.ne t).
6 // Portions Copyright © 1997-1999 Vita Nuova Limited. 6 // Portions Copyright © 1997-1999 Vita Nuova Limited.
7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuov a.com). 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuov a.com).
8 // Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others. 8 // Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
9 // Portions Copyright © 2009 The Go Authors. All rights reserved. 9 // Portions Copyright © 2009 The Go Authors. All rights reserved.
10 // 10 //
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
59 static int nextboot(int, Fhdr*, ExecHdr*); 59 static int nextboot(int, Fhdr*, ExecHdr*);
60 static int sparcboot(int, Fhdr*, ExecHdr*); 60 static int sparcboot(int, Fhdr*, ExecHdr*);
61 static int mipsboot(int, Fhdr*, ExecHdr*); 61 static int mipsboot(int, Fhdr*, ExecHdr*);
62 static int mips4kboot(int, Fhdr*, ExecHdr*); 62 static int mips4kboot(int, Fhdr*, ExecHdr*);
63 static int common(int, Fhdr*, ExecHdr*); 63 static int common(int, Fhdr*, ExecHdr*);
64 static int commonllp64(int, Fhdr*, ExecHdr*); 64 static int commonllp64(int, Fhdr*, ExecHdr*);
65 static int adotout(int, Fhdr*, ExecHdr*); 65 static int adotout(int, Fhdr*, ExecHdr*);
66 static int elfdotout(int, Fhdr*, ExecHdr*); 66 static int elfdotout(int, Fhdr*, ExecHdr*);
67 static int machdotout(int, Fhdr*, ExecHdr*); 67 static int machdotout(int, Fhdr*, ExecHdr*);
68 static int armdotout(int, Fhdr*, ExecHdr*); 68 static int armdotout(int, Fhdr*, ExecHdr*);
69 static» void» setsym(Fhdr*, int32, int32, int32, vlong); 69 static» void» setsym(Fhdr*, vlong, int32, vlong, int32, vlong, int32);
70 static void setdata(Fhdr*, uvlong, int32, vlong, int32); 70 static void setdata(Fhdr*, uvlong, int32, vlong, int32);
71 static void settext(Fhdr*, uvlong, uvlong, int32, vlong); 71 static void settext(Fhdr*, uvlong, uvlong, int32, vlong);
72 static void hswal(void*, int, uint32(*)(uint32)); 72 static void hswal(void*, int, uint32(*)(uint32));
73 static uvlong _round(uvlong, uint32); 73 static uvlong _round(uvlong, uint32);
74 74
75 /* 75 /*
76 * definition of per-executable file type structures 76 * definition of per-executable file type structures
77 */ 77 */
78 78
79 typedef struct Exectable{ 79 typedef struct Exectable{
(...skipping 340 matching lines...) Expand 10 before | Expand all | Expand 10 after
420 adotout(int fd, Fhdr *fp, ExecHdr *hp) 420 adotout(int fd, Fhdr *fp, ExecHdr *hp)
421 { 421 {
422 int32 pgsize; 422 int32 pgsize;
423 423
424 USED(fd); 424 USED(fd);
425 pgsize = mach->pgsize; 425 pgsize = mach->pgsize;
426 settext(fp, hp->e.exechdr.entry, pgsize+sizeof(Exec), 426 settext(fp, hp->e.exechdr.entry, pgsize+sizeof(Exec),
427 hp->e.exechdr.text, sizeof(Exec)); 427 hp->e.exechdr.text, sizeof(Exec));
428 setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize), 428 setdata(fp, _round(pgsize+fp->txtsz+sizeof(Exec), pgsize),
429 hp->e.exechdr.data, fp->txtsz+sizeof(Exec), hp->e.exechdr.bss); 429 hp->e.exechdr.data, fp->txtsz+sizeof(Exec), hp->e.exechdr.bss);
430 » setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, f p->datoff+fp->datsz); 430 » setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.sp sz, 0, hp->e.exechdr.pcsz);
431 return 1; 431 return 1;
432 } 432 }
433 433
434 static void 434 static void
435 commonboot(Fhdr *fp) 435 commonboot(Fhdr *fp)
436 { 436 {
437 if (!(fp->entry & mach->ktmask)) 437 if (!(fp->entry & mach->ktmask))
438 return; 438 return;
439 439
440 switch(fp->type) { /* boot image */ 440 switch(fp->type) { /* boot image */
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
518 char *p; 518 char *p;
519 uvlong *v; 519 uvlong *v;
520 } u; 520 } u;
521 u.p = (char*)&hp->e.exechdr; 521 u.p = (char*)&hp->e.exechdr;
522 entry = beswav(*u.v); 522 entry = beswav(*u.v);
523 523
524 pgsize = mach->pgsize; 524 pgsize = mach->pgsize;
525 settext(fp, entry, pgsize+fp->hdrsz, hp->e.exechdr.text, fp->hdrsz); 525 settext(fp, entry, pgsize+fp->hdrsz, hp->e.exechdr.text, fp->hdrsz);
526 setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize), 526 setdata(fp, _round(pgsize+fp->txtsz+fp->hdrsz, pgsize),
527 hp->e.exechdr.data, fp->txtsz+fp->hdrsz, hp->e.exechdr.bss); 527 hp->e.exechdr.data, fp->txtsz+fp->hdrsz, hp->e.exechdr.bss);
528 » setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, f p->datoff+fp->datsz); 528 » setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.sp sz, 0, hp->e.exechdr.pcsz);
529 529
530 if(hp->e.exechdr.magic & DYN_MAGIC) { 530 if(hp->e.exechdr.magic & DYN_MAGIC) {
531 fp->txtaddr = 0; 531 fp->txtaddr = 0;
532 fp->dataddr = fp->txtsz; 532 fp->dataddr = fp->txtsz;
533 return 1; 533 return 1;
534 } 534 }
535 commonboot(fp); 535 commonboot(fp);
536 return 1; 536 return 1;
537 } 537 }
538 538
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
751 */ 751 */
752 if(ep->machine == SPARC64 && ep->phnum == 2) { 752 if(ep->machine == SPARC64 && ep->phnum == 2) {
753 uint32 txtaddr, txtsz, dataddr, bsssz; 753 uint32 txtaddr, txtsz, dataddr, bsssz;
754 754
755 txtaddr = ph[0].vaddr | 0x80000000; 755 txtaddr = ph[0].vaddr | 0x80000000;
756 txtsz = ph[0].filesz - ph[0].paddr; 756 txtsz = ph[0].filesz - ph[0].paddr;
757 dataddr = txtaddr + txtsz; 757 dataddr = txtaddr + txtsz;
758 bsssz = ph[0].memsz - ph[0].filesz; 758 bsssz = ph[0].memsz - ph[0].filesz;
759 settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, p h[0].offset); 759 settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, p h[0].offset);
760 setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz); 760 setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
761 » » » setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset); 761 » » » setsym(fp, ph[1].offset, ph[1].filesz, 0, 0, 0, ph[1].me msz);
762 free(ph); 762 free(ph);
763 return 1; 763 return 1;
764 } 764 }
765 765
766 werrstr("No TEXT or DATA sections"); 766 werrstr("No TEXT or DATA sections");
767 error:
768 free(ph); 767 free(ph);
769 free(sh); 768 free(sh);
770 return 0; 769 return 0;
771 } 770 }
772 771
773 settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset); 772 settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
774 setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - p h[id].filesz); 773 setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - p h[id].filesz);
775 if(is != -1) 774 if(is != -1)
776 » » setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset); 775 » » setsym(fp, ph[is].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz);
777 else if(sh != 0){ 776 else if(sh != 0){
778 char *buf; 777 char *buf;
779 uvlong symsize = 0; 778 uvlong symsize = 0;
780 uvlong symoff = 0; 779 uvlong symoff = 0;
781 uvlong pclnsz = 0; 780 uvlong pclnsz = 0;
781 uvlong pclnoff = 0;
782 782
783 /* load shstrtab names */ 783 /* load shstrtab names */
784 buf = malloc(sh[ep->shstrndx].size); 784 buf = malloc(sh[ep->shstrndx].size);
785 if (buf == 0) 785 if (buf == 0)
786 goto done; 786 goto done;
787 memset(buf, 0, sizeof buf); 787 memset(buf, 0, sizeof buf);
788 seek(fd, sh[ep->shstrndx].offset, 0); 788 seek(fd, sh[ep->shstrndx].offset, 0);
789 i = read(fd, buf, sh[ep->shstrndx].size); 789 i = read(fd, buf, sh[ep->shstrndx].size);
790 USED(i); // shut up ubuntu gcc 790 USED(i); // shut up ubuntu gcc
791 791
792 for(i = 0; i < ep->shnum; i++) { 792 for(i = 0; i < ep->shnum; i++) {
793 if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) { 793 if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) {
794 symsize = sh[i].size; 794 symsize = sh[i].size;
795 symoff = sh[i].offset; 795 symoff = sh[i].offset;
796 } 796 }
797 if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) { 797 if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) {
798 if (sh[i].offset != symoff+symsize) {
799 werrstr("pc line table not contiguous wi th symbol table");
800 free(buf);
801 goto error;
802 }
803 pclnsz = sh[i].size; 798 pclnsz = sh[i].size;
799 pclnoff = sh[i].offset;
804 } 800 }
805 } 801 }
806 » » setsym(fp, symsize, 0, pclnsz, symoff); 802 » » setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsz);
807 free(buf); 803 free(buf);
808 } 804 }
809 done: 805 done:
810 free(ph); 806 free(ph);
811 free(sh); 807 free(sh);
812 return 1; 808 return 1;
813 } 809 }
814 810
815 static int 811 static int
816 elfdotout(int fd, Fhdr *fp, ExecHdr *hp) 812 elfdotout(int fd, Fhdr *fp, ExecHdr *hp)
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
933 */ 929 */
934 if(ep->machine == SPARC64 && ep->phnum == 2) { 930 if(ep->machine == SPARC64 && ep->phnum == 2) {
935 uint32 txtaddr, txtsz, dataddr, bsssz; 931 uint32 txtaddr, txtsz, dataddr, bsssz;
936 932
937 txtaddr = ph[0].vaddr | 0x80000000; 933 txtaddr = ph[0].vaddr | 0x80000000;
938 txtsz = ph[0].filesz - ph[0].paddr; 934 txtsz = ph[0].filesz - ph[0].paddr;
939 dataddr = txtaddr + txtsz; 935 dataddr = txtaddr + txtsz;
940 bsssz = ph[0].memsz - ph[0].filesz; 936 bsssz = ph[0].memsz - ph[0].filesz;
941 settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, p h[0].offset); 937 settext(fp, ep->elfentry | 0x80000000, txtaddr, txtsz, p h[0].offset);
942 setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz); 938 setdata(fp, dataddr, ph[0].paddr, ph[0].offset + txtsz, bsssz);
943 » » » setsym(fp, ph[1].filesz, 0, ph[1].memsz, ph[1].offset); 939 » » » setsym(fp, ph[1].offset, ph[1].filesz, 0, 0, 0, ph[1].me msz);
944 free(ph); 940 free(ph);
945 return 1; 941 return 1;
946 } 942 }
947 943
948 werrstr("No TEXT or DATA sections"); 944 werrstr("No TEXT or DATA sections");
949 error:
950 free(sh); 945 free(sh);
951 free(ph); 946 free(ph);
952 return 0; 947 return 0;
953 } 948 }
954 949
955 settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset); 950 settext(fp, ep->elfentry, ph[it].vaddr, ph[it].memsz, ph[it].offset);
956 setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - p h[id].filesz); 951 setdata(fp, ph[id].vaddr, ph[id].filesz, ph[id].offset, ph[id].memsz - p h[id].filesz);
957 if(is != -1) 952 if(is != -1)
958 » » setsym(fp, ph[is].filesz, 0, ph[is].memsz, ph[is].offset); 953 » » setsym(fp, ph[is].offset, ph[is].filesz, 0, 0, 0, ph[is].memsz);
959 else if(sh != 0){ 954 else if(sh != 0){
960 char *buf; 955 char *buf;
961 uvlong symsize = 0; 956 uvlong symsize = 0;
962 uvlong symoff = 0; 957 uvlong symoff = 0;
963 » » uvlong pclnsz = 0; 958 » » uvlong pclnsize = 0;
959 » » uvlong pclnoff = 0;
964 960
965 /* load shstrtab names */ 961 /* load shstrtab names */
966 buf = malloc(sh[ep->shstrndx].size); 962 buf = malloc(sh[ep->shstrndx].size);
967 if (buf == 0) 963 if (buf == 0)
968 goto done; 964 goto done;
969 memset(buf, 0, sizeof buf); 965 memset(buf, 0, sizeof buf);
970 seek(fd, sh[ep->shstrndx].offset, 0); 966 seek(fd, sh[ep->shstrndx].offset, 0);
971 i = read(fd, buf, sh[ep->shstrndx].size); 967 i = read(fd, buf, sh[ep->shstrndx].size);
972 USED(i); // shut up ubuntu gcc 968 USED(i); // shut up ubuntu gcc
973 969
974 for(i = 0; i < ep->shnum; i++) { 970 for(i = 0; i < ep->shnum; i++) {
975 if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) { 971 if (strcmp(&buf[sh[i].name], ".gosymtab") == 0) {
976 symsize = sh[i].size; 972 symsize = sh[i].size;
977 symoff = sh[i].offset; 973 symoff = sh[i].offset;
978 } 974 }
979 if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) { 975 if (strcmp(&buf[sh[i].name], ".gopclntab") == 0) {
980 » » » » if (sh[i].offset != symoff+symsize) { 976 » » » » pclnsize = sh[i].size;
981 » » » » » werrstr("pc line table not contiguous wi th symbol table"); 977 » » » » pclnoff = sh[i].offset;
982 » » » » » free(buf);
983 » » » » » goto error;
984 » » » » }
985 » » » » pclnsz = sh[i].size;
986 } 978 }
987 } 979 }
988 » » setsym(fp, symsize, 0, pclnsz, symoff); 980 » » setsym(fp, symoff, symsize, 0, 0, pclnoff, pclnsize);
989 free(buf); 981 free(buf);
990 } 982 }
991 done: 983 done:
992 free(sh); 984 free(sh);
993 free(ph); 985 free(ph);
994 return 1; 986 return 1;
995 } 987 }
996 988
997 static int 989 static int
998 machdotout(int fd, Fhdr *fp, ExecHdr *hp) 990 machdotout(int fd, Fhdr *fp, ExecHdr *hp)
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after
1202 } 1194 }
1203 if (textva == 0 || datava == 0) { 1195 if (textva == 0 || datava == 0) {
1204 free(cmd); 1196 free(cmd);
1205 free(cmdbuf); 1197 free(cmdbuf);
1206 return 0; 1198 return 0;
1207 } 1199 }
1208 /* compute entry by taking address after header - weird - BUG? */ 1200 /* compute entry by taking address after header - weird - BUG? */
1209 settext(fp, textva+sizeof(Machhdr) + mp->sizeofcmds, textva, textsize, t extoff); 1201 settext(fp, textva+sizeof(Machhdr) + mp->sizeofcmds, textva, textsize, t extoff);
1210 setdata(fp, datava, datasize, dataoff, bsssize); 1202 setdata(fp, datava, datasize, dataoff, bsssize);
1211 if(symtab != 0) 1203 if(symtab != 0)
1212 » » setsym(fp, symtab->filesize, 0, pclntab? pclntab->filesize : 0, symtab->fileoff); 1204 » » setsym(fp, symtab->fileoff, symtab->filesize, 0, 0, 0, pclntab? pclntab->filesize : 0);
1213 free(cmd); 1205 free(cmd);
1214 free(cmdbuf); 1206 free(cmdbuf);
1215 return 1; 1207 return 1;
1216 bad: 1208 bad:
1217 free(cmd); 1209 free(cmd);
1218 free(cmdbuf); 1210 free(cmdbuf);
1219 return 0; 1211 return 0;
1220 } 1212 }
1221 1213
1222 /* 1214 /*
1223 * (Free|Net)BSD ARM header. 1215 * (Free|Net)BSD ARM header.
1224 */ 1216 */
1225 static int 1217 static int
1226 armdotout(int fd, Fhdr *fp, ExecHdr *hp) 1218 armdotout(int fd, Fhdr *fp, ExecHdr *hp)
1227 { 1219 {
1228 uvlong kbase; 1220 uvlong kbase;
1229 1221
1230 USED(fd); 1222 USED(fd);
1231 settext(fp, hp->e.exechdr.entry, sizeof(Exec), hp->e.exechdr.text, sizeo f(Exec)); 1223 settext(fp, hp->e.exechdr.entry, sizeof(Exec), hp->e.exechdr.text, sizeo f(Exec));
1232 setdata(fp, fp->txtsz, hp->e.exechdr.data, fp->txtsz, hp->e.exechdr.bss) ; 1224 setdata(fp, fp->txtsz, hp->e.exechdr.data, fp->txtsz, hp->e.exechdr.bss) ;
1233 » setsym(fp, hp->e.exechdr.syms, hp->e.exechdr.spsz, hp->e.exechdr.pcsz, f p->datoff+fp->datsz); 1225 » setsym(fp, fp->datoff+fp->datsz, hp->e.exechdr.syms, 0, hp->e.exechdr.sp sz, 0, hp->e.exechdr.pcsz);
1234 1226
1235 kbase = 0xF0000000; 1227 kbase = 0xF0000000;
1236 if ((fp->entry & kbase) == kbase) { /* Boot image */ 1228 if ((fp->entry & kbase) == kbase) { /* Boot image */
1237 fp->txtaddr = kbase+sizeof(Exec); 1229 fp->txtaddr = kbase+sizeof(Exec);
1238 fp->name = "ARM *BSD boot image"; 1230 fp->name = "ARM *BSD boot image";
1239 fp->hdrsz = 0; /* header stripped */ 1231 fp->hdrsz = 0; /* header stripped */
1240 fp->dataddr = kbase+fp->txtsz; 1232 fp->dataddr = kbase+fp->txtsz;
1241 } 1233 }
1242 return 1; 1234 return 1;
1243 } 1235 }
(...skipping 10 matching lines...) Expand all
1254 static void 1246 static void
1255 setdata(Fhdr *fp, uvlong a, int32 s, vlong off, int32 bss) 1247 setdata(Fhdr *fp, uvlong a, int32 s, vlong off, int32 bss)
1256 { 1248 {
1257 fp->dataddr = a; 1249 fp->dataddr = a;
1258 fp->datsz = s; 1250 fp->datsz = s;
1259 fp->datoff = off; 1251 fp->datoff = off;
1260 fp->bsssz = bss; 1252 fp->bsssz = bss;
1261 } 1253 }
1262 1254
1263 static void 1255 static void
1264 setsym(Fhdr *fp, int32 symsz, int32 sppcsz, int32 lnpcsz, vlong symoff) 1256 setsym(Fhdr *fp, vlong symoff, int32 symsz, vlong sppcoff, int32 sppcsz, vlong l npcoff, int32 lnpcsz)
1265 { 1257 {
1258 fp->symoff = symoff;
1266 fp->symsz = symsz; 1259 fp->symsz = symsz;
1267 » fp->symoff = symoff; 1260 »·······
1261 » if(sppcoff == 0)
1262 » » sppcoff = symoff+symsz;
1263 » fp->sppcoff = symoff;
1268 fp->sppcsz = sppcsz; 1264 fp->sppcsz = sppcsz;
1269 » fp->sppcoff = fp->symoff+fp->symsz; 1265
1266 » if(lnpcoff == 0)
1267 » » lnpcoff = sppcoff + sppcsz;
1268 » fp->lnpcoff = lnpcoff;
1270 fp->lnpcsz = lnpcsz; 1269 fp->lnpcsz = lnpcsz;
1271 fp->lnpcoff = fp->sppcoff+fp->sppcsz;
1272 } 1270 }
1273 1271
1274 1272
1275 static uvlong 1273 static uvlong
1276 _round(uvlong a, uint32 b) 1274 _round(uvlong a, uint32 b)
1277 { 1275 {
1278 uvlong w; 1276 uvlong w;
1279 1277
1280 w = (a/b)*b; 1278 w = (a/b)*b;
1281 if (a!=w) 1279 if (a!=w)
1282 w += b; 1280 w += b;
1283 return(w); 1281 return(w);
1284 } 1282 }
OLDNEW
« no previous file with comments | « src/cmd/ld/symtab.c ('k') | src/pkg/runtime/symtab.c » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b